source: trunk/libtransmission/session.c @ 8130

Last change on this file since 8130 was 8130, checked in by livings124, 13 years ago

libT support for #1679 Schedule Speed Limit mode based on days, not just time

  • Property svn:keywords set to Date Rev Author Id
File size: 56.3 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.com>
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 8130 2009-04-04 02:17:39Z livings124 $
11 */
12
13#include <assert.h>
14#include <stdlib.h>
15#include <string.h> /* memcpy */
16
17#include <signal.h>
18#include <sys/types.h> /* stat */
19#include <sys/stat.h> /* stat */
20#include <unistd.h> /* stat */
21#include <dirent.h> /* opendir */
22
23#include <event.h>
24
25#include "transmission.h"
26#include "session.h"
27#include "bandwidth.h"
28#include "bencode.h"
29#include "blocklist.h"
30#include "fdlimit.h"
31#include "list.h"
32#include "metainfo.h" /* tr_metainfoFree */
33#include "net.h"
34#include "peer-mgr.h"
35#include "platform.h" /* tr_lock */
36#include "port-forwarding.h"
37#include "rpc-server.h"
38#include "stats.h"
39#include "torrent.h"
40#include "tracker.h"
41#include "trevent.h"
42#include "utils.h"
43#include "web.h"
44#include "crypto.h"
45
46#define dbgmsg( ... ) \
47    do { \
48        if( tr_deepLoggingIsActive( ) ) \
49            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
50    } while( 0 )
51
52static tr_port
53getRandomPort( tr_session * s )
54{
55    return tr_cryptoWeakRandInt( s->randomPortHigh - s->randomPortLow + 1) + s->randomPortLow;
56}
57
58/* Generate a peer id : "-TRxyzb-" + 12 random alphanumeric
59   characters, where x is the major version number, y is the
60   minor version number, z is the maintenance number, and b
61   designates beta (Azureus-style) */
62uint8_t*
63tr_peerIdNew( void )
64{
65    int          i;
66    int          val;
67    int          total = 0;
68    uint8_t *    buf = tr_new( uint8_t, 21 );
69    const char * pool = "0123456789abcdefghijklmnopqrstuvwxyz";
70    const int    base = 36;
71
72    memcpy( buf, PEERID_PREFIX, 8 );
73
74    for( i = 8; i < 19; ++i )
75    {
76        val = tr_cryptoRandInt( base );
77        total += val;
78        buf[i] = pool[val];
79    }
80
81    val = total % base ? base - ( total % base ) : 0;
82    buf[19] = pool[val];
83    buf[20] = '\0';
84
85    return buf;
86}
87
88const uint8_t*
89tr_getPeerId( void )
90{
91    static uint8_t * id = NULL;
92
93    if( id == NULL )
94        id = tr_peerIdNew( );
95    return id;
96}
97
98/***
99****
100***/
101
102tr_encryption_mode
103tr_sessionGetEncryption( tr_session * session )
104{
105    assert( session );
106
107    return session->encryptionMode;
108}
109
110void
111tr_sessionSetEncryption( tr_session *       session,
112                         tr_encryption_mode mode )
113{
114    assert( session );
115    assert( mode == TR_ENCRYPTION_PREFERRED
116          || mode == TR_ENCRYPTION_REQUIRED
117          || mode == TR_CLEAR_PREFERRED );
118
119    session->encryptionMode = mode;
120}
121
122/***
123****
124***/
125
126static int
127tr_stringEndsWith( const char * str,
128                   const char * end )
129{
130    const size_t slen = strlen( str );
131    const size_t elen = strlen( end );
132
133    return slen >= elen && !memcmp( &str[slen - elen], end, elen );
134}
135
136static void
137loadBlocklists( tr_session * session )
138{
139    int         binCount = 0;
140    int         newCount = 0;
141    struct stat sb;
142    char      * dirname;
143    DIR *       odir = NULL;
144    tr_list *   list = NULL;
145    const tr_bool   isEnabled = session->isBlocklistEnabled;
146
147    /* walk through the directory and find blocklists */
148    dirname = tr_buildPath( session->configDir, "blocklists", NULL );
149    if( !stat( dirname,
150               &sb ) && S_ISDIR( sb.st_mode )
151      && ( ( odir = opendir( dirname ) ) ) )
152    {
153        struct dirent *d;
154        for( d = readdir( odir ); d; d = readdir( odir ) )
155        {
156            char * filename;
157
158            if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and ..
159                                                      */
160                continue;
161
162            filename = tr_buildPath( dirname, d->d_name, NULL );
163
164            if( tr_stringEndsWith( filename, ".bin" ) )
165            {
166                /* if we don't already have this blocklist, add it */
167                if( !tr_list_find( list, filename,
168                                   (TrListCompareFunc)strcmp ) )
169                {
170                    tr_list_append( &list,
171                                   _tr_blocklistNew( filename, isEnabled ) );
172                    ++binCount;
173                }
174            }
175            else
176            {
177                /* strip out the file suffix, if there is one, and add ".bin"
178                  instead */
179                tr_blocklist * b;
180                const char *   dot = strrchr( d->d_name, '.' );
181                const int      len = dot ? dot - d->d_name
182                                         : (int)strlen( d->d_name );
183                char         * tmp = tr_strdup_printf(
184                                        "%s" TR_PATH_DELIMITER_STR "%*.*s.bin",
185                                        dirname, len, len, d->d_name );
186                b = _tr_blocklistNew( tmp, isEnabled );
187                _tr_blocklistSetContent( b, filename );
188                tr_list_append( &list, b );
189                ++newCount;
190                tr_free( tmp );
191            }
192
193            tr_free( filename );
194        }
195
196        closedir( odir );
197    }
198
199    session->blocklists = list;
200
201    if( binCount )
202        tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
203    if( newCount )
204        tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
205
206    tr_free( dirname );
207}
208
209static tr_bool
210isAltTime( const tr_session * s )
211{
212    int minutes, day;
213    tr_bool withinTime;
214    struct tm tm;
215    const time_t now = time( NULL );
216    const int begin = s->altSpeedTimeBegin;
217    const int end = s->altSpeedTimeEnd;
218    const tr_bool toNextDay = begin > end;
219
220    tr_localtime_r( &now, &tm );
221    minutes = tm.tm_hour*60 + tm.tm_min;
222    day = tm.tm_wday;
223   
224    if( !toNextDay )
225        withinTime = ( begin <= minutes ) && ( minutes < end );
226    else /* goes past midnight */
227        withinTime = ( begin <= minutes ) || ( minutes < end );
228   
229    if( !withinTime )
230        return FALSE;
231   
232    if( toNextDay && (minutes < end) )
233        day = (day - 1) % 7;
234   
235    if( s->altSpeedTimeDay == TR_SCHED_ALL )
236        return TRUE;
237    else if( s->altSpeedTimeDay == TR_SCHED_WEEKDAY )
238        return (day != 0) && day != 6;
239    else if( s->altSpeedTimeDay == TR_SCHED_WEEKEND )
240        return (day == 0) || (day == 6);
241    else
242        return day == s->altSpeedTimeDay;
243}
244
245/***
246****
247***/
248
249#ifdef TR_EMBEDDED
250 #define TR_DEFAULT_ENCRYPTION   TR_CLEAR_PREFERRED
251#else
252 #define TR_DEFAULT_ENCRYPTION   TR_ENCRYPTION_PREFERRED
253#endif
254
255void
256tr_sessionGetDefaultSettings( tr_benc * d )
257{
258    assert( tr_bencIsDict( d ) );
259
260    tr_bencDictReserve( d, 35 );
261    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        FALSE );
262    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR,             tr_getDefaultDownloadDir( ) );
263    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED,                   100 );
264    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED,           FALSE );
265    tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION,               TR_DEFAULT_ENCRYPTION );
266    tr_bencDictAddBool( d, TR_PREFS_KEY_LAZY_BITFIELD,            TRUE );
267    tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL,                 TR_MSG_INF );
268    tr_bencDictAddInt ( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          atoi( TR_DEFAULT_OPEN_FILE_LIMIT_STR ) );
269    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        atoi( TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ) );
270    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       atoi( TR_DEFAULT_PEER_LIMIT_TORRENT_STR ) );
271    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT,                atoi( TR_DEFAULT_PEER_PORT_STR ) );
272    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, FALSE );
273    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     1024 );
274    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    65535 );
275    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          atoi( TR_DEFAULT_PEER_SOCKET_TOS_STR ) );
276    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              TRUE );
277    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          TRUE );
278    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            TR_PREALLOCATE_SPARSE );
279    tr_bencDictAddStr ( d, TR_PREFS_KEY_PROXY,                    "" );
280    tr_bencDictAddBool( d, TR_PREFS_KEY_PROXY_AUTH_ENABLED,       FALSE );
281    tr_bencDictAddBool( d, TR_PREFS_KEY_PROXY_ENABLED,            FALSE );
282    tr_bencDictAddStr ( d, TR_PREFS_KEY_PROXY_PASSWORD,           "" );
283    tr_bencDictAddInt ( d, TR_PREFS_KEY_PROXY_PORT,               80 );
284    tr_bencDictAddInt ( d, TR_PREFS_KEY_PROXY_TYPE,               TR_PROXY_HTTP );
285    tr_bencDictAddStr ( d, TR_PREFS_KEY_PROXY_USERNAME,           "" );
286    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    2.0 );
287    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            FALSE );
288    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        FALSE );
289    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED,              TRUE );
290    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD,             "" );
291    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME,             "" );
292    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST,            TR_DEFAULT_RPC_WHITELIST );
293    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    TRUE );
294    tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT,                 atoi( TR_DEFAULT_RPC_PORT_STR ) );
295    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED,        FALSE );
296    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP,             50 ); /* half the regular */
297    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN,           50 ); /* half the regular */
298    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN,     540 ); /* 9am */
299    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED,   FALSE );
300    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,       1020 ); /* 5pm */
301    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,       TR_SCHED_ALL );
302    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED,                   100 );
303    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED,           FALSE );
304    tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, 14 );
305}
306
307void
308tr_sessionGetSettings( tr_session * s, struct tr_benc * d )
309{
310    int i, n=0;
311    char * freeme[16];
312
313    assert( tr_bencIsDict( d ) );
314
315    tr_bencDictReserve( d, 30 );
316    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        tr_blocklistIsEnabled( s ) );
317    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR,             s->downloadDir );
318    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED,                   tr_sessionGetSpeedLimit( s, TR_DOWN ) );
319    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED,           tr_sessionIsSpeedLimited( s, TR_DOWN ) );
320    tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION,               s->encryptionMode );
321    tr_bencDictAddBool( d, TR_PREFS_KEY_LAZY_BITFIELD,            s->useLazyBitfield );
322    tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL,                 tr_getMessageLevel( ) );
323    tr_bencDictAddInt ( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          s->openFileLimit );
324    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        tr_sessionGetPeerLimit( s ) );
325    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       s->peerLimitPerTorrent );
326    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT,                tr_sessionGetPeerPort( s ) );
327    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, s->isPortRandom );
328    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     s->randomPortLow );
329    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    s->randomPortHigh );
330    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          s->peerSocketTOS );
331    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              s->isPexEnabled );
332    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          tr_sessionIsPortForwardingEnabled( s ) );
333    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            s->preallocationMode );
334    tr_bencDictAddStr ( d, TR_PREFS_KEY_PROXY,                    s->proxy );
335    tr_bencDictAddBool( d, TR_PREFS_KEY_PROXY_AUTH_ENABLED,       s->isProxyAuthEnabled );
336    tr_bencDictAddBool( d, TR_PREFS_KEY_PROXY_ENABLED,            s->isProxyEnabled );
337    tr_bencDictAddStr ( d, TR_PREFS_KEY_PROXY_PASSWORD,           s->proxyPassword );
338    tr_bencDictAddInt ( d, TR_PREFS_KEY_PROXY_PORT,               s->proxyPort );
339    tr_bencDictAddInt ( d, TR_PREFS_KEY_PROXY_TYPE,               s->proxyType );
340    tr_bencDictAddStr ( d, TR_PREFS_KEY_PROXY_USERNAME,           s->proxyUsername );
341    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    s->desiredRatio );
342    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            s->isRatioLimited );
343    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        tr_sessionIsRPCPasswordEnabled( s ) );
344    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED,              tr_sessionIsRPCEnabled( s ) );
345    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD,             freeme[n++] = tr_sessionGetRPCPassword( s ) );
346    tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT,                 tr_sessionGetRPCPort( s ) );
347    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME,             freeme[n++] = tr_sessionGetRPCUsername( s ) );
348    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST,            freeme[n++] = tr_sessionGetRPCWhitelist( s ) );
349    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    tr_sessionGetRPCWhitelistEnabled( s ) );
350    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED,        tr_sessionUsesAltSpeed( s ) );
351    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP,             tr_sessionGetAltSpeed( s, TR_UP ) );
352    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN,           tr_sessionGetAltSpeed( s, TR_DOWN ) );
353    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN,     tr_sessionGetAltSpeedBegin( s ) );
354    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED,   tr_sessionUsesAltSpeedTime( s ) );
355    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,       tr_sessionGetAltSpeedEnd( s ) );
356    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,       tr_sessionGetAltSpeedDay( s ) );
357    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED,                   tr_sessionGetSpeedLimit( s, TR_UP ) );
358    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED,           tr_sessionIsSpeedLimited( s, TR_UP ) );
359    tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, s->uploadSlotsPerTorrent );
360
361    for( i=0; i<n; ++i )
362        tr_free( freeme[i] );
363}
364
365void
366tr_sessionLoadSettings( tr_benc * d, const char * configDir, const char * appName )
367{
368    char * filename;
369    tr_benc fileSettings;
370    tr_benc sessionDefaults;
371    tr_benc tmp;
372
373    assert( tr_bencIsDict( d ) );
374
375    /* initializing the defaults: caller may have passed in some app-level defaults.
376     * preserve those and use the session defaults to fill in any missing gaps. */
377    tr_bencInitDict( &sessionDefaults, 0 );
378    tr_sessionGetDefaultSettings( &sessionDefaults );
379    tr_bencMergeDicts( &sessionDefaults, d );
380    tmp = *d; *d = sessionDefaults; sessionDefaults = tmp;
381
382    /* if caller didn't specify a config dir, use the default */
383    if( !configDir || !*configDir )
384        configDir = tr_getDefaultConfigDir( appName );
385
386    /* file settings override the defaults */
387    filename = tr_buildPath( configDir, "settings.json", NULL );
388    if( !tr_bencLoadJSONFile( filename, &fileSettings ) ) {
389        tr_bencMergeDicts( d, &fileSettings );
390        tr_bencFree( &fileSettings );
391    }
392
393    /* cleanup */
394    tr_bencFree( &sessionDefaults );
395    tr_free( filename );
396}
397
398void
399tr_sessionSaveSettings( tr_session    * session,
400                        const char    * configDir,
401                        const tr_benc * clientSettings )
402{
403    tr_benc settings;
404    char * filename = tr_buildPath( configDir, "settings.json", NULL );
405
406    assert( tr_bencIsDict( clientSettings ) );
407
408    tr_bencInitDict( &settings, 0 );
409
410    /* the existing file settings are the fallback values */
411    {
412        tr_benc fileSettings;
413        if( !tr_bencLoadJSONFile( filename, &fileSettings ) )
414        {
415            tr_bencMergeDicts( &settings, &fileSettings );
416            tr_bencFree( &fileSettings );
417        }
418    }
419
420    /* the client's settings override the file settings */
421    tr_bencMergeDicts( &settings, clientSettings );
422
423    /* the session's true values override the file & client settings */
424    {
425        tr_benc sessionSettings;
426        tr_bencInitDict( &sessionSettings, 0 );
427        tr_sessionGetSettings( session, &sessionSettings );
428        tr_bencMergeDicts( &settings, &sessionSettings );
429        tr_bencFree( &sessionSettings );
430    }
431
432    /* save the result */
433    tr_bencSaveJSONFile( filename, &settings );
434    tr_inf( "Saved \"%s\"", filename );
435
436    /* cleanup */
437    tr_free( filename );
438    tr_bencFree( &settings );
439}
440
441static void metainfoLookupRescan( tr_session * );
442static void tr_sessionInitImpl( void * );
443static void onAltTimer( int, short, void* );
444static void setAltTimer( tr_session * session );
445
446struct init_data
447{
448    tr_session  * session;
449    const char  * configDir;
450    tr_bool       messageQueuingEnabled;
451    tr_benc     * clientSettings;
452};
453
454tr_session *
455tr_sessionInit( const char  * tag,
456                const char  * configDir,
457                tr_bool       messageQueuingEnabled,
458                tr_benc     * clientSettings )
459{
460    tr_session * session;
461    struct init_data data;
462
463    assert( tr_bencIsDict( clientSettings ) );
464
465    /* initialize the bare skeleton of the session object */
466    session = tr_new0( tr_session, 1 );
467    session->bandwidth = tr_bandwidthNew( session, NULL );
468    session->lock = tr_lockNew( );
469    session->tag = tr_strdup( tag );
470    session->magicNumber = SESSION_MAGIC_NUMBER;
471
472    /* start the libtransmission thread */
473    tr_netInit( ); /* must go before tr_eventInit */
474    tr_eventInit( session );
475    assert( session->events != NULL );
476
477    /* run the rest in the libtransmission thread */
478    session->isWaiting = TRUE;
479    data.session = session;
480    data.configDir = configDir;
481    data.messageQueuingEnabled = messageQueuingEnabled;
482    data.clientSettings = clientSettings;
483    tr_runInEventThread( session, tr_sessionInitImpl, &data );
484    while( session->isWaiting )
485        tr_wait( 100 );
486
487    return session;
488}
489
490static void useAltSpeed( tr_session * session, tr_bool enabled, tr_bool byUser );
491static void useAltSpeedTime( tr_session * session, tr_bool enabled, tr_bool byUser );
492
493static void
494tr_sessionInitImpl( void * vdata )
495{
496    int64_t i;
497    int64_t j;
498    double  d;
499    tr_bool found;
500    tr_bool boolVal;
501    const char * str;
502    tr_benc settings;
503    char * filename;
504    struct init_data * data = vdata;
505    tr_benc * clientSettings = data->clientSettings;
506    tr_session * session = data->session;
507
508    assert( tr_amInEventThread( session ) );
509    assert( tr_bencIsDict( clientSettings ) );
510
511    dbgmsg( "tr_sessionInit: the session's top-level bandwidth object is %p", session->bandwidth );
512
513    tr_bencInitDict( &settings, 0 );
514    tr_sessionGetDefaultSettings( &settings );
515    tr_bencMergeDicts( &settings, clientSettings );
516
517#ifndef WIN32
518    /* Don't exit when writing on a broken socket */
519    signal( SIGPIPE, SIG_IGN );
520#endif
521
522    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i );
523    assert( found );
524    session->peerLimitPerTorrent = i;
525
526    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_MSGLEVEL, &i );
527    assert( found );
528    tr_setMessageLevel( i );
529    tr_setMessageQueuing( data->messageQueuingEnabled );
530
531
532    found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_PEX_ENABLED, &boolVal );
533    assert( found );
534    session->isPexEnabled = boolVal;
535
536    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ENCRYPTION, &i );
537    assert( found );
538    assert( tr_isEncryptionMode( i ) );
539    session->encryptionMode = i;
540
541    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PREALLOCATION, &i );
542    assert( found );
543    assert( tr_isPreallocationMode( i ) );
544    session->preallocationMode = i;
545
546    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_SOCKET_TOS, &i );
547    assert( found );
548    session->peerSocketTOS = i;
549
550    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, &str );
551    assert( found );
552    session->downloadDir = tr_strdup( str );
553
554    found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_PROXY_ENABLED, &boolVal );
555    assert( found );
556    session->isProxyEnabled = boolVal;
557
558    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY, &str );
559    assert( found );
560    session->proxy = tr_strdup( str );
561
562    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_PORT, &i );
563    assert( found );
564    session->proxyPort = i;
565
566    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_TYPE, &i );
567    assert( found );
568    session->proxyType = i;
569
570    found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_PROXY_AUTH_ENABLED, &boolVal );
571    assert( found );
572    session->isProxyAuthEnabled = boolVal;
573
574    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_USERNAME, &str );
575    assert( found );
576    session->proxyUsername = tr_strdup( str );
577
578    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_PASSWORD, &str );
579    assert( found );
580    session->proxyPassword = tr_strdup( str );
581
582    session->so_sndbuf = 1500 * 3; /* 3x MTU for most ethernet/wireless */
583    session->so_rcvbuf = 8192;
584
585    tr_setConfigDir( session, data->configDir );
586
587    tr_trackerSessionInit( session );
588    assert( session->tracker != NULL );
589
590    session->peerMgr = tr_peerMgrNew( session );
591
592    found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_LAZY_BITFIELD, &boolVal );
593    assert( found );
594    session->useLazyBitfield = boolVal;
595
596    /* Initialize rate and file descripts controls */
597
598    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_OPEN_FILE_LIMIT, &i );
599    assert( found );
600    session->openFileLimit = i;
601    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &j );
602    assert( found );
603    tr_fdInit( session->openFileLimit, j );
604
605    /**
606    *** random port
607    **/
608
609    found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal );
610    assert( found );
611    session->isPortRandom = boolVal;
612
613    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, &i );
614    assert( found );
615    session->randomPortLow = i;
616
617    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, &i );
618    assert( found );
619    session->randomPortHigh = i;
620
621    found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, &boolVal )
622         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT, &j );
623    assert( found );
624    session->peerPort = session->isPortRandom ? getRandomPort( session ) : j;
625    session->shared = tr_sharedInit( session, boolVal, session->peerPort );
626    session->isPortSet = session->peerPort > 0;
627
628    /**
629    **/
630
631    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, &i );
632    assert( found );
633    session->uploadSlotsPerTorrent = i;
634
635    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_USPEED, &i )
636         && tr_bencDictFindBool( &settings, TR_PREFS_KEY_USPEED_ENABLED, &boolVal );
637    assert( found );
638    tr_sessionSetSpeedLimit( session, TR_UP, i );
639    tr_sessionLimitSpeed( session, TR_UP, boolVal );
640
641    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_DSPEED, &i )
642         && tr_bencDictFindBool( &settings, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal );
643    assert( found );
644    tr_sessionSetSpeedLimit( session, TR_DOWN, i );
645    tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
646
647    found = tr_bencDictFindReal( &settings, TR_PREFS_KEY_RATIO, &d )
648         && tr_bencDictFindBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, &boolVal );
649    assert( found );
650    tr_sessionSetRatioLimit( session, d );
651    tr_sessionSetRatioLimited( session, boolVal );
652
653    /**
654    ***  Alternate speed limits
655    **/
656
657    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ALT_SPEED_UP, &i );
658    assert( found );
659    session->altSpeed[TR_UP] = i;
660
661    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ALT_SPEED_DOWN, &i );
662    assert( found );
663    session->altSpeed[TR_DOWN] = i;
664
665    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i );
666    assert( found );
667    session->altSpeedTimeBegin = i;
668
669    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i );
670    assert( found );
671    session->altSpeedTimeEnd = i;
672   
673    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i );
674    assert( found );
675    session->altSpeedTimeDay = i;
676
677    found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal );
678    assert( found );
679    useAltSpeedTime( session, boolVal, FALSE );
680
681    if( !boolVal )
682    {
683        found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal );
684        assert( found );
685        useAltSpeed( session, boolVal, FALSE );
686    }
687    else
688        useAltSpeed( session, isAltTime( session ), FALSE );
689
690    /**
691    ***  Blocklist
692    **/
693
694    filename = tr_buildPath( session->configDir, "blocklists", NULL );
695    tr_mkdirp( filename, 0777 );
696    tr_free( filename );
697    found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal );
698    assert( found );
699    session->isBlocklistEnabled = boolVal;
700    loadBlocklists( session );
701
702    session->rpcServer = tr_rpcInit( session, &settings );
703
704    tr_bencFree( &settings );
705
706    assert( tr_isSession( session ) );
707
708    session->altTimer = tr_new0( struct event, 1 );
709    evtimer_set( session->altTimer, onAltTimer, session );
710    setAltTimer( session );
711
712    /* first %s is the application name
713       second %s is the version number */
714    tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING );
715
716    tr_statsInit( session );
717    session->web = tr_webInit( session );
718    metainfoLookupRescan( session );
719    session->isWaiting = FALSE;
720    dbgmsg( "returning session %p; session->tracker is %p", session, session->tracker );
721}
722
723/***
724****
725***/
726
727void
728tr_sessionSetDownloadDir( tr_session * session, const char * dir )
729{
730    assert( tr_isSession( session ) );
731
732    if( session->downloadDir != dir )
733    {
734        tr_free( session->downloadDir );
735        session->downloadDir = tr_strdup( dir );
736    }
737}
738
739const char *
740tr_sessionGetDownloadDir( const tr_session * session )
741{
742    assert( tr_isSession( session ) );
743
744    return session->downloadDir;
745}
746
747/***
748****
749***/
750
751void
752tr_globalLock( tr_session * session )
753{
754    assert( tr_isSession( session ) );
755
756    tr_lockLock( session->lock );
757}
758
759void
760tr_globalUnlock( tr_session * session )
761{
762    assert( tr_isSession( session ) );
763
764    tr_lockUnlock( session->lock );
765}
766
767tr_bool
768tr_globalIsLocked( const tr_session * session )
769{
770    return tr_isSession( session ) && tr_lockHave( session->lock );
771}
772
773/***********************************************************************
774 * tr_setBindPort
775 ***********************************************************************
776 *
777 **********************************************************************/
778
779struct bind_port_data
780{
781    tr_session * session;
782    tr_port      port;
783};
784
785static void
786tr_setBindPortImpl( void * vdata )
787{
788    struct bind_port_data * data = vdata;
789    tr_session * session = data->session;
790    const tr_port port = data->port;
791
792    session->isPortSet = 1;
793    tr_sharedSetPort( session->shared, port );
794
795    tr_free( data );
796}
797
798static void
799setPortImpl( tr_session * session, tr_port port )
800{
801    struct bind_port_data * data;
802
803    assert( tr_isSession( session ) );
804
805    data = tr_new( struct bind_port_data, 1 );
806    data->session = session;
807    data->port = port;
808    tr_runInEventThread( session, tr_setBindPortImpl, data );
809}
810
811void
812tr_sessionSetPeerPort( tr_session * session,
813                       tr_port      port )
814{
815    assert( tr_isSession( session ) );
816
817    session->peerPort = port;
818    setPortImpl( session, session->peerPort );
819}
820
821tr_port
822tr_sessionGetPeerPort( const tr_session * session )
823{
824    assert( tr_isSession( session ) );
825
826    return session->peerPort;
827}
828
829tr_port
830tr_sessionSetPeerPortRandom( tr_session * session )
831{
832    assert( tr_isSession( session ) );
833
834    session->peerPort = getRandomPort( session );
835    setPortImpl( session, session->peerPort );
836    return session->peerPort;
837}
838
839void
840tr_sessionSetPeerPortRandomOnStart( tr_session * session,
841                                    tr_bool random )
842{
843    assert( tr_isSession( session ) );
844
845    session->isPortRandom = random;
846}
847
848tr_bool
849tr_sessionGetPeerPortRandomOnStart( tr_session * session )
850{
851    assert( tr_isSession( session ) );
852
853    return session->isPortRandom;
854}
855
856tr_port_forwarding
857tr_sessionGetPortForwarding( const tr_session * session )
858{
859    assert( tr_isSession( session ) );
860
861    return tr_sharedTraversalStatus( session->shared );
862}
863
864/***
865****
866***/
867
868static void
869updateSeedRatio( tr_session * session )
870{
871    tr_torrent * tor = NULL;
872
873    while(( tor = tr_torrentNext( session, tor )))
874        tr_torrentCheckSeedRatio( tor );
875}
876
877void
878tr_sessionSetRatioLimited( tr_session * session, tr_bool isLimited )
879{
880    assert( tr_isSession( session ) );
881
882    session->isRatioLimited = isLimited;
883    updateSeedRatio( session );
884}
885
886void
887tr_sessionSetRatioLimit( tr_session * session, double desiredRatio )
888{
889    assert( tr_isSession( session ) );
890
891    session->desiredRatio = desiredRatio;
892    updateSeedRatio( session );
893}
894
895tr_bool
896tr_sessionIsRatioLimited( const tr_session  * session )
897{
898    assert( tr_isSession( session ) );
899
900    return session->isRatioLimited;
901}
902
903double
904tr_sessionGetRatioLimit( const tr_session * session )
905{
906    assert( tr_isSession( session ) );
907
908    return session->desiredRatio;
909}
910
911/***
912****  SPEED LIMITS
913***/
914
915tr_bool
916tr_sessionGetActiveSpeedLimit( const tr_session * session, tr_direction dir, int * setme )
917{
918    int isLimited = TRUE;
919
920    if( tr_sessionUsesAltSpeed( session ) )
921        *setme = tr_sessionGetAltSpeed( session, dir );
922    else if( tr_sessionIsSpeedLimited( session, dir ) )
923        *setme = tr_sessionGetSpeedLimit( session, dir );
924    else
925        isLimited = FALSE;
926
927    return isLimited;
928}
929
930static void
931updateBandwidth( tr_session * session, tr_direction dir )
932{
933    int limit;
934    const tr_bool isLimited = tr_sessionGetActiveSpeedLimit( session, dir, &limit );
935    const tr_bool zeroCase = isLimited && !limit;
936
937    tr_bandwidthSetLimited( session->bandwidth, dir, isLimited && !zeroCase );
938
939    tr_bandwidthSetDesiredSpeed( session->bandwidth, dir, limit );
940}
941
942static void
943altSpeedToggled( void * vsession )
944{
945    tr_session * session = vsession;
946
947    assert( tr_isSession( session ) );
948
949    updateBandwidth( session, TR_UP );
950    updateBandwidth( session, TR_DOWN );
951
952    if( session->altCallback != NULL )
953        (*session->altCallback)( session, session->altSpeedEnabled, session->altSpeedChangedByUser, session->altCallbackUserData );
954}
955
956/* tell the alt speed limit timer to fire again at the top of the minute */
957static void
958setAltTimer( tr_session * session )
959{
960    const time_t now = time( NULL );
961    struct tm tm;
962    struct timeval tv;
963
964    assert( tr_isSession( session ) );
965    assert( session->altTimer != NULL );
966
967    tr_localtime_r( &now, &tm );
968    tv.tv_sec = 60 - tm.tm_sec;
969    tv.tv_usec = 0;
970    evtimer_add( session->altTimer, &tv );
971}
972
973/* this is called once a minute to:
974 * (1) update session->isAltTime
975 * (2) alter the speed limits when the alt limits go on and off */
976static void
977onAltTimer( int foo UNUSED, short bar UNUSED, void * vsession )
978{
979    tr_session * session = vsession;
980
981    assert( tr_isSession( session ) );
982
983    if( session->altSpeedTimeEnabled )
984    {
985        const time_t now = time( NULL );
986        struct tm tm;
987        int currentMinute, day;
988        tr_bool isBeginTime, isEndTime, isDay;
989        tr_localtime_r( &now, &tm );
990        currentMinute = tm.tm_hour*60 + tm.tm_min;
991        day = tm.tm_wday;
992       
993        isBeginTime = currentMinute == session->altSpeedTimeBegin;
994        isEndTime = currentMinute == session->altSpeedTimeEnd;
995        if( isBeginTime || isEndTime )
996        {
997            /* if looking at the end date, look at the next day if end time is before begin time */
998            if( isEndTime && !isBeginTime && session->altSpeedTimeEnd < session->altSpeedTimeBegin )
999                day = (day - 1) % 7;
1000           
1001            if( session->altSpeedTimeDay == TR_SCHED_ALL )
1002                isDay = TRUE;
1003            else if( session->altSpeedTimeDay == TR_SCHED_WEEKDAY )
1004                isDay = (day != 0) && (day != 6);
1005            else if( session->altSpeedTimeDay == TR_SCHED_WEEKEND )
1006                isDay = (day == 0) || (day == 6);
1007            else
1008                isDay = day == session->altSpeedTimeDay;
1009
1010            if( isDay )
1011                useAltSpeed( session, isBeginTime, FALSE );
1012        }
1013    }
1014
1015    setAltTimer( session );
1016}
1017
1018/***
1019****  Primary session speed limits
1020***/
1021
1022void
1023tr_sessionSetSpeedLimit( tr_session * s, tr_direction d, int KB_s )
1024{
1025    assert( tr_isSession( s ) );
1026    assert( tr_isDirection( d ) );
1027    assert( KB_s >= 0 );
1028
1029    s->speedLimit[d] = KB_s;
1030
1031    updateBandwidth( s, d );
1032}
1033
1034int
1035tr_sessionGetSpeedLimit( const tr_session * s, tr_direction d )
1036{
1037    assert( tr_isSession( s ) );
1038    assert( tr_isDirection( d ) );
1039
1040    return s->speedLimit[d];
1041}
1042
1043void
1044tr_sessionLimitSpeed( tr_session * s, tr_direction d, tr_bool b )
1045{
1046    assert( tr_isSession( s ) );
1047    assert( tr_isDirection( d ) );
1048    assert( tr_isBool( b ) );
1049
1050    s->speedLimitEnabled[d] = b;
1051
1052    updateBandwidth( s, d );
1053}
1054
1055tr_bool
1056tr_sessionIsSpeedLimited( const tr_session * s, tr_direction d )
1057{
1058    assert( tr_isSession( s ) );
1059    assert( tr_isDirection( d ) );
1060
1061    return s->speedLimitEnabled[d];
1062}
1063
1064/***
1065****  Alternative speed limits that are used during scheduled times
1066***/
1067
1068void
1069tr_sessionSetAltSpeed( tr_session * s, tr_direction d, int KB_s )
1070{
1071    assert( tr_isSession( s ) );
1072    assert( tr_isDirection( d ) );
1073    assert( KB_s >= 0 );
1074
1075    s->altSpeed[d] = KB_s;
1076
1077    updateBandwidth( s, d );
1078}
1079
1080int
1081tr_sessionGetAltSpeed( const tr_session * s, tr_direction d )
1082{
1083    assert( tr_isSession( s ) );
1084    assert( tr_isDirection( d ) );
1085
1086    return s->altSpeed[d]; 
1087}
1088
1089void
1090useAltSpeedTime( tr_session * session, tr_bool enabled, tr_bool byUser )
1091{
1092    assert( tr_isSession( session ) );
1093    assert( tr_isBool( enabled ) );
1094    assert( tr_isBool( byUser ) );
1095
1096    if( session->altSpeedTimeEnabled != enabled )
1097    {
1098        const tr_bool isAlt = isAltTime( session );
1099
1100        session->altSpeedTimeEnabled = enabled;
1101
1102        if( enabled && session->altSpeedEnabled != isAlt )
1103            useAltSpeed( session, isAlt, byUser );
1104    }
1105}
1106void
1107tr_sessionUseAltSpeedTime( tr_session * s, tr_bool b )
1108{
1109    useAltSpeedTime( s, b, TRUE );
1110}
1111
1112tr_bool
1113tr_sessionUsesAltSpeedTime( const tr_session * s )
1114{
1115    assert( tr_isSession( s ) );
1116
1117    return s->altSpeedTimeEnabled;
1118}
1119
1120void
1121tr_sessionSetAltSpeedBegin( tr_session * s, int minutes )
1122{
1123    assert( tr_isSession( s ) );
1124    assert( 0<=minutes && minutes<(60*24) );
1125
1126    if( s->altSpeedTimeBegin != minutes )
1127    {
1128        s->altSpeedTimeBegin = minutes;
1129
1130        if( tr_sessionUsesAltSpeedTime( s ) )
1131            useAltSpeed( s, isAltTime( s ), TRUE );
1132    }
1133}
1134
1135int
1136tr_sessionGetAltSpeedBegin( const tr_session * s )
1137{
1138    assert( tr_isSession( s ) );
1139
1140    return s->altSpeedTimeBegin;
1141}
1142
1143void
1144tr_sessionSetAltSpeedEnd( tr_session * s, int minutes )
1145{
1146    assert( tr_isSession( s ) );
1147    assert( 0<=minutes && minutes<(60*24) );
1148
1149    if( s->altSpeedTimeEnd != minutes )
1150    {
1151        s->altSpeedTimeEnd = minutes;
1152
1153        if( tr_sessionUsesAltSpeedTime( s ) )
1154            useAltSpeed( s, isAltTime( s ), TRUE );
1155    }
1156}
1157
1158int
1159tr_sessionGetAltSpeedEnd( const tr_session * s )
1160{
1161    assert( tr_isSession( s ) );
1162
1163    return s->altSpeedTimeEnd;
1164}
1165
1166void
1167tr_sessionSetAltSpeedDay( tr_session * s, tr_sched_day day )
1168{
1169    assert( tr_isSession( s ) );
1170
1171    if( s->altSpeedTimeDay != day )
1172    {
1173        s->altSpeedTimeDay = day;
1174
1175        if( tr_sessionUsesAltSpeedTime( s ) )
1176            useAltSpeed( s, isAltTime( s ), TRUE );
1177    }
1178}
1179
1180tr_sched_day
1181tr_sessionGetAltSpeedDay( const tr_session * s )
1182{
1183    assert( tr_isSession( s ) );
1184
1185    return s->altSpeedTimeDay;
1186}
1187
1188void
1189useAltSpeed( tr_session * s, tr_bool enabled, tr_bool byUser )
1190{
1191    assert( tr_isSession( s ) );
1192    assert( tr_isBool( enabled ) );
1193    assert( tr_isBool( byUser ) );
1194
1195    if( s->altSpeedEnabled != enabled)
1196    {
1197        s->altSpeedEnabled = enabled;
1198        s->altSpeedChangedByUser = byUser;
1199   
1200        tr_runInEventThread( s, altSpeedToggled, s );
1201    }
1202}
1203void
1204tr_sessionUseAltSpeed( tr_session * session, tr_bool enabled )
1205{
1206    useAltSpeed( session, enabled, TRUE );
1207}
1208
1209tr_bool
1210tr_sessionUsesAltSpeed( const tr_session * s )
1211{
1212    assert( tr_isSession( s ) );
1213
1214    return s->altSpeedEnabled;
1215}
1216
1217void
1218tr_sessionSetAltSpeedFunc( tr_session       * session,
1219                           tr_altSpeedFunc    func,
1220                           void             * userData )
1221{
1222    assert( tr_isSession( session ) );
1223
1224    session->altCallback = func;
1225    session->altCallbackUserData = userData;
1226}
1227
1228void
1229tr_sessionClearAltSpeedFunc( tr_session * session )
1230{
1231    tr_sessionSetAltSpeedFunc( session, NULL, NULL );
1232}
1233
1234/***
1235****
1236***/
1237
1238void
1239tr_sessionSetPeerLimit( tr_session * session, uint16_t maxGlobalPeers )
1240{
1241    assert( tr_isSession( session ) );
1242
1243    tr_fdSetPeerLimit( maxGlobalPeers );
1244}
1245
1246uint16_t
1247tr_sessionGetPeerLimit( const tr_session * session )
1248{
1249    assert( tr_isSession( session ) );
1250
1251    return tr_fdGetPeerLimit( );
1252}
1253
1254void
1255tr_sessionSetPeerLimitPerTorrent( tr_session  * session, uint16_t n )
1256{
1257    assert( tr_isSession( session ) );
1258
1259    session->peerLimitPerTorrent = n;
1260}
1261
1262uint16_t
1263tr_sessionGetPeerLimitPerTorrent( const tr_session * session )
1264{
1265    assert( tr_isSession( session ) );
1266
1267    return session->peerLimitPerTorrent;
1268}
1269
1270/***
1271****
1272***/
1273
1274double
1275tr_sessionGetPieceSpeed( const tr_session * session, tr_direction dir )
1276{
1277    return tr_isSession( session ) ? tr_bandwidthGetPieceSpeed( session->bandwidth, 0, dir ) : 0.0;
1278}
1279
1280double
1281tr_sessionGetRawSpeed( const tr_session * session, tr_direction dir )
1282{
1283    return tr_isSession( session ) ? tr_bandwidthGetPieceSpeed( session->bandwidth, 0, dir ) : 0.0;
1284}
1285
1286int
1287tr_sessionCountTorrents( const tr_session * session )
1288{
1289    return tr_isSession( session ) ? session->torrentCount : 0;
1290}
1291
1292static int
1293compareTorrentByCur( const void * va, const void * vb )
1294{
1295    const tr_torrent * a = *(const tr_torrent**)va;
1296    const tr_torrent * b = *(const tr_torrent**)vb;
1297    const uint64_t     aCur = a->downloadedCur + a->uploadedCur;
1298    const uint64_t     bCur = b->downloadedCur + b->uploadedCur;
1299
1300    if( aCur != bCur )
1301        return aCur > bCur ? -1 : 1; /* close the biggest torrents first */
1302
1303    return 0;
1304}
1305
1306static void
1307tr_closeAllConnections( void * vsession )
1308{
1309    tr_session *  session = vsession;
1310    tr_torrent *  tor;
1311    int           i, n;
1312    tr_torrent ** torrents;
1313
1314    assert( tr_isSession( session ) );
1315
1316    evtimer_del( session->altTimer );
1317    tr_free( session->altTimer );
1318    session->altTimer = NULL;
1319
1320    tr_statsClose( session );
1321    tr_sharedShuttingDown( session->shared );
1322    tr_rpcClose( &session->rpcServer );
1323
1324    /* close the torrents.  get the most active ones first so that
1325     * if we can't get them all closed in a reasonable amount of time,
1326     * at least we get the most important ones first. */
1327    tor = NULL;
1328    n = session->torrentCount;
1329    torrents = tr_new( tr_torrent *, session->torrentCount );
1330    for( i = 0; i < n; ++i )
1331        torrents[i] = tor = tr_torrentNext( session, tor );
1332    qsort( torrents, n, sizeof( tr_torrent* ), compareTorrentByCur );
1333    for( i = 0; i < n; ++i )
1334        tr_torrentFree( torrents[i] );
1335    tr_free( torrents );
1336
1337    tr_peerMgrFree( session->peerMgr );
1338
1339    tr_trackerSessionClose( session );
1340    tr_list_free( &session->blocklists,
1341                  (TrListForeachFunc)_tr_blocklistFree );
1342    tr_webClose( &session->web );
1343
1344    session->isClosed = TRUE;
1345}
1346
1347static int
1348deadlineReached( const uint64_t deadline )
1349{
1350    return tr_date( ) >= deadline;
1351}
1352
1353#define SHUTDOWN_MAX_SECONDS 30
1354
1355void
1356tr_sessionClose( tr_session * session )
1357{
1358    int            i;
1359    const int      maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000;
1360    const uint64_t deadline = tr_date( ) + maxwait_msec;
1361
1362    assert( tr_isSession( session ) );
1363
1364    dbgmsg( "shutting down transmission session %p", session );
1365
1366    /* close the session */
1367    tr_runInEventThread( session, tr_closeAllConnections, session );
1368    while( !session->isClosed && !deadlineReached( deadline ) )
1369    {
1370        dbgmsg(
1371            "waiting for the shutdown commands to run in the main thread" );
1372        tr_wait( 100 );
1373    }
1374
1375    /* "shared" and "tracker" have live sockets,
1376     * so we need to keep the transmission thread alive
1377     * for a bit while they tell the router & tracker
1378     * that we're closing now */
1379    while( ( session->shared
1380           || session->tracker ) && !deadlineReached( deadline ) )
1381    {
1382        dbgmsg( "waiting on port unmap (%p) or tracker (%p)",
1383                session->shared, session->tracker );
1384        tr_wait( 100 );
1385    }
1386
1387    tr_fdClose( );
1388
1389    /* close the libtransmission thread */
1390    tr_eventClose( session );
1391    while( session->events && !deadlineReached( deadline ) )
1392    {
1393        dbgmsg( "waiting for the libevent thread to shutdown cleanly" );
1394        tr_wait( 100 );
1395    }
1396
1397    /* free the session memory */
1398    tr_bandwidthFree( session->bandwidth );
1399    tr_lockFree( session->lock );
1400    for( i = 0; i < session->metainfoLookupCount; ++i )
1401        tr_free( session->metainfoLookup[i].filename );
1402    tr_free( session->metainfoLookup );
1403    tr_free( session->tag );
1404    tr_free( session->configDir );
1405    tr_free( session->resumeDir );
1406    tr_free( session->torrentDir );
1407    tr_free( session->downloadDir );
1408    tr_free( session->proxy );
1409    tr_free( session->proxyUsername );
1410    tr_free( session->proxyPassword );
1411    tr_free( session );
1412}
1413
1414tr_torrent **
1415tr_sessionLoadTorrents( tr_session * session,
1416                        tr_ctor    * ctor,
1417                        int        * setmeCount )
1418{
1419    int           i, n = 0;
1420    struct stat   sb;
1421    DIR *         odir = NULL;
1422    const char *  dirname = tr_getTorrentDir( session );
1423    tr_torrent ** torrents;
1424    tr_list *     l = NULL, *list = NULL;
1425
1426    assert( tr_isSession( session ) );
1427
1428    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
1429
1430    if( !stat( dirname, &sb )
1431      && S_ISDIR( sb.st_mode )
1432      && ( ( odir = opendir ( dirname ) ) ) )
1433    {
1434        struct dirent *d;
1435        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1436        {
1437            if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles, ., and ..
1438                                                     */
1439            {
1440                tr_torrent * tor;
1441                char * path = tr_buildPath( dirname, d->d_name, NULL );
1442                tr_ctorSetMetainfoFromFile( ctor, path );
1443                if(( tor = tr_torrentNew( ctor, NULL )))
1444                {
1445                    tr_list_append( &list, tor );
1446                    ++n;
1447                }
1448                tr_free( path );
1449            }
1450        }
1451        closedir( odir );
1452    }
1453
1454    torrents = tr_new( tr_torrent *, n );
1455    for( i = 0, l = list; l != NULL; l = l->next )
1456        torrents[i++] = (tr_torrent*) l->data;
1457    assert( i == n );
1458
1459    tr_list_free( &list, NULL );
1460
1461    if( n )
1462        tr_inf( _( "Loaded %d torrents" ), n );
1463
1464    if( setmeCount )
1465        *setmeCount = n;
1466    return torrents;
1467}
1468
1469/***
1470****
1471***/
1472
1473void
1474tr_sessionSetPexEnabled( tr_session * session,
1475                         tr_bool      enabled )
1476{
1477    assert( tr_isSession( session ) );
1478
1479    session->isPexEnabled = enabled != 0;
1480}
1481
1482tr_bool
1483tr_sessionIsPexEnabled( const tr_session * session )
1484{
1485    assert( tr_isSession( session ) );
1486
1487    return session->isPexEnabled;
1488}
1489
1490/***
1491****
1492***/
1493
1494void
1495tr_sessionSetLazyBitfieldEnabled( tr_session * session,
1496                                  tr_bool      enabled )
1497{
1498    assert( tr_isSession( session ) );
1499
1500    session->useLazyBitfield = enabled != 0;
1501}
1502
1503tr_bool
1504tr_sessionIsLazyBitfieldEnabled( const tr_session * session )
1505{
1506    assert( tr_isSession( session ) );
1507
1508    return session->useLazyBitfield;
1509}
1510
1511/***
1512****
1513***/
1514
1515void
1516tr_sessionSetPortForwardingEnabled( tr_session  * session,
1517                                    tr_bool       enabled )
1518{
1519    assert( tr_isSession( session ) );
1520
1521    tr_globalLock( session );
1522    tr_sharedTraversalEnable( session->shared, enabled );
1523    tr_globalUnlock( session );
1524}
1525
1526tr_bool
1527tr_sessionIsPortForwardingEnabled( const tr_session * session )
1528{
1529    assert( tr_isSession( session ) );
1530
1531    return tr_sharedTraversalIsEnabled( session->shared );
1532}
1533
1534/***
1535****
1536***/
1537
1538int
1539tr_blocklistGetRuleCount( const tr_session * session )
1540{
1541    int       n = 0;
1542    tr_list * l;
1543
1544    assert( tr_isSession( session ) );
1545
1546    for( l = session->blocklists; l; l = l->next )
1547        n += _tr_blocklistGetRuleCount( l->data );
1548    return n;
1549}
1550
1551tr_bool
1552tr_blocklistIsEnabled( const tr_session * session )
1553{
1554    assert( tr_isSession( session ) );
1555
1556    return session->isBlocklistEnabled;
1557}
1558
1559void
1560tr_blocklistSetEnabled( tr_session * session,
1561                        tr_bool      isEnabled )
1562{
1563    tr_list * l;
1564
1565    assert( tr_isSession( session ) );
1566
1567    session->isBlocklistEnabled = isEnabled != 0;
1568
1569    for( l=session->blocklists; l!=NULL; l=l->next )
1570        _tr_blocklistSetEnabled( l->data, isEnabled );
1571}
1572
1573tr_bool
1574tr_blocklistExists( const tr_session * session )
1575{
1576    assert( tr_isSession( session ) );
1577
1578    return session->blocklists != NULL;
1579}
1580
1581int
1582tr_blocklistSetContent( tr_session * session,
1583                        const char * contentFilename )
1584{
1585    tr_list *      l;
1586    tr_blocklist * b;
1587    const char *   defaultName = "level1.bin";
1588
1589    assert( tr_isSession( session ) );
1590
1591    for( b = NULL, l = session->blocklists; !b && l; l = l->next )
1592        if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ),
1593                               defaultName ) )
1594            b = l->data;
1595
1596    if( !b )
1597    {
1598        char * path = tr_buildPath( session->configDir, "blocklists", defaultName, NULL );
1599        b = _tr_blocklistNew( path, session->isBlocklistEnabled );
1600        tr_list_append( &session->blocklists, b );
1601        tr_free( path );
1602    }
1603
1604    return _tr_blocklistSetContent( b, contentFilename );
1605}
1606
1607tr_bool
1608tr_sessionIsAddressBlocked( const tr_session * session,
1609                            const tr_address * addr )
1610{
1611    tr_list * l;
1612
1613    assert( tr_isSession( session ) );
1614
1615    for( l = session->blocklists; l; l = l->next )
1616        if( _tr_blocklistHasAddress( l->data, addr ) )
1617            return TRUE;
1618    return FALSE;
1619}
1620
1621/***
1622****
1623***/
1624
1625static int
1626compareLookupEntries( const void * va, const void * vb )
1627{
1628    const struct tr_metainfo_lookup * a = va;
1629    const struct tr_metainfo_lookup * b = vb;
1630
1631    return strcmp( a->hashString, b->hashString );
1632}
1633
1634static void
1635metainfoLookupResort( tr_session * session )
1636{
1637    assert( tr_isSession( session ) );
1638
1639    qsort( session->metainfoLookup,
1640           session->metainfoLookupCount,
1641           sizeof( struct tr_metainfo_lookup ),
1642           compareLookupEntries );
1643}
1644
1645static int
1646compareHashStringToLookupEntry( const void * va, const void * vb )
1647{
1648    const char *                      a = va;
1649    const struct tr_metainfo_lookup * b = vb;
1650
1651    return strcmp( a, b->hashString );
1652}
1653
1654const char*
1655tr_sessionFindTorrentFile( const tr_session * session,
1656                           const char       * hashStr )
1657{
1658    struct tr_metainfo_lookup * l = bsearch( hashStr,
1659                                             session->metainfoLookup,
1660                                             session->metainfoLookupCount,
1661                                             sizeof( struct tr_metainfo_lookup ),
1662                                             compareHashStringToLookupEntry );
1663
1664    return l ? l->filename : NULL;
1665}
1666
1667static void
1668metainfoLookupRescan( tr_session * session )
1669{
1670    int          i;
1671    int          n;
1672    struct stat  sb;
1673    const char * dirname = tr_getTorrentDir( session );
1674    DIR *        odir = NULL;
1675    tr_ctor *    ctor = NULL;
1676    tr_list *    list = NULL;
1677
1678    assert( tr_isSession( session ) );
1679
1680    /* walk through the directory and find the mappings */
1681    ctor = tr_ctorNew( session );
1682    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
1683    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
1684    {
1685        struct dirent *d;
1686        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1687        {
1688            if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles, ., and ..
1689                                                     */
1690            {
1691                tr_info inf;
1692                char * path = tr_buildPath( dirname, d->d_name, NULL );
1693                tr_ctorSetMetainfoFromFile( ctor, path );
1694                if( !tr_torrentParse( ctor, &inf ) )
1695                {
1696                    tr_list_append( &list, tr_strdup( inf.hashString ) );
1697                    tr_list_append( &list, tr_strdup( path ) );
1698                    tr_metainfoFree( &inf );
1699                }
1700                tr_free( path );
1701            }
1702        }
1703        closedir( odir );
1704    }
1705    tr_ctorFree( ctor );
1706
1707    n = tr_list_size( list ) / 2;
1708    session->metainfoLookup = tr_new0( struct tr_metainfo_lookup, n );
1709    session->metainfoLookupCount = n;
1710    for( i = 0; i < n; ++i )
1711    {
1712        char * hashString = tr_list_pop_front( &list );
1713        char * filename = tr_list_pop_front( &list );
1714
1715        memcpy( session->metainfoLookup[i].hashString, hashString,
1716                2 * SHA_DIGEST_LENGTH + 1 );
1717        tr_free( hashString );
1718        session->metainfoLookup[i].filename = filename;
1719    }
1720
1721    metainfoLookupResort( session );
1722    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
1723}
1724
1725void
1726tr_sessionSetTorrentFile( tr_session * session,
1727                          const char * hashString,
1728                          const char * filename )
1729{
1730    struct tr_metainfo_lookup * l = bsearch( hashString,
1731                                             session->metainfoLookup,
1732                                             session->metainfoLookupCount,
1733                                             sizeof( struct tr_metainfo_lookup ),
1734                                             compareHashStringToLookupEntry );
1735
1736    if( l )
1737    {
1738        if( l->filename != filename )
1739        {
1740            tr_free( l->filename );
1741            l->filename = tr_strdup( filename );
1742        }
1743    }
1744    else
1745    {
1746        const int n = session->metainfoLookupCount++;
1747        struct tr_metainfo_lookup * node;
1748        session->metainfoLookup = tr_renew( struct tr_metainfo_lookup,
1749                                            session->metainfoLookup,
1750                                            session->metainfoLookupCount );
1751        node = session->metainfoLookup + n;
1752        memcpy( node->hashString, hashString, 2 * SHA_DIGEST_LENGTH + 1 );
1753        node->filename = tr_strdup( filename );
1754        metainfoLookupResort( session );
1755    }
1756}
1757
1758tr_torrent*
1759tr_torrentNext( tr_session * session,
1760                tr_torrent * tor )
1761{
1762    assert( tr_isSession( session ) );
1763
1764    return tor ? tor->next : session->torrentList;
1765}
1766
1767/***
1768****
1769***/
1770
1771void
1772tr_sessionSetRPCEnabled( tr_session * session,
1773                         tr_bool      isEnabled )
1774{
1775    assert( tr_isSession( session ) );
1776
1777    tr_rpcSetEnabled( session->rpcServer, isEnabled );
1778}
1779
1780tr_bool
1781tr_sessionIsRPCEnabled( const tr_session * session )
1782{
1783    assert( tr_isSession( session ) );
1784
1785    return tr_rpcIsEnabled( session->rpcServer );
1786}
1787
1788void
1789tr_sessionSetRPCPort( tr_session * session,
1790                      tr_port      port )
1791{
1792    assert( tr_isSession( session ) );
1793
1794    tr_rpcSetPort( session->rpcServer, port );
1795}
1796
1797tr_port
1798tr_sessionGetRPCPort( const tr_session * session )
1799{
1800    assert( tr_isSession( session ) );
1801
1802    return tr_rpcGetPort( session->rpcServer );
1803}
1804
1805void
1806tr_sessionSetRPCCallback( tr_session * session,
1807                          tr_rpc_func  func,
1808                          void *       user_data )
1809{
1810    assert( tr_isSession( session ) );
1811
1812    session->rpc_func = func;
1813    session->rpc_func_user_data = user_data;
1814}
1815
1816void
1817tr_sessionSetRPCWhitelist( tr_session * session,
1818                           const char * whitelist )
1819{
1820    assert( tr_isSession( session ) );
1821
1822    tr_rpcSetWhitelist( session->rpcServer, whitelist );
1823}
1824
1825char*
1826tr_sessionGetRPCWhitelist( const tr_session * session )
1827{
1828    assert( tr_isSession( session ) );
1829
1830    return tr_rpcGetWhitelist( session->rpcServer );
1831}
1832
1833void
1834tr_sessionSetRPCWhitelistEnabled( tr_session * session,
1835                                  tr_bool      isEnabled )
1836{
1837    assert( tr_isSession( session ) );
1838
1839    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
1840}
1841
1842tr_bool
1843tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
1844{
1845    assert( tr_isSession( session ) );
1846
1847    return tr_rpcGetWhitelistEnabled( session->rpcServer );
1848}
1849
1850
1851void
1852tr_sessionSetRPCPassword( tr_session * session,
1853                          const char * password )
1854{
1855    assert( tr_isSession( session ) );
1856
1857    tr_rpcSetPassword( session->rpcServer, password );
1858}
1859
1860char*
1861tr_sessionGetRPCPassword( const tr_session * session )
1862{
1863    assert( tr_isSession( session ) );
1864
1865    return tr_rpcGetPassword( session->rpcServer );
1866}
1867
1868void
1869tr_sessionSetRPCUsername( tr_session * session,
1870                          const char * username )
1871{
1872    assert( tr_isSession( session ) );
1873
1874    tr_rpcSetUsername( session->rpcServer, username );
1875}
1876
1877char*
1878tr_sessionGetRPCUsername( const tr_session * session )
1879{
1880    assert( tr_isSession( session ) );
1881
1882    return tr_rpcGetUsername( session->rpcServer );
1883}
1884
1885void
1886tr_sessionSetRPCPasswordEnabled( tr_session * session,
1887                                 tr_bool      isEnabled )
1888{
1889    assert( tr_isSession( session ) );
1890
1891    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
1892}
1893
1894tr_bool
1895tr_sessionIsRPCPasswordEnabled( const tr_session * session )
1896{
1897    assert( tr_isSession( session ) );
1898
1899    return tr_rpcIsPasswordEnabled( session->rpcServer );
1900}
1901
1902/***
1903****
1904***/
1905
1906tr_bool
1907tr_sessionIsProxyEnabled( const tr_session * session )
1908{
1909    assert( tr_isSession( session ) );
1910
1911    return session->isProxyEnabled;
1912}
1913
1914void
1915tr_sessionSetProxyEnabled( tr_session * session,
1916                           tr_bool      isEnabled )
1917{
1918    assert( tr_isSession( session ) );
1919
1920    session->isProxyEnabled = isEnabled != 0;
1921}
1922
1923tr_proxy_type
1924tr_sessionGetProxyType( const tr_session * session )
1925{
1926    assert( tr_isSession( session ) );
1927
1928    return session->proxyType;
1929}
1930
1931void
1932tr_sessionSetProxyType( tr_session *  session,
1933                        tr_proxy_type type )
1934{
1935    assert( tr_isSession( session ) );
1936
1937    session->proxyType = type;
1938}
1939
1940const char*
1941tr_sessionGetProxy( const tr_session * session )
1942{
1943    assert( tr_isSession( session ) );
1944
1945    return session->proxy;
1946}
1947
1948tr_port
1949tr_sessionGetProxyPort( const tr_session * session )
1950{
1951    assert( tr_isSession( session ) );
1952
1953    return session->proxyPort;
1954}
1955
1956void
1957tr_sessionSetProxy( tr_session * session,
1958                    const char * proxy )
1959{
1960    assert( tr_isSession( session ) );
1961
1962    if( proxy != session->proxy )
1963    {
1964        tr_free( session->proxy );
1965        session->proxy = tr_strdup( proxy );
1966    }
1967}
1968
1969void
1970tr_sessionSetProxyPort( tr_session * session,
1971                        tr_port      port )
1972{
1973    assert( tr_isSession( session ) );
1974
1975    session->proxyPort = port;
1976}
1977
1978tr_bool
1979tr_sessionIsProxyAuthEnabled( const tr_session * session )
1980{
1981    assert( tr_isSession( session ) );
1982
1983    return session->isProxyAuthEnabled;
1984}
1985
1986void
1987tr_sessionSetProxyAuthEnabled( tr_session * session,
1988                               tr_bool      isEnabled )
1989{
1990    assert( tr_isSession( session ) );
1991
1992    session->isProxyAuthEnabled = isEnabled != 0;
1993}
1994
1995const char*
1996tr_sessionGetProxyUsername( const tr_session * session )
1997{
1998    assert( tr_isSession( session ) );
1999
2000    return session->proxyUsername;
2001}
2002
2003void
2004tr_sessionSetProxyUsername( tr_session * session,
2005                            const char * username )
2006{
2007    assert( tr_isSession( session ) );
2008
2009    if( username != session->proxyUsername )
2010    {
2011        tr_free( session->proxyUsername );
2012        session->proxyUsername = tr_strdup( username );
2013    }
2014}
2015
2016const char*
2017tr_sessionGetProxyPassword( const tr_session * session )
2018{
2019    assert( tr_isSession( session ) );
2020
2021    return session->proxyPassword;
2022}
2023
2024void
2025tr_sessionSetProxyPassword( tr_session * session,
2026                            const char * password )
2027{
2028    assert( tr_isSession( session ) );
2029
2030    if( password != session->proxyPassword )
2031    {
2032        tr_free( session->proxyPassword );
2033        session->proxyPassword = tr_strdup( password );
2034    }
2035}
2036
2037int
2038tr_sessionGetActiveTorrentCount( tr_session * session )
2039{
2040    int ret = 0;
2041    tr_torrent * tor = NULL;
2042
2043    assert( tr_isSession( session ) );
2044
2045    while(( tor = tr_torrentNext( session, tor )))
2046        if( tr_torrentGetActivity( tor ) != TR_STATUS_STOPPED )
2047            ++ret;
2048
2049    return ret;
2050}
Note: See TracBrowser for help on using the repository browser.