source: trunk/libtransmission/session.c @ 12184

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

(trunk libT) better shutdown management of libutp and UDP trackers in tr_sessionClose().

This is a little overlapping since the utp code can be closed more-or-less immediately, but the udp manager needs to stay open in order to process the udp tracker connection requests before sending out event=stopped. Moreover DNS resolver can be shut down after the UDP tracker is shutdown.

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