479 lines
11 KiB
C
479 lines
11 KiB
C
/*
|
|
* This file Copyright (C) 2007-2010 Mnemosyne LLC
|
|
*
|
|
* 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.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include <errno.h> /* EINVAL */
|
|
#include "transmission.h"
|
|
#include "bencode.h"
|
|
#include "magnet.h"
|
|
#include "platform.h"
|
|
#include "session.h" /* tr_sessionFindTorrentFile() */
|
|
#include "torrent.h" /* tr_ctorGetSave() */
|
|
#include "utils.h" /* tr_new0 */
|
|
|
|
struct optional_args
|
|
{
|
|
tr_bool isSet_paused;
|
|
tr_bool isSet_connected;
|
|
tr_bool isSet_downloadDir;
|
|
|
|
tr_bool isPaused;
|
|
uint16_t peerLimit;
|
|
char * downloadDir;
|
|
};
|
|
|
|
/** Opaque class used when instantiating torrents.
|
|
* @ingroup tr_ctor */
|
|
struct tr_ctor
|
|
{
|
|
const tr_session * session;
|
|
tr_bool saveInOurTorrentsDir;
|
|
tr_bool doDelete;
|
|
|
|
tr_priority_t bandwidthPriority;
|
|
tr_bool isSet_metainfo;
|
|
tr_bool isSet_delete;
|
|
tr_benc metainfo;
|
|
char * sourceFile;
|
|
|
|
struct optional_args optionalArgs[2];
|
|
|
|
char * incompleteDir;
|
|
|
|
tr_file_index_t * want;
|
|
tr_file_index_t wantSize;
|
|
tr_file_index_t * notWant;
|
|
tr_file_index_t notWantSize;
|
|
tr_file_index_t * low;
|
|
tr_file_index_t lowSize;
|
|
tr_file_index_t * normal;
|
|
tr_file_index_t normalSize;
|
|
tr_file_index_t * high;
|
|
tr_file_index_t highSize;
|
|
};
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
static void
|
|
setSourceFile( tr_ctor * ctor,
|
|
const char * sourceFile )
|
|
{
|
|
tr_free( ctor->sourceFile );
|
|
ctor->sourceFile = tr_strdup( sourceFile );
|
|
}
|
|
|
|
static void
|
|
clearMetainfo( tr_ctor * ctor )
|
|
{
|
|
if( ctor->isSet_metainfo )
|
|
{
|
|
ctor->isSet_metainfo = 0;
|
|
tr_bencFree( &ctor->metainfo );
|
|
}
|
|
|
|
setSourceFile( ctor, NULL );
|
|
}
|
|
|
|
int
|
|
tr_ctorSetMetainfo( tr_ctor * ctor,
|
|
const uint8_t * metainfo,
|
|
size_t len )
|
|
{
|
|
int err;
|
|
|
|
clearMetainfo( ctor );
|
|
err = tr_bencLoad( metainfo, len, &ctor->metainfo, NULL );
|
|
ctor->isSet_metainfo = !err;
|
|
return err;
|
|
}
|
|
|
|
const char*
|
|
tr_ctorGetSourceFile( const tr_ctor * ctor )
|
|
{
|
|
return ctor->sourceFile;
|
|
}
|
|
|
|
int
|
|
tr_ctorSetMetainfoFromMagnetLink( tr_ctor * ctor, const char * magnet_link )
|
|
{
|
|
int err;
|
|
tr_magnet_info * magnet_info = tr_magnetParse( magnet_link );
|
|
|
|
if( magnet_info == NULL )
|
|
err = -1;
|
|
else {
|
|
int len;
|
|
tr_benc tmp;
|
|
char * str;
|
|
|
|
tr_magnetCreateMetainfo( magnet_info, &tmp );
|
|
str = tr_bencToStr( &tmp, TR_FMT_BENC, &len );
|
|
err = tr_ctorSetMetainfo( ctor, (const uint8_t*)str, len );
|
|
|
|
tr_free( str );
|
|
tr_magnetFree( magnet_info );
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
tr_ctorSetMetainfoFromFile( tr_ctor * ctor,
|
|
const char * filename )
|
|
{
|
|
uint8_t * metainfo;
|
|
size_t len;
|
|
int err;
|
|
|
|
metainfo = tr_loadFile( filename, &len );
|
|
if( metainfo && len )
|
|
err = tr_ctorSetMetainfo( ctor, metainfo, len );
|
|
else
|
|
{
|
|
clearMetainfo( ctor );
|
|
err = 1;
|
|
}
|
|
|
|
setSourceFile( ctor, filename );
|
|
|
|
/* if no `name' field was set, then set it from the filename */
|
|
if( ctor->isSet_metainfo )
|
|
{
|
|
tr_benc * info;
|
|
if( tr_bencDictFindDict( &ctor->metainfo, "info", &info ) )
|
|
{
|
|
const char * name;
|
|
if( !tr_bencDictFindStr( info, "name.utf-8", &name ) )
|
|
if( !tr_bencDictFindStr( info, "name", &name ) )
|
|
name = NULL;
|
|
if( !name || !*name )
|
|
{
|
|
char * base = tr_basename( filename );
|
|
tr_bencDictAddStr( info, "name", base );
|
|
tr_free( base );
|
|
}
|
|
}
|
|
}
|
|
|
|
tr_free( metainfo );
|
|
return err;
|
|
}
|
|
|
|
int
|
|
tr_ctorSetMetainfoFromHash( tr_ctor * ctor,
|
|
const char * hashString )
|
|
{
|
|
int err;
|
|
const char * filename;
|
|
|
|
filename = tr_sessionFindTorrentFile( ctor->session, hashString );
|
|
if( !filename )
|
|
err = EINVAL;
|
|
else
|
|
err = tr_ctorSetMetainfoFromFile( ctor, filename );
|
|
|
|
return err;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
void
|
|
tr_ctorSetFilePriorities( tr_ctor * ctor,
|
|
const tr_file_index_t * files,
|
|
tr_file_index_t fileCount,
|
|
tr_priority_t priority )
|
|
{
|
|
tr_file_index_t ** myfiles;
|
|
tr_file_index_t * mycount;
|
|
|
|
switch( priority ) {
|
|
case TR_PRI_LOW: myfiles = &ctor->low; mycount = &ctor->lowSize; break;
|
|
case TR_PRI_HIGH: myfiles = &ctor->high; mycount = &ctor->highSize; break;
|
|
default /*TR_PRI_NORMAL*/: myfiles = &ctor->normal; mycount = &ctor->normalSize; break;
|
|
}
|
|
|
|
tr_free( *myfiles );
|
|
*myfiles = tr_memdup( files, sizeof(tr_file_index_t)*fileCount );
|
|
*mycount = fileCount;
|
|
}
|
|
|
|
void
|
|
tr_ctorInitTorrentPriorities( const tr_ctor * ctor, tr_torrent * tor )
|
|
{
|
|
tr_file_index_t i;
|
|
|
|
for( i=0; i<ctor->lowSize; ++i )
|
|
tr_torrentInitFilePriority( tor, ctor->low[i], TR_PRI_LOW );
|
|
for( i=0; i<ctor->normalSize; ++i )
|
|
tr_torrentInitFilePriority( tor, ctor->normal[i], TR_PRI_NORMAL );
|
|
for( i=0; i<ctor->highSize; ++i )
|
|
tr_torrentInitFilePriority( tor, ctor->high[i], TR_PRI_HIGH );
|
|
}
|
|
|
|
void
|
|
tr_ctorSetFilesWanted( tr_ctor * ctor,
|
|
const tr_file_index_t * files,
|
|
tr_file_index_t fileCount,
|
|
tr_bool wanted )
|
|
{
|
|
tr_file_index_t ** myfiles = wanted ? &ctor->want : &ctor->notWant;
|
|
tr_file_index_t * mycount = wanted ? &ctor->wantSize : &ctor->notWantSize;
|
|
|
|
tr_free( *myfiles );
|
|
*myfiles = tr_memdup( files, sizeof(tr_file_index_t)*fileCount );
|
|
*mycount = fileCount;
|
|
}
|
|
|
|
void
|
|
tr_ctorInitTorrentWanted( const tr_ctor * ctor, tr_torrent * tor )
|
|
{
|
|
if( ctor->notWantSize )
|
|
tr_torrentInitFileDLs( tor, ctor->notWant, ctor->notWantSize, FALSE );
|
|
if( ctor->wantSize )
|
|
tr_torrentInitFileDLs( tor, ctor->want, ctor->wantSize, TRUE );
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
void
|
|
tr_ctorSetDeleteSource( tr_ctor * ctor,
|
|
tr_bool deleteSource )
|
|
{
|
|
ctor->doDelete = deleteSource != 0;
|
|
ctor->isSet_delete = 1;
|
|
}
|
|
|
|
int
|
|
tr_ctorGetDeleteSource( const tr_ctor * ctor,
|
|
uint8_t * setme )
|
|
{
|
|
int err = 0;
|
|
|
|
if( !ctor->isSet_delete )
|
|
err = 1;
|
|
else if( setme )
|
|
*setme = ctor->doDelete ? 1 : 0;
|
|
|
|
return err;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
void
|
|
tr_ctorSetSave( tr_ctor * ctor,
|
|
tr_bool saveInOurTorrentsDir )
|
|
{
|
|
ctor->saveInOurTorrentsDir = saveInOurTorrentsDir != 0;
|
|
}
|
|
|
|
int
|
|
tr_ctorGetSave( const tr_ctor * ctor )
|
|
{
|
|
return ctor && ctor->saveInOurTorrentsDir;
|
|
}
|
|
|
|
void
|
|
tr_ctorSetPaused( tr_ctor * ctor,
|
|
tr_ctorMode mode,
|
|
tr_bool isPaused )
|
|
{
|
|
struct optional_args * args = &ctor->optionalArgs[mode];
|
|
|
|
args->isSet_paused = 1;
|
|
args->isPaused = isPaused ? 1 : 0;
|
|
}
|
|
|
|
void
|
|
tr_ctorSetPeerLimit( tr_ctor * ctor,
|
|
tr_ctorMode mode,
|
|
uint16_t peerLimit )
|
|
{
|
|
struct optional_args * args = &ctor->optionalArgs[mode];
|
|
|
|
args->isSet_connected = 1;
|
|
args->peerLimit = peerLimit;
|
|
}
|
|
|
|
void
|
|
tr_ctorSetDownloadDir( tr_ctor * ctor,
|
|
tr_ctorMode mode,
|
|
const char * directory )
|
|
{
|
|
struct optional_args * args = &ctor->optionalArgs[mode];
|
|
|
|
tr_free( args->downloadDir );
|
|
args->downloadDir = NULL;
|
|
args->isSet_downloadDir = 0;
|
|
|
|
if( directory && *directory )
|
|
{
|
|
args->isSet_downloadDir = 1;
|
|
args->downloadDir = tr_strdup( directory );
|
|
}
|
|
}
|
|
|
|
void
|
|
tr_ctorSetIncompleteDir( tr_ctor * ctor, const char * directory )
|
|
{
|
|
tr_free( ctor->incompleteDir );
|
|
ctor->incompleteDir = tr_strdup( directory );
|
|
}
|
|
|
|
int
|
|
tr_ctorGetPeerLimit( const tr_ctor * ctor,
|
|
tr_ctorMode mode,
|
|
uint16_t * setmeCount )
|
|
{
|
|
int err = 0;
|
|
const struct optional_args * args = &ctor->optionalArgs[mode];
|
|
|
|
if( !args->isSet_connected )
|
|
err = 1;
|
|
else if( setmeCount )
|
|
*setmeCount = args->peerLimit;
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
tr_ctorGetPaused( const tr_ctor * ctor,
|
|
tr_ctorMode mode,
|
|
uint8_t * setmeIsPaused )
|
|
{
|
|
int err = 0;
|
|
const struct optional_args * args = &ctor->optionalArgs[mode];
|
|
|
|
if( !args->isSet_paused )
|
|
err = 1;
|
|
else if( setmeIsPaused )
|
|
*setmeIsPaused = args->isPaused ? 1 : 0;
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
tr_ctorGetDownloadDir( const tr_ctor * ctor,
|
|
tr_ctorMode mode,
|
|
const char ** setmeDownloadDir )
|
|
{
|
|
int err = 0;
|
|
const struct optional_args * args = &ctor->optionalArgs[mode];
|
|
|
|
if( !args->isSet_downloadDir )
|
|
err = 1;
|
|
else if( setmeDownloadDir )
|
|
*setmeDownloadDir = args->downloadDir;
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
tr_ctorGetIncompleteDir( const tr_ctor * ctor,
|
|
const char ** setmeIncompleteDir )
|
|
{
|
|
int err = 0;
|
|
|
|
if( ctor->incompleteDir == NULL )
|
|
err = 1;
|
|
else
|
|
*setmeIncompleteDir = ctor->incompleteDir;
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
tr_ctorGetMetainfo( const tr_ctor * ctor,
|
|
const tr_benc ** setme )
|
|
{
|
|
int err = 0;
|
|
|
|
if( !ctor->isSet_metainfo )
|
|
err = 1;
|
|
else if( setme )
|
|
*setme = &ctor->metainfo;
|
|
|
|
return err;
|
|
}
|
|
|
|
tr_session*
|
|
tr_ctorGetSession( const tr_ctor * ctor )
|
|
{
|
|
return (tr_session*) ctor->session;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
static tr_bool
|
|
isPriority( int i )
|
|
{
|
|
return (i==TR_PRI_LOW) || (i==TR_PRI_NORMAL) || (i==TR_PRI_HIGH);
|
|
}
|
|
|
|
void
|
|
tr_ctorSetBandwidthPriority( tr_ctor * ctor, tr_priority_t priority )
|
|
{
|
|
if( isPriority( priority ) )
|
|
ctor->bandwidthPriority = priority;
|
|
}
|
|
|
|
tr_priority_t
|
|
tr_ctorGetBandwidthPriority( const tr_ctor * ctor )
|
|
{
|
|
return ctor->bandwidthPriority;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
tr_ctor*
|
|
tr_ctorNew( const tr_session * session )
|
|
{
|
|
tr_ctor * ctor = tr_new0( struct tr_ctor, 1 );
|
|
|
|
ctor->session = session;
|
|
ctor->bandwidthPriority = TR_PRI_NORMAL;
|
|
if( session != NULL )
|
|
{
|
|
tr_ctorSetDeleteSource( ctor, tr_sessionGetDeleteSource( session ) );
|
|
tr_ctorSetPaused( ctor, TR_FALLBACK, tr_sessionGetPaused( session ) );
|
|
tr_ctorSetPeerLimit( ctor, TR_FALLBACK, session->peerLimitPerTorrent );
|
|
tr_ctorSetDownloadDir( ctor, TR_FALLBACK, session->downloadDir );
|
|
}
|
|
tr_ctorSetSave( ctor, TRUE );
|
|
return ctor;
|
|
}
|
|
|
|
void
|
|
tr_ctorFree( tr_ctor * ctor )
|
|
{
|
|
clearMetainfo( ctor );
|
|
tr_free( ctor->optionalArgs[1].downloadDir );
|
|
tr_free( ctor->optionalArgs[0].downloadDir );
|
|
tr_free( ctor->incompleteDir );
|
|
tr_free( ctor->want );
|
|
tr_free( ctor->notWant );
|
|
tr_free( ctor->low );
|
|
tr_free( ctor->high );
|
|
tr_free( ctor->normal );
|
|
tr_free( ctor );
|
|
}
|