transmission/libtransmission/session.c

1043 lines
28 KiB
C
Raw Normal View History

/*
* This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
2006-07-16 19:39:23 +00:00
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
2006-07-16 19:39:23 +00:00
*
* $Id$
*/
2006-07-16 19:39:23 +00:00
2007-11-09 20:07:52 +00:00
#include <assert.h>
#include <stdlib.h>
2008-02-19 18:39:49 +00:00
#include <string.h> /* memcpy */
2007-07-12 17:51:45 +00:00
#include <signal.h>
#include <sys/types.h> /* stat */
#include <sys/stat.h> /* stat */
#include <unistd.h> /* stat */
#include <dirent.h> /* opendir */
2006-07-16 19:39:23 +00:00
#include "transmission.h"
2008-03-30 13:22:45 +00:00
#include "blocklist.h"
2007-07-09 20:10:42 +00:00
#include "fdlimit.h"
#include "list.h"
#include "metainfo.h" /* tr_metainfoFree */
2007-07-09 20:10:42 +00:00
#include "net.h"
#include "peer-mgr.h"
2008-02-19 18:39:49 +00:00
#include "platform.h" /* tr_lock */
#include "port-forwarding.h"
#include "ratecontrol.h"
2008-05-18 16:44:30 +00:00
#include "rpc-server.h"
#include "stats.h"
#include "torrent.h"
#include "tracker.h"
#include "trevent.h"
#include "utils.h"
#include "web.h"
2006-07-16 19:39:23 +00:00
/* Generate a peer id : "-TRxyzb-" + 12 random alphanumeric
characters, where x is the major version number, y is the
minor version number, z is the maintenance number, and b
designates beta (Azureus-style) */
2008-01-07 06:19:34 +00:00
uint8_t*
tr_peerIdNew( void )
{
int i;
2008-01-07 17:52:50 +00:00
int val;
2008-01-07 06:19:34 +00:00
int total = 0;
uint8_t * buf = tr_new( uint8_t, 21 );
const char * pool = "0123456789abcdefghijklmnopqrstuvwxyz";
2008-01-07 17:52:50 +00:00
const int base = 36;
2008-01-07 06:19:34 +00:00
memcpy( buf, PEERID_PREFIX, 8 );
for( i=8; i<19; ++i ) {
2008-01-07 17:52:50 +00:00
val = tr_rand( base );
2008-01-07 06:19:34 +00:00
total += val;
buf[i] = pool[val];
}
2008-01-07 17:52:50 +00:00
val = total % base ? base - (total % base) : 0;
buf[19] = pool[val];
2008-01-07 06:19:34 +00:00
buf[20] = '\0';
2008-01-07 17:52:50 +00:00
2008-01-07 06:19:34 +00:00
return buf;
}
2008-01-07 06:19:34 +00:00
const uint8_t*
tr_getPeerId( void )
{
2008-01-07 06:19:34 +00:00
static uint8_t * id = NULL;
if( id == NULL )
id = tr_peerIdNew( );
return id;
}
/***
****
***/
tr_encryption_mode
tr_sessionGetEncryption( tr_session * session )
{
2008-08-01 16:43:22 +00:00
assert( session );
return session->encryptionMode;
}
void
tr_sessionSetEncryption( tr_session * session, tr_encryption_mode mode )
{
2008-08-01 16:43:22 +00:00
assert( session );
2007-11-09 01:25:34 +00:00
assert( mode==TR_ENCRYPTION_PREFERRED
|| mode==TR_ENCRYPTION_REQUIRED
|| mode==TR_PLAINTEXT_PREFERRED );
session->encryptionMode = mode;
}
/***
****
***/
static void
loadBlocklists( tr_session * session )
{
int binCount = 0;
int newCount = 0;
struct stat sb;
char dirname[MAX_PATH_LENGTH];
DIR * odir = NULL;
tr_list * list = NULL;
const int isEnabled = session->isBlocklistEnabled;
/* walk through the directory and find blocklists */
tr_buildPath( dirname, sizeof( dirname ), session->configDir, "blocklists", NULL );
if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && (( odir = opendir( dirname ))))
{
struct dirent *d;
for( d=readdir( odir ); d; d=readdir( odir ) )
{
char filename[MAX_PATH_LENGTH];
if( !d->d_name || d->d_name[0]=='.' ) /* skip dotfiles, ., and .. */
continue;
tr_buildPath( filename, sizeof(filename), dirname, d->d_name, NULL );
if( tr_stringEndsWith( filename, ".bin" ) )
{
/* if we don't already have this blocklist, add it */
if( !tr_list_find( list, filename, (TrListCompareFunc)strcmp ) )
{
tr_list_append( &list, _tr_blocklistNew( filename, isEnabled ) );
++binCount;
}
}
else
{
/* strip out the file suffix, if there is one, and add ".bin" instead */
tr_blocklist * b;
const char * dot = strrchr( d->d_name, '.' );
const int len = dot ? dot - d->d_name : (int)strlen( d->d_name );
char tmp[MAX_PATH_LENGTH];
tr_snprintf( tmp, sizeof( tmp ),
"%s%c%*.*s.bin", dirname, TR_PATH_DELIMITER, len, len, d->d_name );
b = _tr_blocklistNew( tmp, isEnabled );
_tr_blocklistSetContent( b, filename );
tr_list_append( &list, b );
++newCount;
}
}
closedir( odir );
}
session->blocklists = list;
if( binCount )
tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
if( newCount )
tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
}
/***
****
***/
static void metainfoLookupRescan( tr_handle * h );
tr_handle *
tr_sessionInitFull( const char * configDir,
const char * tag,
const char * downloadDir,
int isPexEnabled,
int isPortForwardingEnabled,
int publicPort,
int encryptionMode,
int isUploadLimitEnabled,
int uploadLimit,
int isDownloadLimitEnabled,
int downloadLimit,
int globalPeerLimit,
int messageLevel,
int isMessageQueueingEnabled,
int isBlocklistEnabled,
int peerSocketTOS,
int rpcIsEnabled,
int rpcPort,
const char * rpcACL,
int rpcAuthIsEnabled,
const char * rpcUsername,
const char * rpcPassword,
int proxyIsEnabled,
const char * proxy,
int proxyPort,
tr_proxy_type proxyType,
int proxyAuthIsEnabled,
const char * proxyUsername,
const char * proxyPassword )
2006-07-16 19:39:23 +00:00
{
tr_handle * h;
char filename[MAX_PATH_LENGTH];
2006-07-16 19:39:23 +00:00
#ifndef WIN32
/* Don't exit when writing on a broken socket */
signal( SIGPIPE, SIG_IGN );
#endif
if( configDir == NULL )
configDir = tr_getDefaultConfigDir( );
tr_msgInit( );
tr_setMessageLevel( messageLevel );
tr_setMessageQueuing( isMessageQueueingEnabled );
2006-07-16 19:39:23 +00:00
h = tr_new0( tr_handle, 1 );
h->lock = tr_lockNew( );
h->isPexEnabled = isPexEnabled ? 1 : 0;
h->encryptionMode = encryptionMode;
2008-04-12 00:29:49 +00:00
h->peerSocketTOS = peerSocketTOS;
2008-05-18 16:44:30 +00:00
h->downloadDir = tr_strdup( downloadDir );
2008-06-10 16:16:31 +00:00
h->isProxyEnabled = proxyIsEnabled ? 1 : 0;
h->proxy = tr_strdup( proxy );
h->proxyPort = proxyPort;
h->proxyType = proxyType;
2008-06-10 16:16:31 +00:00
h->isProxyAuthEnabled = proxyAuthIsEnabled ? 1 : 0;
h->proxyUsername = tr_strdup( proxyUsername );
h->proxyPassword = tr_strdup( proxyPassword );
tr_setConfigDir( h, configDir );
tr_netInit(); /* must go before tr_eventInit */
tr_eventInit( h );
while( !h->events )
tr_wait( 50 );
2008-04-17 18:45:02 +00:00
h->tag = tr_strdup( tag );
h->peerMgr = tr_peerMgrNew( h );
2006-07-16 19:39:23 +00:00
/* Initialize rate and file descripts controls */
h->upload = tr_rcInit();
tr_rcSetLimit( h->upload, uploadLimit );
h->useUploadLimit = isUploadLimitEnabled;
h->download = tr_rcInit();
tr_rcSetLimit( h->download, downloadLimit );
h->useDownloadLimit = isDownloadLimitEnabled;
tr_fdInit( globalPeerLimit );
2008-04-12 00:29:49 +00:00
h->shared = tr_sharedInit( h, isPortForwardingEnabled, publicPort );
h->isPortSet = publicPort >= 0;
2006-07-16 19:39:23 +00:00
/* first %s is the application name
second %s is the version number */
2008-04-09 20:53:09 +00:00
tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING );
/* initialize the blocklist */
tr_buildPath( filename, sizeof( filename ), h->configDir, "blocklists", NULL );
tr_mkdirp( filename, 0777 );
h->isBlocklistEnabled = isBlocklistEnabled;
loadBlocklists( h );
tr_statsInit( h );
h->web = tr_webInit( h );
h->rpcServer = tr_rpcInit( h, rpcIsEnabled, rpcPort, rpcACL,
2008-06-10 16:16:31 +00:00
rpcAuthIsEnabled, rpcUsername, rpcPassword );
metainfoLookupRescan( h );
2006-07-16 19:39:23 +00:00
return h;
}
tr_handle *
2008-05-18 16:44:30 +00:00
tr_sessionInit( const char * configDir,
const char * downloadDir,
const char * tag )
{
return tr_sessionInitFull( configDir,
downloadDir,
tag,
TR_DEFAULT_PEX_ENABLED,
TR_DEFAULT_PORT_FORWARDING_ENABLED,
-1, /* public port */
TR_ENCRYPTION_PREFERRED, /* encryption mode */
FALSE, /* use upload speed limit? */
-1, /* upload speed limit */
FALSE, /* use download speed limit? */
-1, /* download speed limit */
TR_DEFAULT_GLOBAL_PEER_LIMIT,
TR_MSG_INF, /* message level */
FALSE, /* is message queueing enabled? */
FALSE, /* is the blocklist enabled? */
TR_DEFAULT_PEER_SOCKET_TOS,
TR_DEFAULT_RPC_ENABLED,
TR_DEFAULT_RPC_PORT,
TR_DEFAULT_RPC_ACL,
FALSE,
"fnord",
2008-06-10 16:16:31 +00:00
"potzrebie",
TR_DEFAULT_PROXY_ENABLED,
TR_DEFAULT_PROXY,
TR_DEFAULT_PROXY_PORT,
TR_DEFAULT_PROXY_TYPE,
2008-06-10 16:16:31 +00:00
TR_DEFAULT_PROXY_AUTH_ENABLED,
TR_DEFAULT_PROXY_USERNAME,
TR_DEFAULT_PROXY_PASSWORD );
2008-05-18 16:44:30 +00:00
}
/***
****
***/
void
tr_sessionSetDownloadDir( tr_handle * handle, const char * dir )
{
if( handle->downloadDir != dir )
{
tr_free( handle->downloadDir );
handle->downloadDir = tr_strdup( dir );
}
}
const char *
tr_sessionGetDownloadDir( const tr_handle * handle )
{
return handle->downloadDir;
}
/***
****
***/
void
tr_globalLock( struct tr_handle * handle )
{
tr_lockLock( handle->lock );
}
void
tr_globalUnlock( struct tr_handle * handle )
{
tr_lockUnlock( handle->lock );
}
int
tr_globalIsLocked( const struct tr_handle * handle )
{
2008-02-28 16:40:31 +00:00
return handle && tr_lockHave( handle->lock );
}
2006-07-16 19:39:23 +00:00
/***********************************************************************
* tr_setBindPort
***********************************************************************
*
**********************************************************************/
2007-11-22 06:13:57 +00:00
struct bind_port_data
{
tr_handle * handle;
int port;
};
static void
tr_setBindPortImpl( void * vdata )
{
struct bind_port_data * data = vdata;
tr_handle * handle = data->handle;
const int port = data->port;
handle->isPortSet = 1;
tr_sharedSetPort( handle->shared, port );
tr_free( data );
}
void
tr_sessionSetPeerPort( tr_handle * handle, int port )
2006-07-16 19:39:23 +00:00
{
2007-11-22 06:13:57 +00:00
struct bind_port_data * data = tr_new( struct bind_port_data, 1 );
data->handle = handle;
data->port = port;
tr_runInEventThread( handle, tr_setBindPortImpl, data );
2006-09-25 18:37:45 +00:00
}
int
tr_sessionGetPeerPort( const tr_handle * h )
{
2008-08-01 16:43:22 +00:00
assert( h );
return tr_sharedGetPeerPort( h->shared );
}
tr_port_forwarding
tr_sessionGetPortForwarding( const tr_handle * h )
2006-09-25 18:37:45 +00:00
{
return tr_sharedTraversalStatus( h->shared );
2006-09-25 18:37:45 +00:00
}
/***
****
***/
void
tr_sessionSetSpeedLimitEnabled( tr_handle * h,
int up_or_down,
int use_flag )
2006-07-16 19:39:23 +00:00
{
if( up_or_down == TR_UP )
h->useUploadLimit = use_flag ? 1 : 0;
else
h->useDownloadLimit = use_flag ? 1 : 0;
2006-07-16 19:39:23 +00:00
}
int
tr_sessionIsSpeedLimitEnabled( const tr_handle * h, int up_or_down )
{
return up_or_down==TR_UP ? h->useUploadLimit : h->useDownloadLimit;
}
void
tr_sessionSetSpeedLimit( tr_handle * h,
int up_or_down,
int KiB_sec )
2006-07-16 19:39:23 +00:00
{
if( up_or_down == TR_DOWN )
tr_rcSetLimit( h->download, KiB_sec );
else
tr_rcSetLimit( h->upload, KiB_sec );
}
int
tr_sessionGetSpeedLimit( const tr_handle * h, int up_or_down )
{
return tr_rcGetLimit( up_or_down==TR_UP ? h->upload : h->download );
}
/***
****
***/
void
tr_sessionSetPeerLimit( tr_handle * handle UNUSED,
uint16_t maxGlobalPeers )
{
tr_fdSetPeerLimit( maxGlobalPeers );
}
uint16_t
tr_sessionGetPeerLimit( const tr_handle * handle UNUSED )
{
return tr_fdGetPeerLimit( );
}
/***
****
***/
void
tr_sessionGetSpeed( const tr_handle * session,
float * toClient,
float * toPeer )
2006-07-16 19:39:23 +00:00
{
if( session && toClient )
*toClient = tr_rcRate( session->download );
if( session && toPeer )
*toPeer = tr_rcRate( session->upload );
2006-07-16 19:39:23 +00:00
}
int
tr_sessionCountTorrents( const tr_handle * h )
2006-07-16 19:39:23 +00:00
{
return h->torrentCount;
}
/* close the biggest torrents first */
static int
compareTorrentByCur( const void * va, const void * vb )
{
const tr_torrent * a = *(const tr_torrent**)va;
const tr_torrent * b = *(const tr_torrent**)vb;
return -tr_compareUint64( a->downloadedCur + a->uploadedCur,
b->downloadedCur + b->uploadedCur );
}
static void
2008-08-01 13:46:03 +00:00
tr_closeAllConnections( void * vsession )
2006-07-16 19:39:23 +00:00
{
2008-08-01 13:46:03 +00:00
tr_handle * session = vsession;
tr_torrent * tor;
int i, n;
tr_torrent ** torrents;
2008-08-01 13:46:03 +00:00
tr_statsClose( session );
tr_sharedShuttingDown( session->shared );
tr_rpcClose( &session->rpcServer );
/* close the torrents. get the most active ones first so that
* if we can't get them all closed in a reasonable amount of time,
* at least we get the most important ones first. */
tor = NULL;
2008-08-01 13:46:03 +00:00
n = session->torrentCount;
torrents = tr_new( tr_torrent*, session->torrentCount );
for( i=0; i<n; ++i )
2008-08-01 13:46:03 +00:00
torrents[i] = tor = tr_torrentNext( session, tor );
qsort( torrents, n, sizeof(tr_torrent*), compareTorrentByCur );
for( i=0; i<n; ++i )
tr_torrentFree( torrents[i] );
tr_free( torrents );
2008-08-01 13:46:03 +00:00
tr_peerMgrFree( session->peerMgr );
2008-08-01 13:46:03 +00:00
tr_rcClose( session->upload );
tr_rcClose( session->download );
tr_trackerSessionClose( session );
tr_list_free( &session->blocklists, (TrListForeachFunc)_tr_blocklistFree );
tr_webClose( &session->web );
2008-08-01 13:46:03 +00:00
session->isClosed = TRUE;
}
static int
deadlineReached( const uint64_t deadline )
{
return tr_date( ) >= deadline;
}
#define SHUTDOWN_MAX_SECONDS 30
2008-08-01 13:46:03 +00:00
#define dbgmsg(fmt...) tr_deepLog( __FILE__, __LINE__, NULL, ##fmt )
void
2008-08-01 13:46:03 +00:00
tr_sessionClose( tr_handle * session )
{
2008-04-17 19:54:22 +00:00
int i;
const int maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000;
const uint64_t deadline = tr_date( ) + maxwait_msec;
2008-08-01 13:46:03 +00:00
dbgmsg( "shutting down transmission session %p", session );
2008-08-01 13:46:03 +00:00
/* close the session */
tr_runInEventThread( session, tr_closeAllConnections, session );
while( !session->isClosed && !deadlineReached( deadline ) ) {
dbgmsg( "waiting for the shutdown commands to run in the main thread" );
tr_wait( 100 );
2008-08-01 13:46:03 +00:00
}
2008-08-01 13:46:03 +00:00
/* "shared" and "tracker" have live sockets,
* so we need to keep the transmission thread alive
* for a bit while they tell the router & tracker
* that we're closing now */
while( ( session->shared || session->tracker ) && !deadlineReached( deadline ) ) {
dbgmsg( "waiting on port unmap (%p) or tracker (%p)",
session->shared, session->tracker );
tr_wait( 100 );
}
tr_fdClose( );
2008-08-01 13:46:03 +00:00
/* close the libtransmission thread */
tr_eventClose( session );
while( session->events && !deadlineReached( deadline ) ) {
dbgmsg( "waiting for the libevent thread to shutdown cleanly" );
tr_wait( 100 );
2008-08-01 13:46:03 +00:00
}
2008-08-01 13:46:03 +00:00
/* free the session memory */
tr_lockFree( session->lock );
for( i=0; i<session->metainfoLookupCount; ++i )
tr_free( session->metainfoLookup[i].filename );
tr_free( session->metainfoLookup );
tr_free( session->tag );
tr_free( session->configDir );
tr_free( session->resumeDir );
tr_free( session->torrentDir );
tr_free( session->downloadDir );
tr_free( session->proxy );
tr_free( session->proxyUsername );
tr_free( session->proxyPassword );
tr_free( session );
2006-07-16 19:39:23 +00:00
}
tr_torrent **
2008-05-18 16:44:30 +00:00
tr_sessionLoadTorrents ( tr_handle * h,
tr_ctor * ctor,
int * setmeCount )
{
int i, n = 0;
struct stat sb;
DIR * odir = NULL;
const char * dirname = tr_getTorrentDir( h );
tr_torrent ** torrents;
tr_list *l=NULL, *list=NULL;
tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
2007-12-21 22:18:40 +00:00
if( !stat( dirname, &sb )
&& S_ISDIR( sb.st_mode )
2007-12-21 22:18:40 +00:00
&& (( odir = opendir ( dirname ) )) )
{
struct dirent *d;
for (d = readdir( odir ); d!=NULL; d=readdir( odir ) )
{
if( d->d_name && d->d_name[0]!='.' ) /* skip dotfiles, ., and .. */
{
tr_torrent * tor;
2007-12-21 22:18:40 +00:00
char filename[MAX_PATH_LENGTH];
tr_buildPath( filename, sizeof(filename), dirname, d->d_name, NULL );
tr_ctorSetMetainfoFromFile( ctor, filename );
tor = tr_torrentNew( h, ctor, NULL );
if( tor ) {
tr_list_append( &list, tor );
++n;
}
}
}
closedir( odir );
}
torrents = tr_new( tr_torrent*, n );
for( i=0, l=list; l!=NULL; l=l->next )
torrents[i++] = (tr_torrent*) l->data;
assert( i==n );
tr_list_free( &list, NULL );
if( n )
tr_inf( _( "Loaded %d torrents" ), n );
2008-05-18 16:44:30 +00:00
if( setmeCount )
*setmeCount = n;
return torrents;
}
/***
****
***/
void
tr_sessionSetPexEnabled( tr_handle * handle, int isPexEnabled )
{
handle->isPexEnabled = isPexEnabled ? 1 : 0;
}
int
tr_sessionIsPexEnabled( const tr_handle * handle )
{
return handle->isPexEnabled;
}
/***
****
***/
void
tr_sessionSetPortForwardingEnabled( tr_handle * h, int enable )
{
tr_globalLock( h );
tr_sharedTraversalEnable( h->shared, enable );
tr_globalUnlock( h );
}
int
tr_sessionIsPortForwardingEnabled( const tr_handle * h )
{
return tr_sharedTraversalIsEnabled( h->shared );
}
/***
****
***/
int
tr_blocklistGetRuleCount( const tr_session * session )
{
int n = 0;
tr_list * l;
for( l=session->blocklists; l; l=l->next )
n += _tr_blocklistGetRuleCount( l->data );
return n;
}
int
tr_blocklistIsEnabled( const tr_session * session )
{
return session->isBlocklistEnabled;
}
void
tr_blocklistSetEnabled( tr_session * session, int isEnabled )
{
tr_list * l;
session->isBlocklistEnabled = isEnabled ? 1 : 0;
for( l=session->blocklists; l; l=l->next )
_tr_blocklistSetEnabled( l->data, isEnabled );
}
int
tr_blocklistExists( const tr_session * session )
{
return session->blocklists != NULL;
}
int
tr_blocklistSetContent( tr_session * session, const char * contentFilename )
{
tr_list * l;
tr_blocklist * b;
const char * defaultName = "level1.bin";
for( b=NULL, l=session->blocklists; !b && l; l=l->next )
if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ), defaultName ) )
b = l->data;
if( !b ) {
char filename[MAX_PATH_LENGTH];
tr_buildPath( filename, sizeof( filename ), session->configDir, "blocklists", defaultName, NULL );
b = _tr_blocklistNew( filename, session->isBlocklistEnabled );
tr_list_append( &session->blocklists, b );
}
return _tr_blocklistSetContent( b, contentFilename );
}
int
tr_sessionIsAddressBlocked( const tr_session * session,
const struct in_addr * addr )
{
tr_list * l;
for( l=session->blocklists; l; l=l->next )
if( _tr_blocklistHasAddress( l->data, addr ) )
return TRUE;
return FALSE;
}
/***
****
***/
static int
compareLookupEntries( const void * va, const void * vb )
{
const struct tr_metainfo_lookup * a = va;
const struct tr_metainfo_lookup * b = vb;
return strcmp( a->hashString, b->hashString );
}
static void
metainfoLookupResort( tr_handle * h )
{
qsort( h->metainfoLookup,
h->metainfoLookupCount,
sizeof( struct tr_metainfo_lookup ),
compareLookupEntries );
}
static int
compareHashStringToLookupEntry( const void * va, const void * vb )
{
const char * a = va;
const struct tr_metainfo_lookup * b = vb;
return strcmp( a, b->hashString );
}
const char*
tr_sessionFindTorrentFile( const tr_handle * h,
const char * hashStr )
{
struct tr_metainfo_lookup * l = bsearch( hashStr,
h->metainfoLookup,
h->metainfoLookupCount,
sizeof( struct tr_metainfo_lookup ),
compareHashStringToLookupEntry );
return l ? l->filename : NULL;
}
static void
metainfoLookupRescan( tr_handle * h )
{
int i;
int n;
struct stat sb;
const char * dirname = tr_getTorrentDir( h );
DIR * odir = NULL;
tr_ctor * ctor = NULL;
tr_list * list = NULL;
/* walk through the directory and find the mappings */
ctor = tr_ctorNew( h );
tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && (( odir = opendir( dirname ))))
{
struct dirent *d;
for (d = readdir( odir ); d!=NULL; d=readdir( odir ) )
{
if( d->d_name && d->d_name[0]!='.' ) /* skip dotfiles, ., and .. */
{
tr_info inf;
char filename[MAX_PATH_LENGTH];
tr_buildPath( filename, sizeof(filename), dirname, d->d_name, NULL );
tr_ctorSetMetainfoFromFile( ctor, filename );
if( !tr_torrentParse( h, ctor, &inf ) )
{
tr_list_append( &list, tr_strdup( inf.hashString ) );
tr_list_append( &list, tr_strdup( filename ) );
tr_metainfoFree( &inf );
}
}
}
closedir( odir );
}
tr_ctorFree( ctor );
n = tr_list_size( list ) / 2;
h->metainfoLookup = tr_new0( struct tr_metainfo_lookup, n );
h->metainfoLookupCount = n;
for( i=0; i<n; ++i )
{
char * hashString = tr_list_pop_front( &list );
char * filename = tr_list_pop_front( &list );
memcpy( h->metainfoLookup[i].hashString, hashString, 2*SHA_DIGEST_LENGTH+1 );
tr_free( hashString );
h->metainfoLookup[i].filename = filename;
}
metainfoLookupResort( h );
tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
}
void
tr_sessionSetTorrentFile( tr_handle * h,
const char * hashString,
const char * filename )
{
struct tr_metainfo_lookup * l = bsearch( hashString,
h->metainfoLookup,
h->metainfoLookupCount,
sizeof( struct tr_metainfo_lookup ),
compareHashStringToLookupEntry );
2008-08-01 16:43:22 +00:00
if( l )
{
if( l->filename != filename )
{
tr_free( l->filename );
l->filename = tr_strdup( filename );
}
}
else
{
const int n = h->metainfoLookupCount++;
struct tr_metainfo_lookup * node;
h->metainfoLookup = tr_renew( struct tr_metainfo_lookup,
h->metainfoLookup,
h->metainfoLookupCount );
node = h->metainfoLookup + n;
memcpy( node->hashString, hashString, 2*SHA_DIGEST_LENGTH+1 );
node->filename = tr_strdup( filename );
metainfoLookupResort( h );
}
}
tr_torrent*
tr_torrentNext( tr_handle * session, tr_torrent * tor )
{
return tor ? tor->next : session->torrentList;
}
2008-05-18 16:44:30 +00:00
/***
****
***/
void
2008-06-10 16:16:31 +00:00
tr_sessionSetRPCEnabled( tr_session * session, int isEnabled )
2008-05-18 16:44:30 +00:00
{
tr_rpcSetEnabled( session->rpcServer, isEnabled );
}
int
2008-06-10 16:16:31 +00:00
tr_sessionIsRPCEnabled( const tr_session * session )
2008-05-18 16:44:30 +00:00
{
return tr_rpcIsEnabled( session->rpcServer );
}
void
2008-06-10 16:16:31 +00:00
tr_sessionSetRPCPort( tr_session * session, int port )
2008-05-18 16:44:30 +00:00
{
tr_rpcSetPort( session->rpcServer, port );
}
int
2008-06-10 16:16:31 +00:00
tr_sessionGetRPCPort( const tr_session * session )
2008-05-18 16:44:30 +00:00
{
return tr_rpcGetPort( session->rpcServer );
}
void
2008-06-10 16:16:31 +00:00
tr_sessionSetRPCCallback( tr_session * session,
2008-05-18 16:44:30 +00:00
tr_rpc_func func,
void * user_data )
{
session->rpc_func = func;
session->rpc_func_user_data = user_data;
}
int
2008-06-10 16:16:31 +00:00
tr_sessionTestRPCACL( const tr_session * session,
const char * acl,
char ** allocme_errmsg )
{
return tr_rpcTestACL( session->rpcServer, acl, allocme_errmsg );
}
int
2008-06-10 16:16:31 +00:00
tr_sessionSetRPCACL( tr_session * session,
const char * acl,
char ** allocme_errmsg )
2008-05-18 16:44:30 +00:00
{
return tr_rpcSetACL( session->rpcServer, acl, allocme_errmsg );
2008-05-18 16:44:30 +00:00
}
char*
2008-05-18 16:44:30 +00:00
tr_sessionGetRPCACL( const tr_session * session )
{
return tr_rpcGetACL( session->rpcServer );
}
void
2008-06-10 16:16:31 +00:00
tr_sessionSetRPCPassword( tr_session * session, const char * password )
{
tr_rpcSetPassword( session->rpcServer, password );
}
char*
2008-06-10 16:16:31 +00:00
tr_sessionGetRPCPassword( const tr_session * session )
{
return tr_rpcGetPassword( session->rpcServer );
}
void
2008-06-10 16:16:31 +00:00
tr_sessionSetRPCUsername( tr_session * session, const char * username )
{
tr_rpcSetUsername( session->rpcServer, username );
}
char*
2008-06-10 16:16:31 +00:00
tr_sessionGetRPCUsername( const tr_session * session )
{
return tr_rpcGetUsername( session->rpcServer );
}
void
2008-06-10 16:16:31 +00:00
tr_sessionSetRPCPasswordEnabled( tr_session * session, int isEnabled )
{
tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
}
int
2008-06-10 16:16:31 +00:00
tr_sessionIsRPCPasswordEnabled( const tr_session * session )
{
return tr_rpcIsPasswordEnabled( session->rpcServer );
}
2008-06-10 16:16:31 +00:00
/***
****
***/
int
tr_sessionIsProxyEnabled( const tr_session * session )
{
return session->isProxyEnabled;
}
void
tr_sessionSetProxyEnabled( tr_session * session, int isEnabled )
{
session->isProxyEnabled = isEnabled ? 1 : 0;
}
tr_proxy_type
tr_sessionGetProxyType( const tr_session * session )
{
return session->proxyType;
}
void
tr_sessionSetProxyType( tr_session * session, tr_proxy_type type )
{
session->proxyType = type;
}
2008-06-10 16:16:31 +00:00
const char*
tr_sessionGetProxy( const tr_session * session )
{
return session->proxy;
}
int
tr_sessionGetProxyPort( const tr_session * session )
{
return session->proxyPort;
}
2008-06-10 16:16:31 +00:00
void
tr_sessionSetProxy( tr_session * session, const char * proxy )
{
if( proxy != session->proxy )
{
tr_free( session->proxy );
session->proxy = tr_strdup( proxy );
}
}
void
tr_sessionSetProxyPort( tr_session * session, int port )
{
session->proxyPort = port;
}
2008-06-10 16:16:31 +00:00
int
tr_sessionIsProxyAuthEnabled( const tr_session * session )
{
return session->isProxyAuthEnabled;
}
void
tr_sessionSetProxyAuthEnabled( tr_session * session, int isEnabled )
{
session->isProxyAuthEnabled = isEnabled ? 1 : 0;
}
const char*
tr_sessionGetProxyUsername( const tr_session * session )
{
return session->proxyUsername;
}
void
tr_sessionSetProxyUsername( tr_session * session, const char * username )
{
if( username != session->proxyUsername )
{
tr_free( session->proxyUsername );
session->proxyUsername = tr_strdup( username );
}
}
const char*
tr_sessionGetProxyPassword( const tr_session * session )
{
return session->proxyPassword;
}
void
tr_sessionSetProxyPassword( tr_session * session, const char * password )
{
if( password != session->proxyPassword )
{
tr_free( session->proxyPassword );
session->proxyPassword = tr_strdup( password );
}
}