source: trunk/libtransmission/session.c @ 12248

Last change on this file since 12248 was 12248, checked in by jordan, 11 years ago

(trunk libT) break the mac build and introduce new crashes.

This is partially to address #4145 "Downloads stuck at 100%" by refactoring the bitset, bitfield, and tr_completion; however, the ripple effect is larger than usual so things may get worse in the short term before getting better.

livings124: to fix the mac build, remove bitset.[ch] from xcode

  • Property svn:keywords set to Date Rev Author Id
File size: 71.9 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: session.c 12248 2011-03-28 16:31:05Z jordan $
11 */
12
13#include <assert.h>
14#include <errno.h> /* ENOENT */
15#include <stdlib.h>
16#include <string.h> /* memcpy */
17
18#include <signal.h>
19#include <sys/types.h> /* stat(), umask() */
20#include <sys/stat.h> /* stat(), umask() */
21#include <unistd.h> /* stat */
22#include <dirent.h> /* opendir */
23
24#include <event2/dns.h> /* evdns_base_free() */
25#include <event2/event.h>
26
27//#define TR_SHOW_DEPRECATED
28#include "transmission.h"
29#include "announcer.h"
30#include "bandwidth.h"
31#include "bencode.h"
32#include "blocklist.h"
33#include "cache.h"
34#include "crypto.h"
35#include "fdlimit.h"
36#include "list.h"
37#include "net.h"
38#include "peer-io.h"
39#include "peer-mgr.h"
40#include "platform.h" /* tr_lock */
41#include "port-forwarding.h"
42#include "rpc-server.h"
43#include "session.h"
44#include "stats.h"
45#include "torrent.h"
46#include "tr-dht.h" /* tr_dhtUpkeep() */
47#include "tr-udp.h"
48#include "tr-utp.h"
49#include "tr-lpd.h"
50#include "trevent.h"
51#include "utils.h"
52#include "verify.h"
53#include "version.h"
54#include "web.h"
55
56enum
57{
58#ifdef TR_LIGHTWEIGHT
59    DEFAULT_CACHE_SIZE_MB = 2,
60    DEFAULT_PREFETCH_ENABLED = false,
61#else
62    DEFAULT_CACHE_SIZE_MB = 4,
63    DEFAULT_PREFETCH_ENABLED = true,
64#endif
65    SAVE_INTERVAL_SECS = 360
66};
67
68
69#define dbgmsg( ... ) \
70    do { \
71        if( tr_deepLoggingIsActive( ) ) \
72            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
73    } while( 0 )
74
75static tr_port
76getRandomPort( tr_session * s )
77{
78    return tr_cryptoWeakRandInt( s->randomPortHigh - s->randomPortLow + 1) + s->randomPortLow;
79}
80
81/* Generate a peer id : "-TRxyzb-" + 12 random alphanumeric
82   characters, where x is the major version number, y is the
83   minor version number, z is the maintenance number, and b
84   designates beta (Azureus-style) */
85void
86tr_peerIdInit( uint8_t * buf )
87{
88    int          i;
89    int          val;
90    int          total = 0;
91    const char * pool = "0123456789abcdefghijklmnopqrstuvwxyz";
92    const int    base = 36;
93
94    memcpy( buf, PEERID_PREFIX, 8 );
95
96    tr_cryptoRandBuf( buf+8, 11 );
97    for( i=8; i<19; ++i ) {
98        val = buf[i] % base;
99        total += val;
100        buf[i] = pool[val];
101    }
102
103    val = total % base ? base - ( total % base ) : 0;
104    buf[19] = pool[val];
105    buf[20] = '\0';
106}
107
108/***
109****
110***/
111
112tr_encryption_mode
113tr_sessionGetEncryption( tr_session * session )
114{
115    assert( session );
116
117    return session->encryptionMode;
118}
119
120void
121tr_sessionSetEncryption( tr_session *       session,
122                         tr_encryption_mode mode )
123{
124    assert( session );
125    assert( mode == TR_ENCRYPTION_PREFERRED
126          || mode == TR_ENCRYPTION_REQUIRED
127          || mode == TR_CLEAR_PREFERRED );
128
129    session->encryptionMode = mode;
130}
131
132/***
133****
134***/
135
136struct tr_bindinfo
137{
138    int socket;
139    tr_address addr;
140    struct event * ev;
141};
142
143
144static void
145close_bindinfo( struct tr_bindinfo * b )
146{
147    if( ( b != NULL ) && ( b->socket >=0 ) )
148    {
149        event_free( b->ev );
150        b->ev = NULL;
151        tr_netCloseSocket( b->socket );
152    }
153}
154
155static void
156close_incoming_peer_port( tr_session * session )
157{
158    close_bindinfo( session->public_ipv4 );
159    close_bindinfo( session->public_ipv6 );
160}
161
162static void
163free_incoming_peer_port( tr_session * session )
164{
165    close_bindinfo( session->public_ipv4 );
166    tr_free( session->public_ipv4 );
167    session->public_ipv4 = NULL;
168
169    close_bindinfo( session->public_ipv6 );
170    tr_free( session->public_ipv6 );
171    session->public_ipv6 = NULL;
172}
173
174static void
175accept_incoming_peer( int fd, short what UNUSED, void * vsession )
176{
177    int clientSocket;
178    tr_port clientPort;
179    tr_address clientAddr;
180    tr_session * session = vsession;
181
182    clientSocket = tr_netAccept( session, fd, &clientAddr, &clientPort );
183    if( clientSocket > 0 ) {
184        tr_deepLog( __FILE__, __LINE__, NULL, "new incoming connection %d (%s)",
185                   clientSocket, tr_peerIoAddrStr( &clientAddr, clientPort ) );
186        tr_peerMgrAddIncoming( session->peerMgr, &clientAddr, clientPort,
187                               clientSocket, NULL );
188    }
189}
190
191static void
192open_incoming_peer_port( tr_session * session )
193{
194    struct tr_bindinfo * b;
195
196    /* bind an ipv4 port to listen for incoming peers... */
197    b = session->public_ipv4;
198    b->socket = tr_netBindTCP( &b->addr, session->private_peer_port, false );
199    if( b->socket >= 0 ) {
200        b->ev = event_new( session->event_base, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session );
201        event_add( b->ev, NULL );
202    }
203
204    /* and do the exact same thing for ipv6, if it's supported... */
205    if( tr_net_hasIPv6( session->private_peer_port ) ) {
206        b = session->public_ipv6;
207        b->socket = tr_netBindTCP( &b->addr, session->private_peer_port, false );
208        if( b->socket >= 0 ) {
209            b->ev = event_new( session->event_base, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session );
210            event_add( b->ev, NULL );
211        }
212    }
213}
214
215const tr_address*
216tr_sessionGetPublicAddress( const tr_session * session, int tr_af_type, bool * is_default_value )
217{
218    const char * default_value;
219    const struct tr_bindinfo * bindinfo;
220
221    switch( tr_af_type )
222    {
223        case TR_AF_INET:
224            bindinfo = session->public_ipv4;
225            default_value = TR_DEFAULT_BIND_ADDRESS_IPV4;
226            break;
227
228        case TR_AF_INET6:
229            bindinfo = session->public_ipv6;
230            default_value = TR_DEFAULT_BIND_ADDRESS_IPV6;
231            break;
232
233        default:
234            bindinfo = NULL;
235            default_value = "";
236            break;
237    }
238
239    if( is_default_value && bindinfo )
240        *is_default_value = !tr_strcmp0( default_value, tr_address_to_string( &bindinfo->addr ) );
241
242    return bindinfo ? &bindinfo->addr : NULL;
243}
244
245/***
246****
247***/
248
249#ifdef TR_LIGHTWEIGHT
250 #define TR_DEFAULT_ENCRYPTION   TR_CLEAR_PREFERRED
251#else
252 #define TR_DEFAULT_ENCRYPTION   TR_ENCRYPTION_PREFERRED
253#endif
254
255static int
256parse_tos( const char *str )
257{
258    char *p;
259    int value;
260
261    if( !evutil_ascii_strcasecmp( str, "" ) )
262        return 0;
263    if( !evutil_ascii_strcasecmp( str, "default" ) )
264        return 0;
265
266    if( !evutil_ascii_strcasecmp( str, "lowcost" ) )
267        return 0x10;
268    if( !evutil_ascii_strcasecmp( str, "mincost" ) )
269        return 0x10;
270
271    if( !evutil_ascii_strcasecmp( str, "throughput" ) )
272        return 0x08;
273    if( !evutil_ascii_strcasecmp( str, "reliability" ) )
274        return 0x04;
275    if( !evutil_ascii_strcasecmp( str, "lowdelay" ) )
276        return 0x02;
277
278    value = strtol( str, &p, 0 );
279    if( !p || ( p == str ) )
280        return 0;
281
282    return value;
283}
284
285static const char *
286format_tos(int value)
287{
288    static char buf[8];
289    switch(value) {
290    case 0: return "default";
291    case 0x10: return "lowcost";
292    case 0x08: return "throughput";
293    case 0x04: return "reliability";
294    case 0x02: return "lowdelay";
295    default:
296        snprintf(buf, 8, "%d", value);
297        return buf;
298    }
299}
300
301void
302tr_sessionGetDefaultSettings( tr_benc * d )
303{
304    assert( tr_bencIsDict( d ) );
305
306    tr_bencDictReserve( d, 60 );
307    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        false );
308    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL,            "http://www.example.com/blocklist" );
309    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        DEFAULT_CACHE_SIZE_MB );
310    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED,              true );
311    tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED,              true );
312    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED,              false );
313    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR,             tr_getDefaultDownloadDir( ) );
314    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps,              100 );
315    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED,           false );
316    tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION,               TR_DEFAULT_ENCRYPTION );
317    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT,               30 );
318    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED,       false );
319    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR,           tr_getDefaultDownloadDir( ) );
320    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED,   false );
321    tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL,                 TR_MSG_INF );
322    tr_bencDictAddInt ( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          atoi( TR_DEFAULT_OPEN_FILE_LIMIT_STR ) );
323    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        atoi( TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ) );
324    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       atoi( TR_DEFAULT_PEER_LIMIT_TORRENT_STR ) );
325    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT,                atoi( TR_DEFAULT_PEER_PORT_STR ) );
326    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, false );
327    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     49152 );
328    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    65535 );
329    tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          TR_DEFAULT_PEER_SOCKET_TOS_STR );
330    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              true );
331    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          true );
332    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            TR_PREALLOCATE_SPARSE );
333    tr_bencDictAddBool( d, TR_PREFS_KEY_PREFETCH_ENABLED,         DEFAULT_PREFETCH_ENABLED );
334    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    2.0 );
335    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            false );
336    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     true );
337    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        false );
338    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS,         "0.0.0.0" );
339    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED,              false );
340    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD,             "" );
341    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME,             "" );
342    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST,            TR_DEFAULT_RPC_WHITELIST );
343    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    true );
344    tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT,                 atoi( TR_DEFAULT_RPC_PORT_STR ) );
345    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL,                  TR_DEFAULT_RPC_URL_STR );
346    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, "" );
347    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, false );
348    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED,        false );
349    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps,        50 ); /* half the regular */
350    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps,      50 ); /* half the regular */
351    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN,     540 ); /* 9am */
352    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED,   false );
353    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,       1020 ); /* 5pm */
354    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,       TR_SCHED_ALL );
355    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps,              100 );
356    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED,           false );
357    tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK,                    022 );
358    tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, 14 );
359    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4,        TR_DEFAULT_BIND_ADDRESS_IPV4 );
360    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6,        TR_DEFAULT_BIND_ADDRESS_IPV6 );
361    tr_bencDictAddBool( d, TR_PREFS_KEY_START,                    true );
362    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL,           false );
363}
364
365void
366tr_sessionGetSettings( tr_session * s, struct tr_benc * d )
367{
368    assert( tr_bencIsDict( d ) );
369
370    tr_bencDictReserve( d, 60 );
371    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        tr_blocklistIsEnabled( s ) );
372    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL,            tr_blocklistGetURL( s ) );
373    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        tr_sessionGetCacheLimit_MB( s ) );
374    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED,              s->isDHTEnabled );
375    tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED,              s->isUTPEnabled );
376    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED,              s->isLPDEnabled );
377    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR,             s->downloadDir );
378    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps,              tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
379    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED,           tr_sessionIsSpeedLimited( s, TR_DOWN ) );
380    tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION,               s->encryptionMode );
381    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT,               tr_sessionGetIdleLimit( s ) );
382    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED,       tr_sessionIsIdleLimited( s ) );
383    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR,           tr_sessionGetIncompleteDir( s ) );
384    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED,   tr_sessionIsIncompleteDirEnabled( s ) );
385    tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL,                 tr_getMessageLevel( ) );
386    tr_bencDictAddInt ( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          tr_fdGetFileLimit( s ) );
387    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        tr_sessionGetPeerLimit( s ) );
388    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       s->peerLimitPerTorrent );
389    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT,                tr_sessionGetPeerPort( s ) );
390    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, s->isPortRandom );
391    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     s->randomPortLow );
392    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    s->randomPortHigh );
393    tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          format_tos(s->peerSocketTOS) );
394    tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM, s->peer_congestion_algorithm );
395    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              s->isPexEnabled );
396    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          tr_sessionIsPortForwardingEnabled( s ) );
397    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            s->preallocationMode );
398    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREFETCH_ENABLED,         s->isPrefetchEnabled );
399    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    s->desiredRatio );
400    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            s->isRatioLimited );
401    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     tr_sessionIsIncompleteFileNamingEnabled( s ) );
402    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        tr_sessionIsRPCPasswordEnabled( s ) );
403    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS,         tr_sessionGetRPCBindAddress( s ) );
404    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED,              tr_sessionIsRPCEnabled( s ) );
405    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD,             tr_sessionGetRPCPassword( s ) );
406    tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT,                 tr_sessionGetRPCPort( s ) );
407    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL,                  tr_sessionGetRPCUrl( s ) );
408    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME,             tr_sessionGetRPCUsername( s ) );
409    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST,            tr_sessionGetRPCWhitelist( s ) );
410    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    tr_sessionGetRPCWhitelistEnabled( s ) );
411    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
412    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
413    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED,        tr_sessionUsesAltSpeed( s ) );
414    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps,        tr_sessionGetAltSpeed_KBps( s, TR_UP ) );
415    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps,      tr_sessionGetAltSpeed_KBps( s, TR_DOWN ) );
416    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN,     tr_sessionGetAltSpeedBegin( s ) );
417    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED,   tr_sessionUsesAltSpeedTime( s ) );
418    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,       tr_sessionGetAltSpeedEnd( s ) );
419    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,       tr_sessionGetAltSpeedDay( s ) );
420    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps,              tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
421    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED,           tr_sessionIsSpeedLimited( s, TR_UP ) );
422    tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK,                    s->umask );
423    tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, s->uploadSlotsPerTorrent );
424    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4,        tr_address_to_string( &s->public_ipv4->addr ) );
425    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6,        tr_address_to_string( &s->public_ipv6->addr ) );
426    tr_bencDictAddBool( d, TR_PREFS_KEY_START,                    !tr_sessionGetPaused( s ) );
427    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL,           tr_sessionGetDeleteSource( s ) );
428}
429
430bool
431tr_sessionLoadSettings( tr_benc * d, const char * configDir, const char * appName )
432{
433    int err = 0;
434    char * filename;
435    tr_benc fileSettings;
436    tr_benc sessionDefaults;
437    tr_benc tmp;
438    bool success = false;
439
440    assert( tr_bencIsDict( d ) );
441
442    /* initializing the defaults: caller may have passed in some app-level defaults.
443     * preserve those and use the session defaults to fill in any missing gaps. */
444    tr_bencInitDict( &sessionDefaults, 0 );
445    tr_sessionGetDefaultSettings( &sessionDefaults );
446    tr_bencMergeDicts( &sessionDefaults, d );
447    tmp = *d; *d = sessionDefaults; sessionDefaults = tmp;
448
449    /* if caller didn't specify a config dir, use the default */
450    if( !configDir || !*configDir )
451        configDir = tr_getDefaultConfigDir( appName );
452
453    /* file settings override the defaults */
454    filename = tr_buildPath( configDir, "settings.json", NULL );
455    err = tr_bencLoadFile( &fileSettings, TR_FMT_JSON, filename );
456    if( !err ) {
457        tr_bencMergeDicts( d, &fileSettings );
458        tr_bencFree( &fileSettings );
459    }
460
461    /* cleanup */
462    tr_bencFree( &sessionDefaults );
463    tr_free( filename );
464    success = (err==0) || (err==ENOENT);
465    return success;
466}
467
468void
469tr_sessionSaveSettings( tr_session    * session,
470                        const char    * configDir,
471                        const tr_benc * clientSettings )
472{
473    tr_benc settings;
474    char * filename = tr_buildPath( configDir, "settings.json", NULL );
475
476    assert( tr_bencIsDict( clientSettings ) );
477
478    tr_bencInitDict( &settings, 0 );
479
480    /* the existing file settings are the fallback values */
481    {
482        tr_benc fileSettings;
483        const int err = tr_bencLoadFile( &fileSettings, TR_FMT_JSON, filename );
484        if( !err )
485        {
486            tr_bencMergeDicts( &settings, &fileSettings );
487            tr_bencFree( &fileSettings );
488        }
489    }
490
491    /* the client's settings override the file settings */
492    tr_bencMergeDicts( &settings, clientSettings );
493
494    /* the session's true values override the file & client settings */
495    {
496        tr_benc sessionSettings;
497        tr_bencInitDict( &sessionSettings, 0 );
498        tr_sessionGetSettings( session, &sessionSettings );
499        tr_bencMergeDicts( &settings, &sessionSettings );
500        tr_bencFree( &sessionSettings );
501    }
502
503    /* save the result */
504    tr_bencToFile( &settings, TR_FMT_JSON, filename );
505
506    /* cleanup */
507    tr_free( filename );
508    tr_bencFree( &settings );
509}
510
511/***
512****
513***/
514
515/**
516 * Periodically save the .resume files of any torrents whose
517 * status has recently changed. This prevents loss of metadata
518 * in the case of a crash, unclean shutdown, clumsy user, etc.
519 */
520static void
521onSaveTimer( int foo UNUSED, short bar UNUSED, void * vsession )
522{
523    tr_torrent * tor = NULL;
524    tr_session * session = vsession;
525
526    if( tr_cacheFlushDone( session->cache ) )
527        tr_err( "Error while flushing completed pieces from cache" );
528
529    while(( tor = tr_torrentNext( session, tor )))
530        tr_torrentSave( tor );
531
532    tr_statsSaveDirty( session );
533
534    tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 );
535}
536
537/***
538****
539***/
540
541static void tr_sessionInitImpl( void * );
542
543struct init_data
544{
545    tr_session  * session;
546    const char  * configDir;
547    bool          done;
548    bool          messageQueuingEnabled;
549    tr_benc     * clientSettings;
550};
551
552tr_session *
553tr_sessionInit( const char  * tag,
554                const char  * configDir,
555                bool          messageQueuingEnabled,
556                tr_benc     * clientSettings )
557{
558    int64_t i;
559    tr_session * session;
560    struct init_data data;
561
562    assert( tr_bencIsDict( clientSettings ) );
563
564    tr_timeUpdate( time( NULL ) );
565
566    /* initialize the bare skeleton of the session object */
567    session = tr_new0( tr_session, 1 );
568    session->udp_socket = -1;
569    session->udp6_socket = -1;
570    session->bandwidth = tr_bandwidthNew( session, NULL );
571    session->lock = tr_lockNew( );
572    session->cache = tr_cacheNew( 1024*1024*2 );
573    session->tag = tr_strdup( tag );
574    session->magicNumber = SESSION_MAGIC_NUMBER;
575    session->buffer = tr_valloc( SESSION_BUFFER_SIZE );
576    tr_peerIdInit( session->peer_id );
577    tr_bencInitList( &session->removedTorrents, 0 );
578
579    /* nice to start logging at the very beginning */
580    if( tr_bencDictFindInt( clientSettings, TR_PREFS_KEY_MSGLEVEL, &i ) )
581        tr_setMessageLevel( i );
582
583    /* start the libtransmission thread */
584    tr_netInit( ); /* must go before tr_eventInit */
585    tr_eventInit( session );
586    assert( session->events != NULL );
587
588    /* run the rest in the libtransmission thread */
589    data.done = false;
590    data.session = session;
591    data.configDir = configDir;
592    data.messageQueuingEnabled = messageQueuingEnabled;
593    data.clientSettings = clientSettings;
594    tr_runInEventThread( session, tr_sessionInitImpl, &data );
595    while( !data.done )
596        tr_wait_msec( 100 );
597
598    return session;
599}
600
601static void turtleCheckClock( tr_session * s, struct tr_turtle_info * t );
602
603static void
604onNowTimer( int foo UNUSED, short bar UNUSED, void * vsession )
605{
606    int usec;
607    const int min = 100;
608    const int max = 999999;
609    struct timeval tv;
610    tr_torrent * tor = NULL;
611    tr_session * session = vsession;
612    const time_t now = time( NULL );
613
614    assert( tr_isSession( session ) );
615    assert( session->nowTimer != NULL );
616
617    /**
618    ***  tr_session things to do once per second
619    **/
620
621    tr_timeUpdate( now );
622
623    tr_dhtUpkeep( session );
624
625    if( session->turtle.isClockEnabled )
626        turtleCheckClock( session, &session->turtle );
627
628    while(( tor = tr_torrentNext( session, tor ))) {
629        if( tor->isRunning ) {
630            if( tr_torrentIsSeed( tor ) )
631                ++tor->secondsSeeding;
632            else
633                ++tor->secondsDownloading;
634        }
635    }
636
637    /**
638    ***  Set the timer
639    **/
640
641    /* schedule the next timer for right after the next second begins */
642    gettimeofday( &tv, NULL );
643    usec = 1000000 - tv.tv_usec;
644    if( usec > max ) usec = max;
645    if( usec < min ) usec = min;
646    tr_timerAdd( session->nowTimer, 0, usec );
647    /* fprintf( stderr, "time %zu sec, %zu microsec\n", (size_t)tr_time(), (size_t)tv.tv_usec ); */
648}
649
650static void loadBlocklists( tr_session * session );
651
652static void
653tr_sessionInitImpl( void * vdata )
654{
655    tr_benc settings;
656    struct init_data * data = vdata;
657    tr_benc * clientSettings = data->clientSettings;
658    tr_session * session = data->session;
659
660    assert( tr_amInEventThread( session ) );
661    assert( tr_bencIsDict( clientSettings ) );
662
663    dbgmsg( "tr_sessionInit: the session's top-level bandwidth object is %p",
664            session->bandwidth );
665
666    tr_bencInitDict( &settings, 0 );
667    tr_sessionGetDefaultSettings( &settings );
668    tr_bencMergeDicts( &settings, clientSettings );
669
670    assert( session->event_base != NULL );
671    session->nowTimer = evtimer_new( session->event_base, onNowTimer, session );
672    onNowTimer( 0, 0, session );
673
674#ifndef WIN32
675    /* Don't exit when writing on a broken socket */
676    signal( SIGPIPE, SIG_IGN );
677#endif
678
679    tr_setMessageQueuing( data->messageQueuingEnabled );
680
681    tr_setConfigDir( session, data->configDir );
682
683    session->peerMgr = tr_peerMgrNew( session );
684
685    session->shared = tr_sharedInit( session );
686
687    /**
688    ***  Blocklist
689    **/
690
691    {
692        char * filename = tr_buildPath( session->configDir, "blocklists", NULL );
693        tr_mkdirp( filename, 0777 );
694        tr_free( filename );
695        loadBlocklists( session );
696    }
697
698    assert( tr_isSession( session ) );
699
700    session->saveTimer = evtimer_new( session->event_base, onSaveTimer, session );
701    tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 );
702
703    tr_announcerInit( session );
704
705    /* first %s is the application name
706       second %s is the version number */
707    tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING );
708
709    tr_statsInit( session );
710
711    tr_webInit( session );
712
713    tr_sessionSet( session, &settings );
714
715    tr_udpInit( session );
716
717    if( session->isLPDEnabled )
718        tr_lpdInit( session, &session->public_ipv4->addr );
719
720    /* cleanup */
721    tr_bencFree( &settings );
722    data->done = true;
723}
724
725static void turtleBootstrap( tr_session *, struct tr_turtle_info * );
726static void setPeerPort( tr_session * session, tr_port port );
727
728static void
729sessionSetImpl( void * vdata )
730{
731    int64_t i;
732    double  d;
733    bool boolVal;
734    const char * str;
735    struct tr_bindinfo b;
736    struct init_data * data = vdata;
737    tr_session * session = data->session;
738    tr_benc * settings = data->clientSettings;
739    struct tr_turtle_info * turtle = &session->turtle;
740
741    assert( tr_isSession( session ) );
742    assert( tr_bencIsDict( settings ) );
743    assert( tr_amInEventThread( session ) );
744
745    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_MSGLEVEL, &i ) )
746        tr_setMessageLevel( i );
747
748    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_UMASK, &i ) ) {
749        session->umask = (mode_t)i;
750        umask( session->umask );
751    }
752
753    /* misc features */
754    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
755        tr_sessionSetCacheLimit_MB( session, i );
756    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
757        tr_sessionSetPeerLimitPerTorrent( session, i );
758    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
759        tr_sessionSetPexEnabled( session, boolVal );
760    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
761        tr_sessionSetDHTEnabled( session, boolVal );
762    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_UTP_ENABLED, &boolVal ) )
763        tr_sessionSetUTPEnabled( session, boolVal );
764    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
765        tr_sessionSetLPDEnabled( session, boolVal );
766    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ENCRYPTION, &i ) )
767        tr_sessionSetEncryption( session, i );
768    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_PEER_SOCKET_TOS, &str ) )
769        session->peerSocketTOS = parse_tos( str );
770    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM, &str ) )
771        session->peer_congestion_algorithm = tr_strdup(str);
772    else
773        session->peer_congestion_algorithm = tr_strdup("");
774    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
775        tr_blocklistSetEnabled( session, boolVal );
776    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_BLOCKLIST_URL, &str ) )
777        tr_blocklistSetURL( session, str );
778    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_START, &boolVal ) )
779        tr_sessionSetPaused( session, !boolVal );
780    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal) )
781        tr_sessionSetDeleteSource( session, boolVal );
782
783    /* files and directories */
784    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PREFETCH_ENABLED, &boolVal ) )
785        session->isPrefetchEnabled = boolVal;
786    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PREALLOCATION, &i ) )
787        session->preallocationMode = i;
788    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
789        tr_sessionSetDownloadDir( session, str );
790    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
791        tr_sessionSetIncompleteDir( session, str );
792    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
793        tr_sessionSetIncompleteDirEnabled( session, boolVal );
794    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
795        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
796
797    /* rpc server */
798    if( session->rpcServer != NULL ) /* close the old one */
799        tr_rpcClose( &session->rpcServer );
800    session->rpcServer = tr_rpcInit( session, settings );
801
802    /* public addresses */
803
804    free_incoming_peer_port( session );
805
806    str = TR_PREFS_KEY_BIND_ADDRESS_IPV4;
807    tr_bencDictFindStr( settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, &str );
808    if( !tr_address_from_string( &b.addr, str ) || ( b.addr.type != TR_AF_INET ) )
809        b.addr = tr_inaddr_any;
810    b.socket = -1;
811    session->public_ipv4 = tr_memdup( &b, sizeof( struct tr_bindinfo ) );
812
813    str = TR_PREFS_KEY_BIND_ADDRESS_IPV6;
814    tr_bencDictFindStr( settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, &str );
815    if( !tr_address_from_string( &b.addr, str ) || ( b.addr.type != TR_AF_INET6 ) )
816        b.addr = tr_in6addr_any;
817    b.socket = -1;
818    session->public_ipv6 = tr_memdup( &b, sizeof( struct tr_bindinfo ) );
819
820    /* incoming peer port */
821    if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, &i ) )
822        session->randomPortLow = i;
823    if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, &i ) )
824        session->randomPortHigh = i;
825    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
826        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
827    if( !tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_PORT, &i ) )
828        i = session->private_peer_port;
829    setPeerPort( session, boolVal ? getRandomPort( session ) : i );
830    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
831        tr_sessionSetPortForwardingEnabled( session, boolVal );
832
833    /* file and peer socket limits */
834    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
835        tr_fdSetPeerLimit( session, i );
836    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_OPEN_FILE_LIMIT, &i ) )
837        tr_fdSetFileLimit( session, i );
838
839    /**
840    **/
841
842    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, &i ) )
843        session->uploadSlotsPerTorrent = i;
844
845    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_USPEED_KBps, &i ) )
846        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
847    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
848        tr_sessionLimitSpeed( session, TR_UP, boolVal );
849
850    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_DSPEED_KBps, &i ) )
851        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
852    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
853        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
854
855    if( tr_bencDictFindReal( settings, TR_PREFS_KEY_RATIO, &d ) )
856        tr_sessionSetRatioLimit( session, d );
857    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_RATIO_ENABLED, &boolVal ) )
858        tr_sessionSetRatioLimited( session, boolVal );
859
860    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
861        tr_sessionSetIdleLimit( session, i );
862    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
863        tr_sessionSetIdleLimited( session, boolVal );
864
865    /**
866    ***  Turtle Mode
867    **/
868
869    /* update the turtle mode's fields */
870    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
871        turtle->speedLimit_Bps[TR_UP] = toSpeedBytes( i );
872    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
873        turtle->speedLimit_Bps[TR_DOWN] = toSpeedBytes( i );
874    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
875        turtle->beginMinute = i;
876    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
877        turtle->endMinute = i;
878    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
879        turtle->days = i;
880    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
881        turtle->isClockEnabled = boolVal;
882    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
883        turtle->isEnabled = boolVal;
884    turtleBootstrap( session, turtle );
885
886    /**
887    ***  Scripts
888    **/
889
890    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
891        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
892    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
893        tr_sessionSetTorrentDoneScript( session, str );
894
895    data->done = true;
896}
897
898void
899tr_sessionSet( tr_session * session, struct tr_benc  * settings )
900{
901    struct init_data data;
902    data.done = false;
903    data.session = session;
904    data.clientSettings = settings;
905
906    /* run the rest in the libtransmission thread */
907    tr_runInEventThread( session, sessionSetImpl, &data );
908    while( !data.done )
909        tr_wait_msec( 100 );
910}
911
912/***
913****
914***/
915
916void
917tr_sessionSetDownloadDir( tr_session * session, const char * dir )
918{
919    assert( tr_isSession( session ) );
920
921    if( session->downloadDir != dir )
922    {
923        tr_free( session->downloadDir );
924        session->downloadDir = tr_strdup( dir );
925    }
926}
927
928const char *
929tr_sessionGetDownloadDir( const tr_session * session )
930{
931    assert( tr_isSession( session ) );
932
933    return session->downloadDir;
934}
935
936int64_t
937tr_sessionGetDownloadDirFreeSpace( const tr_session * session )
938{
939    assert( tr_isSession( session ) );
940
941    return tr_getFreeSpace( session->downloadDir );
942}
943
944/***
945****
946***/
947
948void
949tr_sessionSetIncompleteFileNamingEnabled( tr_session * session, bool b )
950{
951    assert( tr_isSession( session ) );
952    assert( tr_isBool( b ) );
953
954    session->isIncompleteFileNamingEnabled = b;
955}
956
957bool
958tr_sessionIsIncompleteFileNamingEnabled( const tr_session * session )
959{
960    assert( tr_isSession( session ) );
961
962    return session->isIncompleteFileNamingEnabled;
963}
964
965/***
966****
967***/
968
969
970void
971tr_sessionSetIncompleteDir( tr_session * session, const char * dir )
972{
973    assert( tr_isSession( session ) );
974
975    if( session->incompleteDir != dir )
976    {
977        tr_free( session->incompleteDir );
978
979        session->incompleteDir = tr_strdup( dir );
980    }
981}
982
983const char*
984tr_sessionGetIncompleteDir( const tr_session * session )
985{
986    assert( tr_isSession( session ) );
987
988    return session->incompleteDir;
989}
990
991void
992tr_sessionSetIncompleteDirEnabled( tr_session * session, bool b )
993{
994    assert( tr_isSession( session ) );
995    assert( tr_isBool( b ) );
996
997    session->isIncompleteDirEnabled = b;
998}
999
1000bool
1001tr_sessionIsIncompleteDirEnabled( const tr_session * session )
1002{
1003    assert( tr_isSession( session ) );
1004
1005    return session->isIncompleteDirEnabled;
1006}
1007
1008/***
1009****
1010***/
1011
1012void*
1013tr_sessionGetBuffer( tr_session * session )
1014{
1015    assert( tr_isSession( session ) );
1016    assert( !session->bufferInUse );
1017    assert( tr_amInEventThread( session ) );
1018
1019    session->bufferInUse = true;
1020    return session->buffer;
1021}
1022
1023void
1024tr_sessionReleaseBuffer( tr_session * session )
1025{
1026    assert( tr_isSession( session ) );
1027    assert( session->bufferInUse );
1028    assert( tr_amInEventThread( session ) );
1029
1030    session->bufferInUse = false;
1031}
1032
1033void
1034tr_sessionLock( tr_session * session )
1035{
1036    assert( tr_isSession( session ) );
1037
1038    tr_lockLock( session->lock );
1039}
1040
1041void
1042tr_sessionUnlock( tr_session * session )
1043{
1044    assert( tr_isSession( session ) );
1045
1046    tr_lockUnlock( session->lock );
1047}
1048
1049bool
1050tr_sessionIsLocked( const tr_session * session )
1051{
1052    return tr_isSession( session ) && tr_lockHave( session->lock );
1053}
1054
1055/***********************************************************************
1056 * tr_setBindPort
1057 ***********************************************************************
1058 *
1059 **********************************************************************/
1060
1061static void
1062peerPortChanged( void * session )
1063{
1064    tr_torrent * tor = NULL;
1065
1066    assert( tr_isSession( session ) );
1067
1068    close_incoming_peer_port( session );
1069    open_incoming_peer_port( session );
1070    tr_sharedPortChanged( session );
1071
1072    while(( tor = tr_torrentNext( session, tor )))
1073        tr_torrentChangeMyPort( tor );
1074}
1075
1076static void
1077setPeerPort( tr_session * session, tr_port port )
1078{
1079    session->private_peer_port = port;
1080    session->public_peer_port = port;
1081
1082    tr_runInEventThread( session, peerPortChanged, session );
1083}
1084
1085void
1086tr_sessionSetPeerPort( tr_session * session, tr_port port )
1087{
1088    if( tr_isSession( session ) && ( session->private_peer_port != port ) )
1089    {
1090        setPeerPort( session, port );
1091    }
1092}
1093
1094tr_port
1095tr_sessionGetPeerPort( const tr_session * session )
1096{
1097    return tr_isSession( session ) ? session->private_peer_port : 0;
1098}
1099
1100tr_port
1101tr_sessionSetPeerPortRandom( tr_session * session )
1102{
1103    assert( tr_isSession( session ) );
1104
1105    tr_sessionSetPeerPort( session, getRandomPort( session ) );
1106    return session->private_peer_port;
1107}
1108
1109void
1110tr_sessionSetPeerPortRandomOnStart( tr_session * session,
1111                                    bool random )
1112{
1113    assert( tr_isSession( session ) );
1114
1115    session->isPortRandom = random;
1116}
1117
1118bool
1119tr_sessionGetPeerPortRandomOnStart( tr_session * session )
1120{
1121    assert( tr_isSession( session ) );
1122
1123    return session->isPortRandom;
1124}
1125
1126tr_port_forwarding
1127tr_sessionGetPortForwarding( const tr_session * session )
1128{
1129    assert( tr_isSession( session ) );
1130
1131    return tr_sharedTraversalStatus( session->shared );
1132}
1133
1134/***
1135****
1136***/
1137
1138void
1139tr_sessionSetRatioLimited( tr_session * session, bool isLimited )
1140{
1141    assert( tr_isSession( session ) );
1142
1143    session->isRatioLimited = isLimited;
1144}
1145
1146void
1147tr_sessionSetRatioLimit( tr_session * session, double desiredRatio )
1148{
1149    assert( tr_isSession( session ) );
1150
1151    session->desiredRatio = desiredRatio;
1152}
1153
1154bool
1155tr_sessionIsRatioLimited( const tr_session  * session )
1156{
1157    assert( tr_isSession( session ) );
1158
1159    return session->isRatioLimited;
1160}
1161
1162double
1163tr_sessionGetRatioLimit( const tr_session * session )
1164{
1165    assert( tr_isSession( session ) );
1166
1167    return session->desiredRatio;
1168}
1169
1170/***
1171****
1172***/
1173
1174void
1175tr_sessionSetIdleLimited( tr_session * session, bool isLimited )
1176{
1177    assert( tr_isSession( session ) );
1178
1179    session->isIdleLimited = isLimited;
1180}
1181
1182void
1183tr_sessionSetIdleLimit( tr_session * session, uint16_t idleMinutes )
1184{
1185    assert( tr_isSession( session ) );
1186
1187    session->idleLimitMinutes = idleMinutes;
1188}
1189
1190bool
1191tr_sessionIsIdleLimited( const tr_session  * session )
1192{
1193    assert( tr_isSession( session ) );
1194
1195    return session->isIdleLimited;
1196}
1197
1198uint16_t
1199tr_sessionGetIdleLimit( const tr_session * session )
1200{
1201    assert( tr_isSession( session ) );
1202
1203    return session->idleLimitMinutes;
1204}
1205
1206/***
1207****
1208****  SPEED LIMITS
1209****
1210***/
1211
1212bool
1213tr_sessionGetActiveSpeedLimit_Bps( const tr_session * session, tr_direction dir, int * setme_Bps )
1214{
1215    int isLimited = true;
1216
1217    if( !tr_isSession( session ) )
1218        return false;
1219
1220    if( tr_sessionUsesAltSpeed( session ) )
1221        *setme_Bps = tr_sessionGetAltSpeed_Bps( session, dir );
1222    else if( tr_sessionIsSpeedLimited( session, dir ) )
1223        *setme_Bps = tr_sessionGetSpeedLimit_Bps( session, dir );
1224    else
1225        isLimited = false;
1226
1227    return isLimited;
1228}
1229bool
1230tr_sessionGetActiveSpeedLimit_KBps( const tr_session  * session,
1231                                    tr_direction        dir,
1232                                    double            * setme_KBps )
1233{
1234    int Bps = 0;
1235    const bool is_active = tr_sessionGetActiveSpeedLimit_Bps( session, dir, &Bps );
1236    *setme_KBps = toSpeedKBps( Bps );
1237    return is_active;
1238}
1239
1240static void
1241updateBandwidth( tr_session * session, tr_direction dir )
1242{
1243    int limit_Bps = 0;
1244    const bool isLimited = tr_sessionGetActiveSpeedLimit_Bps( session, dir, &limit_Bps );
1245    const bool zeroCase = isLimited && !limit_Bps;
1246
1247    tr_bandwidthSetLimited( session->bandwidth, dir, isLimited && !zeroCase );
1248
1249    tr_bandwidthSetDesiredSpeed_Bps( session->bandwidth, dir, limit_Bps );
1250}
1251
1252enum
1253{
1254    MINUTES_PER_HOUR = 60,
1255    MINUTES_PER_DAY = MINUTES_PER_HOUR * 24,
1256    MINUTES_PER_WEEK = MINUTES_PER_DAY * 7
1257};
1258
1259static void
1260turtleUpdateTable( struct tr_turtle_info * t )
1261{
1262    int day;
1263    tr_bitfield * b = &t->minutes;
1264
1265    tr_bitfieldSetHasNone( b );
1266
1267    for( day=0; day<7; ++day )
1268    {
1269        if( t->days & (1<<day) )
1270        {
1271            int i;
1272            const time_t begin = t->beginMinute;
1273            time_t end = t->endMinute;
1274
1275            if( end <= begin )
1276                end += MINUTES_PER_DAY;
1277
1278            for( i=begin; i<end; ++i )
1279                tr_bitfieldAdd( b, (i+day*MINUTES_PER_DAY) % MINUTES_PER_WEEK );
1280        }
1281    }
1282}
1283
1284static void
1285altSpeedToggled( void * vsession )
1286{
1287    tr_session * session = vsession;
1288    struct tr_turtle_info * t = &session->turtle;
1289
1290    assert( tr_isSession( session ) );
1291
1292    updateBandwidth( session, TR_UP );
1293    updateBandwidth( session, TR_DOWN );
1294
1295    if( t->callback != NULL )
1296        (*t->callback)( session, t->isEnabled, t->changedByUser, t->callbackUserData );
1297}
1298
1299static void
1300useAltSpeed( tr_session * s, struct tr_turtle_info * t,
1301             bool enabled, bool byUser )
1302{
1303    assert( tr_isSession( s ) );
1304    assert( t != NULL );
1305    assert( tr_isBool( enabled ) );
1306    assert( tr_isBool( byUser ) );
1307
1308    if( t->isEnabled != enabled )
1309    {
1310        t->isEnabled = enabled;
1311        t->changedByUser = byUser;
1312        tr_runInEventThread( s, altSpeedToggled, s );
1313    }
1314}
1315
1316/**
1317 * @param enabled whether turtle should be on/off according to the scheduler
1318 * @param changed whether that's different from the previous minute
1319 */
1320static void
1321testTurtleTime( const struct tr_turtle_info * t,
1322                bool * enabled,
1323                bool * changed )
1324{
1325    bool e;
1326    struct tm tm;
1327    size_t minute_of_the_week;
1328    const time_t now = tr_time( );
1329
1330    tr_localtime_r( &now, &tm );
1331
1332    minute_of_the_week = tm.tm_wday * MINUTES_PER_DAY
1333                       + tm.tm_hour * MINUTES_PER_HOUR
1334                       + tm.tm_min;
1335    if( minute_of_the_week >= MINUTES_PER_WEEK ) /* leap minutes? */
1336        minute_of_the_week = MINUTES_PER_WEEK - 1;
1337
1338    e = tr_bitfieldHasFast( &t->minutes, minute_of_the_week );
1339    if( enabled != NULL )
1340        *enabled = e;
1341
1342    if( changed != NULL )
1343    {
1344        const size_t prev = minute_of_the_week > 0 ? minute_of_the_week - 1
1345                                                   : MINUTES_PER_WEEK - 1;
1346        *changed = e != tr_bitfieldHasFast( &t->minutes, prev );
1347    }
1348}
1349
1350static void
1351turtleCheckClock( tr_session * s, struct tr_turtle_info * t )
1352{
1353    bool enabled;
1354    bool changed;
1355
1356    assert( t->isClockEnabled );
1357
1358    testTurtleTime( t, &enabled, &changed );
1359
1360    if( changed )
1361    {
1362        tr_inf( "Time to turn %s turtle mode!", (enabled?"on":"off") );
1363        useAltSpeed( s, t, enabled, false );
1364    }
1365}
1366
1367/* Called after the turtle's fields are loaded from an outside source.
1368 * It initializes the implementation fields
1369 * and turns on turtle mode if the clock settings say to. */
1370static void
1371turtleBootstrap( tr_session * session, struct tr_turtle_info * turtle )
1372{
1373    turtle->changedByUser = false;
1374
1375    tr_bitfieldConstruct( &turtle->minutes, MINUTES_PER_WEEK );
1376
1377    turtleUpdateTable( turtle );
1378
1379    if( turtle->isClockEnabled )
1380        testTurtleTime( turtle, &turtle->isEnabled, NULL );
1381
1382    altSpeedToggled( session );
1383}
1384
1385/***
1386****  Primary session speed limits
1387***/
1388
1389void
1390tr_sessionSetSpeedLimit_Bps( tr_session * s, tr_direction d, int Bps )
1391{
1392    assert( tr_isSession( s ) );
1393    assert( tr_isDirection( d ) );
1394    assert( Bps >= 0 );
1395
1396    s->speedLimit_Bps[d] = Bps;
1397
1398    updateBandwidth( s, d );
1399}
1400void
1401tr_sessionSetSpeedLimit_KBps( tr_session * s, tr_direction d, int KBps )
1402{
1403    tr_sessionSetSpeedLimit_Bps( s, d, toSpeedBytes( KBps ) );
1404}
1405
1406int
1407tr_sessionGetSpeedLimit_Bps( const tr_session * s, tr_direction d )
1408{
1409    assert( tr_isSession( s ) );
1410    assert( tr_isDirection( d ) );
1411
1412    return s->speedLimit_Bps[d];
1413}
1414int
1415tr_sessionGetSpeedLimit_KBps( const tr_session * s, tr_direction d )
1416{
1417    return toSpeedKBps( tr_sessionGetSpeedLimit_Bps( s, d ) );
1418}
1419
1420void
1421tr_sessionLimitSpeed( tr_session * s, tr_direction d, bool b )
1422{
1423    assert( tr_isSession( s ) );
1424    assert( tr_isDirection( d ) );
1425    assert( tr_isBool( b ) );
1426
1427    s->speedLimitEnabled[d] = b;
1428
1429    updateBandwidth( s, d );
1430}
1431
1432bool
1433tr_sessionIsSpeedLimited( const tr_session * s, tr_direction d )
1434{
1435    assert( tr_isSession( s ) );
1436    assert( tr_isDirection( d ) );
1437
1438    return s->speedLimitEnabled[d];
1439}
1440
1441/***
1442****  Alternative speed limits that are used during scheduled times
1443***/
1444
1445void
1446tr_sessionSetAltSpeed_Bps( tr_session * s, tr_direction d, int Bps )
1447{
1448    assert( tr_isSession( s ) );
1449    assert( tr_isDirection( d ) );
1450    assert( Bps >= 0 );
1451
1452    s->turtle.speedLimit_Bps[d] = Bps;
1453
1454    updateBandwidth( s, d );
1455}
1456
1457void
1458tr_sessionSetAltSpeed_KBps( tr_session * s, tr_direction d, int KBps )
1459{
1460    tr_sessionSetAltSpeed_Bps( s, d, toSpeedBytes( KBps ) );
1461}
1462
1463int
1464tr_sessionGetAltSpeed_Bps( const tr_session * s, tr_direction d )
1465{
1466    assert( tr_isSession( s ) );
1467    assert( tr_isDirection( d ) );
1468
1469    return s->turtle.speedLimit_Bps[d];
1470}
1471int
1472tr_sessionGetAltSpeed_KBps( const tr_session * s, tr_direction d )
1473{
1474    return toSpeedKBps( tr_sessionGetAltSpeed_Bps( s, d ) );
1475}
1476
1477static void
1478userPokedTheClock( tr_session * s, struct tr_turtle_info * t )
1479{
1480    tr_dbg( "Refreshing the turtle mode clock due to user changes" );
1481
1482    turtleUpdateTable( t );
1483
1484    if( t->isClockEnabled )
1485    {
1486        bool enabled, changed;
1487        testTurtleTime( t, &enabled, &changed );
1488        useAltSpeed( s, t, enabled, true );
1489    }
1490}
1491
1492void
1493tr_sessionUseAltSpeedTime( tr_session * s, bool b )
1494{
1495    struct tr_turtle_info * t = &s->turtle;
1496
1497    assert( tr_isSession( s ) );
1498    assert( tr_isBool ( b ) );
1499
1500    if( t->isClockEnabled != b ) {
1501        t->isClockEnabled = b;
1502        userPokedTheClock( s, t );
1503    }
1504}
1505
1506bool
1507tr_sessionUsesAltSpeedTime( const tr_session * s )
1508{
1509    assert( tr_isSession( s ) );
1510
1511    return s->turtle.isClockEnabled;
1512}
1513
1514void
1515tr_sessionSetAltSpeedBegin( tr_session * s, int minute )
1516{
1517    assert( tr_isSession( s ) );
1518    assert( 0<=minute && minute<(60*24) );
1519
1520    if( s->turtle.beginMinute != minute ) {
1521        s->turtle.beginMinute = minute;
1522        userPokedTheClock( s, &s->turtle );
1523    }
1524}
1525
1526int
1527tr_sessionGetAltSpeedBegin( const tr_session * s )
1528{
1529    assert( tr_isSession( s ) );
1530
1531    return s->turtle.beginMinute;
1532}
1533
1534void
1535tr_sessionSetAltSpeedEnd( tr_session * s, int minute )
1536{
1537    assert( tr_isSession( s ) );
1538    assert( 0<=minute && minute<(60*24) );
1539
1540    if( s->turtle.endMinute != minute ) {
1541        s->turtle.endMinute = minute;
1542        userPokedTheClock( s, &s->turtle );
1543    }
1544}
1545
1546int
1547tr_sessionGetAltSpeedEnd( const tr_session * s )
1548{
1549    assert( tr_isSession( s ) );
1550
1551    return s->turtle.endMinute;
1552}
1553
1554void
1555tr_sessionSetAltSpeedDay( tr_session * s, tr_sched_day days )
1556{
1557    assert( tr_isSession( s ) );
1558
1559    if( s->turtle.days != days ) {
1560        s->turtle.days = days;
1561        userPokedTheClock( s, &s->turtle );
1562    }
1563}
1564
1565tr_sched_day
1566tr_sessionGetAltSpeedDay( const tr_session * s )
1567{
1568    assert( tr_isSession( s ) );
1569
1570    return s->turtle.days;
1571}
1572
1573void
1574tr_sessionUseAltSpeed( tr_session * session, bool enabled )
1575{
1576    useAltSpeed( session, &session->turtle, enabled, true );
1577}
1578
1579bool
1580tr_sessionUsesAltSpeed( const tr_session * s )
1581{
1582    assert( tr_isSession( s ) );
1583
1584    return s->turtle.isEnabled;
1585}
1586
1587void
1588tr_sessionSetAltSpeedFunc( tr_session       * session,
1589                           tr_altSpeedFunc    func,
1590                           void             * userData )
1591{
1592    assert( tr_isSession( session ) );
1593
1594    session->turtle.callback = func;
1595    session->turtle.callbackUserData = userData;
1596}
1597
1598void
1599tr_sessionClearAltSpeedFunc( tr_session * session )
1600{
1601    tr_sessionSetAltSpeedFunc( session, NULL, NULL );
1602}
1603
1604/***
1605****
1606***/
1607
1608void
1609tr_sessionSetPeerLimit( tr_session * session, uint16_t maxGlobalPeers )
1610{
1611    assert( tr_isSession( session ) );
1612
1613    tr_fdSetPeerLimit( session, maxGlobalPeers );
1614}
1615
1616uint16_t
1617tr_sessionGetPeerLimit( const tr_session * session )
1618{
1619    assert( tr_isSession( session ) );
1620
1621    return tr_fdGetPeerLimit( session );
1622}
1623
1624void
1625tr_sessionSetPeerLimitPerTorrent( tr_session  * session, uint16_t n )
1626{
1627    assert( tr_isSession( session ) );
1628
1629    session->peerLimitPerTorrent = n;
1630}
1631
1632uint16_t
1633tr_sessionGetPeerLimitPerTorrent( const tr_session * session )
1634{
1635    assert( tr_isSession( session ) );
1636
1637    return session->peerLimitPerTorrent;
1638}
1639
1640/***
1641****
1642***/
1643
1644void
1645tr_sessionSetPaused( tr_session * session, bool isPaused )
1646{
1647    assert( tr_isSession( session ) );
1648
1649    session->pauseAddedTorrent = isPaused;
1650}
1651
1652bool
1653tr_sessionGetPaused( const tr_session * session )
1654{
1655    assert( tr_isSession( session ) );
1656
1657    return session->pauseAddedTorrent;
1658}
1659
1660void
1661tr_sessionSetDeleteSource( tr_session * session, bool deleteSource )
1662{
1663    assert( tr_isSession( session ) );
1664
1665    session->deleteSourceTorrent = deleteSource;
1666}
1667
1668bool
1669tr_sessionGetDeleteSource( const tr_session * session )
1670{
1671    assert( tr_isSession( session ) );
1672
1673    return session->deleteSourceTorrent;
1674}
1675
1676/***
1677****
1678***/
1679
1680int
1681tr_sessionGetPieceSpeed_Bps( const tr_session * session, tr_direction dir )
1682{
1683    return tr_isSession( session ) ? tr_bandwidthGetPieceSpeed_Bps( session->bandwidth, 0, dir ) : 0;
1684}
1685
1686int
1687tr_sessionGetRawSpeed_Bps( const tr_session * session, tr_direction dir )
1688{
1689    return tr_isSession( session ) ? tr_bandwidthGetRawSpeed_Bps( session->bandwidth, 0, dir ) : 0;
1690}
1691double
1692tr_sessionGetRawSpeed_KBps( const tr_session * session, tr_direction dir )
1693{
1694    return toSpeedKBps( tr_sessionGetRawSpeed_Bps( session, dir ) );
1695}
1696
1697
1698int
1699tr_sessionCountTorrents( const tr_session * session )
1700{
1701    return tr_isSession( session ) ? session->torrentCount : 0;
1702}
1703
1704static int
1705compareTorrentByCur( const void * va, const void * vb )
1706{
1707    const tr_torrent * a = *(const tr_torrent**)va;
1708    const tr_torrent * b = *(const tr_torrent**)vb;
1709    const uint64_t     aCur = a->downloadedCur + a->uploadedCur;
1710    const uint64_t     bCur = b->downloadedCur + b->uploadedCur;
1711
1712    if( aCur != bCur )
1713        return aCur > bCur ? -1 : 1; /* close the biggest torrents first */
1714
1715    return 0;
1716}
1717
1718static void closeBlocklists( tr_session * );
1719
1720static void
1721sessionCloseImpl( void * vsession )
1722{
1723    tr_session *  session = vsession;
1724    tr_torrent *  tor;
1725    int           i, n;
1726    tr_torrent ** torrents;
1727
1728    assert( tr_isSession( session ) );
1729
1730    free_incoming_peer_port( session );
1731
1732    if( session->isLPDEnabled )
1733        tr_lpdUninit( session );
1734
1735    tr_utpClose( session );
1736    tr_dhtUninit( session );
1737
1738    event_free( session->saveTimer );
1739    session->saveTimer = NULL;
1740
1741    event_free( session->nowTimer );
1742    session->nowTimer = NULL;
1743
1744    tr_verifyClose( session );
1745    tr_sharedClose( session );
1746    tr_rpcClose( &session->rpcServer );
1747
1748    /* Close the torrents. Get the most active ones first so that
1749     * if we can't get them all closed in a reasonable amount of time,
1750     * at least we get the most important ones first. */
1751    tor = NULL;
1752    n = session->torrentCount;
1753    torrents = tr_new( tr_torrent *, session->torrentCount );
1754    for( i = 0; i < n; ++i )
1755        torrents[i] = tor = tr_torrentNext( session, tor );
1756    qsort( torrents, n, sizeof( tr_torrent* ), compareTorrentByCur );
1757    for( i = 0; i < n; ++i )
1758        tr_torrentFree( torrents[i] );
1759    tr_free( torrents );
1760
1761    /* Close the announcer *after* closing the torrents
1762       so that all the &event=stopped messages will be
1763       queued to be sent by tr_announcerClose() */
1764    tr_announcerClose( session );
1765
1766    /* and this goes *after* announcer close so that
1767       it won't be idle until the announce events are sent... */
1768    tr_webClose( session, TR_WEB_CLOSE_WHEN_IDLE );
1769
1770    tr_cacheFree( session->cache );
1771    session->cache = NULL;
1772
1773    /* gotta keep udp running long enough to send out all
1774       the &event=stopped UDP tracker messages */
1775    while( !tr_tracker_udp_is_idle( session ) ) {
1776        tr_tracker_udp_upkeep( session );
1777        tr_wait_msec( 100 );
1778    }
1779
1780    /* we had to wait until UDP trackers were closed before closing these: */
1781    evdns_base_free( session->evdns_base, 0 );
1782    session->evdns_base = NULL;
1783    tr_tracker_udp_close( session );
1784    tr_udpUninit( session );
1785
1786    tr_statsClose( session );
1787    tr_peerMgrFree( session->peerMgr );
1788
1789    closeBlocklists( session );
1790
1791    tr_fdClose( session );
1792
1793    session->isClosed = true;
1794}
1795
1796static int
1797deadlineReached( const time_t deadline )
1798{
1799    return time( NULL ) >= deadline;
1800}
1801
1802#define SHUTDOWN_MAX_SECONDS 20
1803
1804void
1805tr_sessionClose( tr_session * session )
1806{
1807    const time_t deadline = time( NULL ) + SHUTDOWN_MAX_SECONDS;
1808
1809    assert( tr_isSession( session ) );
1810
1811    dbgmsg( "shutting down transmission session %p... now is %zu, deadline is %zu", session, (size_t)time(NULL), (size_t)deadline );
1812
1813    /* close the session */
1814    tr_runInEventThread( session, sessionCloseImpl, session );
1815    while( !session->isClosed && !deadlineReached( deadline ) )
1816    {
1817        dbgmsg( "waiting for the libtransmission thread to finish" );
1818        tr_wait_msec( 100 );
1819    }
1820
1821    /* "shared" and "tracker" have live sockets,
1822     * so we need to keep the transmission thread alive
1823     * for a bit while they tell the router & tracker
1824     * that we're closing now */
1825    while( ( session->shared || session->web || session->announcer || session->announcer_udp )
1826           && !deadlineReached( deadline ) )
1827    {
1828        dbgmsg( "waiting on port unmap (%p) or announcer (%p)... now %zu deadline %zu",
1829                session->shared, session->announcer, (size_t)time(NULL), (size_t)deadline );
1830        tr_wait_msec( 100 );
1831    }
1832
1833    tr_webClose( session, TR_WEB_CLOSE_NOW );
1834
1835    /* close the libtransmission thread */
1836    tr_eventClose( session );
1837    while( session->events != NULL )
1838    {
1839        static bool forced = false;
1840        dbgmsg( "waiting for libtransmission thread to finish... now %zu deadline %zu", (size_t)time(NULL), (size_t)deadline );
1841        tr_wait_msec( 500 );
1842        if( deadlineReached( deadline ) && !forced )
1843        {
1844            dbgmsg( "calling event_loopbreak()" );
1845            forced = true;
1846            event_base_loopbreak( session->event_base );
1847        }
1848        if( deadlineReached( deadline+3 ) )
1849        {
1850            dbgmsg( "deadline+3 reached... calling break...\n" );
1851            break;
1852        }
1853    }
1854
1855    /* free the session memory */
1856    tr_bencFree( &session->removedTorrents );
1857    tr_bandwidthFree( session->bandwidth );
1858    tr_bitfieldDestruct( &session->turtle.minutes );
1859    tr_lockFree( session->lock );
1860    if( session->metainfoLookup ) {
1861        tr_bencFree( session->metainfoLookup );
1862        tr_free( session->metainfoLookup );
1863    }
1864    tr_free( session->torrentDoneScript );
1865    tr_free( session->buffer );
1866    tr_free( session->tag );
1867    tr_free( session->configDir );
1868    tr_free( session->resumeDir );
1869    tr_free( session->torrentDir );
1870    tr_free( session->downloadDir );
1871    tr_free( session->incompleteDir );
1872    tr_free( session->blocklist_url );
1873    tr_free( session->peer_congestion_algorithm );
1874    tr_free( session );
1875}
1876
1877tr_torrent **
1878tr_sessionLoadTorrents( tr_session * session,
1879                        tr_ctor    * ctor,
1880                        int        * setmeCount )
1881{
1882    int           i, n = 0;
1883    struct stat   sb;
1884    DIR *         odir = NULL;
1885    const char *  dirname = tr_getTorrentDir( session );
1886    tr_torrent ** torrents;
1887    tr_list *     l = NULL, *list = NULL;
1888
1889    assert( tr_isSession( session ) );
1890
1891    tr_ctorSetSave( ctor, false ); /* since we already have them */
1892
1893    if( !stat( dirname, &sb )
1894      && S_ISDIR( sb.st_mode )
1895      && ( ( odir = opendir ( dirname ) ) ) )
1896    {
1897        struct dirent *d;
1898        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1899        {
1900            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
1901            {
1902                tr_torrent * tor;
1903                char * path = tr_buildPath( dirname, d->d_name, NULL );
1904                tr_ctorSetMetainfoFromFile( ctor, path );
1905                if(( tor = tr_torrentNew( ctor, NULL )))
1906                {
1907                    tr_list_append( &list, tor );
1908                    ++n;
1909                }
1910                tr_free( path );
1911            }
1912        }
1913        closedir( odir );
1914    }
1915
1916    torrents = tr_new( tr_torrent *, n );
1917    for( i = 0, l = list; l != NULL; l = l->next )
1918        torrents[i++] = (tr_torrent*) l->data;
1919    assert( i == n );
1920
1921    tr_list_free( &list, NULL );
1922
1923    if( n )
1924        tr_inf( _( "Loaded %d torrents" ), n );
1925
1926    if( setmeCount )
1927        *setmeCount = n;
1928    return torrents;
1929}
1930
1931/***
1932****
1933***/
1934
1935void
1936tr_sessionSetPexEnabled( tr_session * session, bool enabled )
1937{
1938    assert( tr_isSession( session ) );
1939
1940    session->isPexEnabled = enabled != 0;
1941}
1942
1943bool
1944tr_sessionIsPexEnabled( const tr_session * session )
1945{
1946    assert( tr_isSession( session ) );
1947
1948    return session->isPexEnabled;
1949}
1950
1951bool
1952tr_sessionAllowsDHT( const tr_session * session UNUSED )
1953{
1954    return tr_sessionIsDHTEnabled( session );
1955}
1956
1957bool
1958tr_sessionIsDHTEnabled( const tr_session * session )
1959{
1960    assert( tr_isSession( session ) );
1961
1962    return session->isDHTEnabled;
1963}
1964
1965static void
1966toggleDHTImpl(  void * data )
1967{
1968    tr_session * session = data;
1969    assert( tr_isSession( session ) );
1970
1971    tr_udpUninit( session );
1972    session->isDHTEnabled = !session->isDHTEnabled;
1973    tr_udpInit( session );
1974}
1975
1976void
1977tr_sessionSetDHTEnabled( tr_session * session, bool enabled )
1978{
1979    assert( tr_isSession( session ) );
1980    assert( tr_isBool( enabled ) );
1981
1982    if( ( enabled != 0 ) != ( session->isDHTEnabled != 0 ) )
1983        tr_runInEventThread( session, toggleDHTImpl, session );
1984}
1985
1986/***
1987****
1988***/
1989
1990bool
1991tr_sessionIsUTPEnabled( const tr_session * session )
1992{
1993    assert( tr_isSession( session ) );
1994
1995#ifdef WITH_UTP
1996    return session->isUTPEnabled;
1997#else
1998    return false;
1999#endif
2000}
2001
2002static void
2003toggle_utp(  void * data )
2004{
2005    tr_session * session = data;
2006    assert( tr_isSession( session ) );
2007
2008    session->isUTPEnabled = !session->isUTPEnabled;
2009
2010    tr_udpSetSocketBuffers( session );
2011
2012    /* But don't call tr_utpClose -- see reset_timer in tr-utp.c for an
2013       explanation. */
2014}
2015
2016void
2017tr_sessionSetUTPEnabled( tr_session * session, bool enabled )
2018{
2019    assert( tr_isSession( session ) );
2020    assert( tr_isBool( enabled ) );
2021
2022    if( ( enabled != 0 ) != ( session->isUTPEnabled != 0 ) )
2023        tr_runInEventThread( session, toggle_utp, session );
2024}
2025
2026/***
2027****
2028***/
2029
2030static void
2031toggleLPDImpl(  void * data )
2032{
2033    tr_session * session = data;
2034    assert( tr_isSession( session ) );
2035
2036    if( session->isLPDEnabled )
2037        tr_lpdUninit( session );
2038
2039    session->isLPDEnabled = !session->isLPDEnabled;
2040
2041    if( session->isLPDEnabled )
2042        tr_lpdInit( session, &session->public_ipv4->addr );
2043}
2044
2045void
2046tr_sessionSetLPDEnabled( tr_session * session, bool enabled )
2047{
2048    assert( tr_isSession( session ) );
2049    assert( tr_isBool( enabled ) );
2050
2051    if( ( enabled != 0 ) != ( session->isLPDEnabled != 0 ) )
2052        tr_runInEventThread( session, toggleLPDImpl, session );
2053}
2054
2055bool
2056tr_sessionIsLPDEnabled( const tr_session * session )
2057{
2058    assert( tr_isSession( session ) );
2059
2060    return session->isLPDEnabled;
2061}
2062
2063bool
2064tr_sessionAllowsLPD( const tr_session * session )
2065{
2066    return tr_sessionIsLPDEnabled( session );
2067}
2068
2069/***
2070****
2071***/
2072
2073void
2074tr_sessionSetCacheLimit_MB( tr_session * session, int max_bytes )
2075{
2076    assert( tr_isSession( session ) );
2077
2078    tr_cacheSetLimit( session->cache, toMemBytes( max_bytes ) );
2079}
2080
2081int
2082tr_sessionGetCacheLimit_MB( const tr_session * session )
2083{
2084    assert( tr_isSession( session ) );
2085
2086    return toMemMB( tr_cacheGetLimit( session->cache ) );
2087}
2088
2089/***
2090****
2091***/
2092
2093struct port_forwarding_data
2094{
2095    bool enabled;
2096    struct tr_shared * shared;
2097};
2098
2099static void
2100setPortForwardingEnabled( void * vdata )
2101{
2102    struct port_forwarding_data * data = vdata;
2103    tr_sharedTraversalEnable( data->shared, data->enabled );
2104    tr_free( data );
2105}
2106
2107void
2108tr_sessionSetPortForwardingEnabled( tr_session  * session, bool enabled )
2109{
2110    struct port_forwarding_data * d;
2111    d = tr_new0( struct port_forwarding_data, 1 );
2112    d->shared = session->shared;
2113    d->enabled = enabled;
2114    tr_runInEventThread( session, setPortForwardingEnabled, d );
2115}
2116
2117bool
2118tr_sessionIsPortForwardingEnabled( const tr_session * session )
2119{
2120    assert( tr_isSession( session ) );
2121
2122    return tr_sharedTraversalIsEnabled( session->shared );
2123}
2124
2125/***
2126****
2127***/
2128
2129static int
2130tr_stringEndsWith( const char * str, const char * end )
2131{
2132    const size_t slen = strlen( str );
2133    const size_t elen = strlen( end );
2134
2135    return slen >= elen && !memcmp( &str[slen - elen], end, elen );
2136}
2137
2138static void
2139loadBlocklists( tr_session * session )
2140{
2141    int         binCount = 0;
2142    int         newCount = 0;
2143    struct stat sb;
2144    char      * dirname;
2145    DIR *       odir = NULL;
2146    tr_list *   list = NULL;
2147    const bool  isEnabled = session->isBlocklistEnabled;
2148
2149    /* walk through the directory and find blocklists */
2150    dirname = tr_buildPath( session->configDir, "blocklists", NULL );
2151    if( !stat( dirname,
2152               &sb ) && S_ISDIR( sb.st_mode )
2153      && ( ( odir = opendir( dirname ) ) ) )
2154    {
2155        struct dirent *d;
2156        for( d = readdir( odir ); d; d = readdir( odir ) )
2157        {
2158            char * filename;
2159
2160            if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and ..
2161                                                      */
2162                continue;
2163
2164            filename = tr_buildPath( dirname, d->d_name, NULL );
2165
2166            if( tr_stringEndsWith( filename, ".bin" ) )
2167            {
2168                /* if we don't already have this blocklist, add it */
2169                if( !tr_list_find( list, filename,
2170                                   (TrListCompareFunc)strcmp ) )
2171                {
2172                    tr_list_append( &list,
2173                                   _tr_blocklistNew( filename, isEnabled ) );
2174                    ++binCount;
2175                }
2176            }
2177            else
2178            {
2179                /* strip out the file suffix, if there is one, and add ".bin"
2180                  instead */
2181                tr_blocklist * b;
2182                const char *   dot = strrchr( d->d_name, '.' );
2183                const int      len = dot ? dot - d->d_name
2184                                         : (int)strlen( d->d_name );
2185                char         * tmp = tr_strdup_printf(
2186                                        "%s" TR_PATH_DELIMITER_STR "%*.*s.bin",
2187                                        dirname, len, len, d->d_name );
2188                b = _tr_blocklistNew( tmp, isEnabled );
2189                _tr_blocklistSetContent( b, filename );
2190                tr_list_append( &list, b );
2191                ++newCount;
2192                tr_free( tmp );
2193            }
2194
2195            tr_free( filename );
2196        }
2197
2198        closedir( odir );
2199    }
2200
2201    session->blocklists = list;
2202
2203    if( binCount )
2204        tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
2205    if( newCount )
2206        tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
2207
2208    tr_free( dirname );
2209}
2210
2211static void
2212closeBlocklists( tr_session * session )
2213{
2214    tr_list_free( &session->blocklists,
2215                  (TrListForeachFunc)_tr_blocklistFree );
2216}
2217
2218void
2219tr_sessionReloadBlocklists( tr_session * session )
2220{
2221    closeBlocklists( session );
2222    loadBlocklists( session );
2223
2224    tr_peerMgrOnBlocklistChanged( session->peerMgr );
2225}
2226
2227int
2228tr_blocklistGetRuleCount( const tr_session * session )
2229{
2230    int       n = 0;
2231    tr_list * l;
2232
2233    assert( tr_isSession( session ) );
2234
2235    for( l = session->blocklists; l; l = l->next )
2236        n += _tr_blocklistGetRuleCount( l->data );
2237    return n;
2238}
2239
2240bool
2241tr_blocklistIsEnabled( const tr_session * session )
2242{
2243    assert( tr_isSession( session ) );
2244
2245    return session->isBlocklistEnabled;
2246}
2247
2248void
2249tr_blocklistSetEnabled( tr_session * session, bool isEnabled )
2250{
2251    tr_list * l;
2252
2253    assert( tr_isSession( session ) );
2254
2255    session->isBlocklistEnabled = isEnabled != 0;
2256
2257    for( l=session->blocklists; l!=NULL; l=l->next )
2258        _tr_blocklistSetEnabled( l->data, isEnabled );
2259}
2260
2261bool
2262tr_blocklistExists( const tr_session * session )
2263{
2264    assert( tr_isSession( session ) );
2265
2266    return session->blocklists != NULL;
2267}
2268
2269int
2270tr_blocklistSetContent( tr_session * session, const char * contentFilename )
2271{
2272    tr_list * l;
2273    int ruleCount;
2274    tr_blocklist * b;
2275    const char * defaultName = DEFAULT_BLOCKLIST_FILENAME;
2276    tr_sessionLock( session );
2277
2278    for( b = NULL, l = session->blocklists; !b && l; l = l->next )
2279        if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ),
2280                               defaultName ) )
2281            b = l->data;
2282
2283    if( !b )
2284    {
2285        char * path = tr_buildPath( session->configDir, "blocklists", defaultName, NULL );
2286        b = _tr_blocklistNew( path, session->isBlocklistEnabled );
2287        tr_list_append( &session->blocklists, b );
2288        tr_free( path );
2289    }
2290
2291    ruleCount = _tr_blocklistSetContent( b, contentFilename );
2292    tr_sessionUnlock( session );
2293    return ruleCount;
2294}
2295
2296bool
2297tr_sessionIsAddressBlocked( const tr_session * session,
2298                            const tr_address * addr )
2299{
2300    tr_list * l;
2301
2302    assert( tr_isSession( session ) );
2303
2304    for( l = session->blocklists; l; l = l->next )
2305        if( _tr_blocklistHasAddress( l->data, addr ) )
2306            return true;
2307    return false;
2308}
2309
2310void
2311tr_blocklistSetURL( tr_session * session, const char * url )
2312{
2313    if( session->blocklist_url != url )
2314    {
2315        tr_free( session->blocklist_url );
2316        session->blocklist_url = tr_strdup( url );
2317    }
2318}
2319
2320const char *
2321tr_blocklistGetURL ( const tr_session * session )
2322{
2323    return session->blocklist_url;
2324}
2325
2326
2327/***
2328****
2329***/
2330
2331static void
2332metainfoLookupInit( tr_session * session )
2333{
2334    struct stat  sb;
2335    const char * dirname = tr_getTorrentDir( session );
2336    DIR *        odir = NULL;
2337    tr_ctor *    ctor = NULL;
2338    tr_benc * lookup;
2339    int n = 0;
2340
2341    assert( tr_isSession( session ) );
2342
2343    /* walk through the directory and find the mappings */
2344    lookup = tr_new0( tr_benc, 1 );
2345    tr_bencInitDict( lookup, 0 );
2346    ctor = tr_ctorNew( session );
2347    tr_ctorSetSave( ctor, false ); /* since we already have them */
2348    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
2349    {
2350        struct dirent *d;
2351        while(( d = readdir( odir )))
2352        {
2353            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
2354            {
2355                tr_info inf;
2356                char * path = tr_buildPath( dirname, d->d_name, NULL );
2357                tr_ctorSetMetainfoFromFile( ctor, path );
2358                if( !tr_torrentParse( ctor, &inf ) )
2359                {
2360                    ++n;
2361                    tr_bencDictAddStr( lookup, inf.hashString, path );
2362                }
2363                tr_free( path );
2364            }
2365        }
2366        closedir( odir );
2367    }
2368    tr_ctorFree( ctor );
2369
2370    session->metainfoLookup = lookup;
2371    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
2372}
2373
2374const char*
2375tr_sessionFindTorrentFile( const tr_session * session,
2376                           const char       * hashString )
2377{
2378    const char * filename = NULL;
2379    if( !session->metainfoLookup )
2380        metainfoLookupInit( (tr_session*)session );
2381    tr_bencDictFindStr( session->metainfoLookup, hashString, &filename );
2382    return filename;
2383}
2384
2385void
2386tr_sessionSetTorrentFile( tr_session * session,
2387                          const char * hashString,
2388                          const char * filename )
2389{
2390    /* since we walk session->configDir/torrents/ to build the lookup table,
2391     * and tr_sessionSetTorrentFile() is just to tell us there's a new file
2392     * in that same directory, we don't need to do anything here if the
2393     * lookup table hasn't been built yet */
2394    if( session->metainfoLookup )
2395        tr_bencDictAddStr( session->metainfoLookup, hashString, filename );
2396}
2397
2398/***
2399****
2400***/
2401
2402void
2403tr_sessionSetRPCEnabled( tr_session * session, bool isEnabled )
2404{
2405    assert( tr_isSession( session ) );
2406
2407    tr_rpcSetEnabled( session->rpcServer, isEnabled );
2408}
2409
2410bool
2411tr_sessionIsRPCEnabled( const tr_session * session )
2412{
2413    assert( tr_isSession( session ) );
2414
2415    return tr_rpcIsEnabled( session->rpcServer );
2416}
2417
2418void
2419tr_sessionSetRPCPort( tr_session * session,
2420                      tr_port      port )
2421{
2422    assert( tr_isSession( session ) );
2423
2424    tr_rpcSetPort( session->rpcServer, port );
2425}
2426
2427tr_port
2428tr_sessionGetRPCPort( const tr_session * session )
2429{
2430    assert( tr_isSession( session ) );
2431
2432    return tr_rpcGetPort( session->rpcServer );
2433}
2434
2435void
2436tr_sessionSetRPCUrl( tr_session * session,
2437                     const char * url )
2438{
2439    assert( tr_isSession( session ) );
2440
2441    tr_rpcSetUrl( session->rpcServer, url );
2442}
2443
2444const char*
2445tr_sessionGetRPCUrl( const tr_session * session )
2446{
2447    assert( tr_isSession( session ) );
2448
2449    return tr_rpcGetUrl( session->rpcServer );
2450}
2451
2452void
2453tr_sessionSetRPCCallback( tr_session * session,
2454                          tr_rpc_func  func,
2455                          void *       user_data )
2456{
2457    assert( tr_isSession( session ) );
2458
2459    session->rpc_func = func;
2460    session->rpc_func_user_data = user_data;
2461}
2462
2463void
2464tr_sessionSetRPCWhitelist( tr_session * session,
2465                           const char * whitelist )
2466{
2467    assert( tr_isSession( session ) );
2468
2469    tr_rpcSetWhitelist( session->rpcServer, whitelist );
2470}
2471
2472const char*
2473tr_sessionGetRPCWhitelist( const tr_session * session )
2474{
2475    assert( tr_isSession( session ) );
2476
2477    return tr_rpcGetWhitelist( session->rpcServer );
2478}
2479
2480void
2481tr_sessionSetRPCWhitelistEnabled( tr_session * session, bool isEnabled )
2482{
2483    assert( tr_isSession( session ) );
2484
2485    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
2486}
2487
2488bool
2489tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
2490{
2491    assert( tr_isSession( session ) );
2492
2493    return tr_rpcGetWhitelistEnabled( session->rpcServer );
2494}
2495
2496
2497void
2498tr_sessionSetRPCPassword( tr_session * session,
2499                          const char * password )
2500{
2501    assert( tr_isSession( session ) );
2502
2503    tr_rpcSetPassword( session->rpcServer, password );
2504}
2505
2506const char*
2507tr_sessionGetRPCPassword( const tr_session * session )
2508{
2509    assert( tr_isSession( session ) );
2510
2511    return tr_rpcGetPassword( session->rpcServer );
2512}
2513
2514void
2515tr_sessionSetRPCUsername( tr_session * session,
2516                          const char * username )
2517{
2518    assert( tr_isSession( session ) );
2519
2520    tr_rpcSetUsername( session->rpcServer, username );
2521}
2522
2523const char*
2524tr_sessionGetRPCUsername( const tr_session * session )
2525{
2526    assert( tr_isSession( session ) );
2527
2528    return tr_rpcGetUsername( session->rpcServer );
2529}
2530
2531void
2532tr_sessionSetRPCPasswordEnabled( tr_session * session, bool isEnabled )
2533{
2534    assert( tr_isSession( session ) );
2535
2536    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
2537}
2538
2539bool
2540tr_sessionIsRPCPasswordEnabled( const tr_session * session )
2541{
2542    assert( tr_isSession( session ) );
2543
2544    return tr_rpcIsPasswordEnabled( session->rpcServer );
2545}
2546
2547const char *
2548tr_sessionGetRPCBindAddress( const tr_session * session )
2549{
2550    assert( tr_isSession( session ) );
2551
2552    return tr_rpcGetBindAddress( session->rpcServer );
2553}
2554
2555/****
2556*****
2557****/
2558
2559bool
2560tr_sessionIsTorrentDoneScriptEnabled( const tr_session * session )
2561{
2562    assert( tr_isSession( session ) );
2563
2564    return session->isTorrentDoneScriptEnabled;
2565}
2566
2567void
2568tr_sessionSetTorrentDoneScriptEnabled( tr_session * session, bool isEnabled )
2569{
2570    assert( tr_isSession( session ) );
2571    assert( tr_isBool( isEnabled ) );
2572
2573    session->isTorrentDoneScriptEnabled = isEnabled;
2574}
2575
2576const char *
2577tr_sessionGetTorrentDoneScript( const tr_session * session )
2578{
2579    assert( tr_isSession( session ) );
2580
2581    return session->torrentDoneScript;
2582}
2583
2584void
2585tr_sessionSetTorrentDoneScript( tr_session * session, const char * scriptFilename )
2586{
2587    assert( tr_isSession( session ) );
2588
2589    if( session->torrentDoneScript != scriptFilename )
2590    {
2591        tr_free( session->torrentDoneScript );
2592        session->torrentDoneScript = tr_strdup( scriptFilename );
2593    }
2594}
2595
2596/***
2597****
2598***/
2599
2600void
2601tr_sessionSetWebConfigFunc( tr_session * session, void (*func)(tr_session*, void*, const char* ) )
2602{
2603    session->curl_easy_config_func = func;
2604}
Note: See TracBrowser for help on using the repository browser.