(trunk) #2802, #2716, #2717 -- remember magnet links and their settings between sessions, and allow their trackers to be modified
This commit is contained in:
parent
b9e8624b3c
commit
36d97610bd
|
@ -381,7 +381,7 @@ main( int argc,
|
|||
if( fileContents != NULL ) {
|
||||
tr_ctorSetMetainfo( ctor, fileContents, fileLength );
|
||||
} else if( !memcmp( torrentPath, "magnet:?", 8 ) ) {
|
||||
tr_ctorSetMagnet( ctor, torrentPath );
|
||||
tr_ctorSetMetainfoFromMagnetLink( ctor, torrentPath );
|
||||
} else if( !memcmp( torrentPath, "http", 4 ) ) {
|
||||
tr_webRun( h, torrentPath, NULL, onTorrentFileDownloaded, ctor );
|
||||
waitingOnWeb = TRUE;
|
||||
|
|
|
@ -1052,7 +1052,7 @@ tr_core_add_from_url( TrCore * core, const char * url )
|
|||
if( gtr_is_hex_hashcode( url ) )
|
||||
url = tmp = g_strdup_printf( "magnet:?xt=urn:btih:%s", url );
|
||||
|
||||
err = tr_ctorSetMagnet( ctor, url );
|
||||
err = tr_ctorSetMetainfoFromMagnetLink( ctor, url );
|
||||
|
||||
if( !err )
|
||||
{
|
||||
|
|
|
@ -671,8 +671,7 @@ tr_bencListAdd( tr_benc * list )
|
|||
}
|
||||
|
||||
tr_benc *
|
||||
tr_bencListAddInt( tr_benc * list,
|
||||
int64_t val )
|
||||
tr_bencListAddInt( tr_benc * list, int64_t val )
|
||||
{
|
||||
tr_benc * node = tr_bencListAdd( list );
|
||||
|
||||
|
@ -681,15 +680,37 @@ tr_bencListAddInt( tr_benc * list,
|
|||
}
|
||||
|
||||
tr_benc *
|
||||
tr_bencListAddStr( tr_benc * list,
|
||||
const char * val )
|
||||
tr_bencListAddReal( tr_benc * list, double val )
|
||||
{
|
||||
tr_benc * node = tr_bencListAdd( list );
|
||||
tr_bencInitReal( node, val );
|
||||
return node;
|
||||
}
|
||||
|
||||
tr_benc *
|
||||
tr_bencListAddBool( tr_benc * list, tr_bool val )
|
||||
{
|
||||
tr_benc * node = tr_bencListAdd( list );
|
||||
tr_bencInitBool( node, val );
|
||||
return node;
|
||||
}
|
||||
|
||||
tr_benc *
|
||||
tr_bencListAddStr( tr_benc * list, const char * val )
|
||||
{
|
||||
tr_benc * node = tr_bencListAdd( list );
|
||||
tr_bencInitStr( node, val, -1 );
|
||||
return node;
|
||||
}
|
||||
|
||||
tr_benc *
|
||||
tr_bencListAddRaw( tr_benc * list, const uint8_t * val, size_t len )
|
||||
{
|
||||
tr_benc * node = tr_bencListAdd( list );
|
||||
tr_bencInitRaw( node, val, len );
|
||||
return node;
|
||||
}
|
||||
|
||||
tr_benc*
|
||||
tr_bencListAddList( tr_benc * list,
|
||||
size_t reserveCount )
|
||||
|
@ -802,6 +823,35 @@ tr_bencDictAddStr( tr_benc * dict, const char * key, const char * val )
|
|||
return child;
|
||||
}
|
||||
|
||||
tr_benc*
|
||||
tr_bencDictAddRaw( tr_benc * dict,
|
||||
const char * key,
|
||||
const void * src,
|
||||
size_t len )
|
||||
{
|
||||
tr_benc * child;
|
||||
|
||||
/* see if it already exists, and if so, try to reuse it */
|
||||
if(( child = tr_bencDictFind( dict, key ))) {
|
||||
if( tr_bencIsString( child ) ) {
|
||||
if( stringIsAlloced( child ) )
|
||||
tr_free( child->val.s.str.ptr );
|
||||
} else {
|
||||
tr_bencDictRemove( dict, key );
|
||||
child = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* if it doesn't exist, create it */
|
||||
if( child == NULL )
|
||||
child = tr_bencDictAdd( dict, key );
|
||||
|
||||
/* set it */
|
||||
tr_bencInitRaw( child, src, len );
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
tr_benc*
|
||||
tr_bencDictAddList( tr_benc * dict,
|
||||
const char * key,
|
||||
|
@ -824,18 +874,6 @@ tr_bencDictAddDict( tr_benc * dict,
|
|||
return child;
|
||||
}
|
||||
|
||||
tr_benc*
|
||||
tr_bencDictAddRaw( tr_benc * dict,
|
||||
const char * key,
|
||||
const void * src,
|
||||
size_t len )
|
||||
{
|
||||
tr_benc * child = tr_bencDictAdd( dict, key );
|
||||
|
||||
tr_bencInitRaw( child, src, len );
|
||||
return child;
|
||||
}
|
||||
|
||||
int
|
||||
tr_bencDictRemove( tr_benc * dict,
|
||||
const char * key )
|
||||
|
@ -1404,6 +1442,51 @@ static const struct WalkFuncs jsonWalkFuncs = { jsonIntFunc,
|
|||
****
|
||||
***/
|
||||
|
||||
static void
|
||||
tr_bencListCopy( tr_benc * target, const tr_benc * src )
|
||||
{
|
||||
int i = 0;
|
||||
const tr_benc * val;
|
||||
|
||||
while(( val = tr_bencListChild( (tr_benc*)src, i++ )))
|
||||
{
|
||||
if( tr_bencIsBool( val ) )
|
||||
{
|
||||
tr_bool boolVal = 0;
|
||||
tr_bencGetBool( val, &boolVal );
|
||||
tr_bencListAddBool( target, boolVal );
|
||||
}
|
||||
else if( tr_bencIsReal( val ) )
|
||||
{
|
||||
double realVal = 0;
|
||||
tr_bencGetReal( val, &realVal );
|
||||
tr_bencListAddReal( target, realVal );
|
||||
}
|
||||
else if( tr_bencIsInt( val ) )
|
||||
{
|
||||
int64_t intVal = 0;
|
||||
tr_bencGetInt( val, &intVal );
|
||||
tr_bencListAddInt( target, intVal );
|
||||
}
|
||||
else if( tr_bencIsString( val ) )
|
||||
{
|
||||
tr_bencListAddRaw( target, (const uint8_t*)getStr( val ), val->val.s.len );
|
||||
}
|
||||
else if( tr_bencIsDict( val ) )
|
||||
{
|
||||
tr_bencMergeDicts( tr_bencListAddDict( target, 0 ), val );
|
||||
}
|
||||
else if ( tr_bencIsList( val ) )
|
||||
{
|
||||
tr_bencListCopy( tr_bencListAddList( target, 0 ), val );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_err( "tr_bencListCopy skipping item" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t
|
||||
tr_bencDictSize( const tr_benc * dict )
|
||||
{
|
||||
|
@ -1470,14 +1553,19 @@ tr_bencMergeDicts( tr_benc * target, const tr_benc * source )
|
|||
}
|
||||
else if( tr_bencIsString( val ) )
|
||||
{
|
||||
const char * strVal = NULL;
|
||||
tr_bencGetStr( val, &strVal );
|
||||
tr_bencDictAddStr( target, key, strVal );
|
||||
tr_bencDictAddRaw( target, key, getStr( val ), val->val.s.len );
|
||||
}
|
||||
else if( tr_bencIsDict( val ) && tr_bencDictFindDict( target, key, &t ) )
|
||||
{
|
||||
tr_bencMergeDicts( t, val );
|
||||
}
|
||||
else if( tr_bencIsList( val ) )
|
||||
{
|
||||
if( tr_bencDictFind( target, key ) == NULL )
|
||||
{
|
||||
tr_bencListCopy( tr_bencDictAddList( target, key, tr_bencListSize( val ) ), val );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_dbg( "tr_bencMergeDicts skipping \"%s\"", key );
|
||||
|
|
|
@ -138,10 +138,16 @@ int tr_bencListReserve( tr_benc *, size_t reserveCount );
|
|||
|
||||
tr_benc * tr_bencListAdd( tr_benc * );
|
||||
|
||||
tr_benc * tr_bencListAddBool( tr_benc *, tr_bool val );
|
||||
|
||||
tr_benc * tr_bencListAddInt( tr_benc *, int64_t val );
|
||||
|
||||
tr_benc * tr_bencListAddReal( tr_benc *, double val );
|
||||
|
||||
tr_benc * tr_bencListAddStr( tr_benc *, const char * val );
|
||||
|
||||
tr_benc * tr_bencListAddRaw( tr_benc *, const uint8_t * val, size_t len );
|
||||
|
||||
tr_benc * tr_bencListAddList( tr_benc *, size_t reserveCount );
|
||||
|
||||
tr_benc * tr_bencListAddDict( tr_benc *, size_t reserveCount );
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <string.h> /* strchr() */
|
||||
|
||||
#include "transmission.h"
|
||||
#include "bencode.h"
|
||||
#include "magnet.h"
|
||||
#include "utils.h"
|
||||
#include "web.h"
|
||||
|
@ -193,3 +194,35 @@ tr_magnetFree( tr_magnet_info * info )
|
|||
tr_free( info );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tr_magnetCreateMetainfo( const tr_magnet_info * info, tr_benc * top )
|
||||
{
|
||||
int i;
|
||||
tr_benc * d;
|
||||
tr_bencInitDict( top, 4 );
|
||||
|
||||
/* announce list */
|
||||
if( info->trackerCount == 1 )
|
||||
tr_bencDictAddStr( top, "announce", info->trackers[0] );
|
||||
else {
|
||||
tr_benc * trackers = tr_bencDictAddList( top, "announce-list", info->trackerCount );
|
||||
for( i=0; i<info->trackerCount; ++i )
|
||||
tr_bencListAddStr( tr_bencListAddList( trackers, 1 ), info->trackers[i] );
|
||||
}
|
||||
|
||||
/* webseeds */
|
||||
if( info->webseedCount > 0 ) {
|
||||
tr_benc * urls = tr_bencDictAddList( top, "url-list", info->webseedCount );
|
||||
for( i=0; i<info->webseedCount; ++i )
|
||||
tr_bencListAddStr( urls, info->webseeds[i] );
|
||||
}
|
||||
|
||||
/* nonstandard keys */
|
||||
d = tr_bencDictAddDict( top, "magnet-info", 2 );
|
||||
tr_bencDictAddRaw( d, "info_hash", info->hash, 20 );
|
||||
if( info->displayName != NULL )
|
||||
tr_bencDictAddStr( d, "display-name", info->displayName );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,10 @@ tr_magnet_info;
|
|||
|
||||
tr_magnet_info * tr_magnetParse( const char * uri );
|
||||
|
||||
struct tr_benc;
|
||||
|
||||
void tr_magnetCreateMetainfo( const tr_magnet_info *, struct tr_benc * );
|
||||
|
||||
void tr_magnetFree( tr_magnet_info * info );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "session.h"
|
||||
#include "bencode.h"
|
||||
#include "crypto.h" /* tr_sha1 */
|
||||
#include "magnet.h"
|
||||
#include "metainfo.h"
|
||||
#include "platform.h"
|
||||
#include "utils.h"
|
||||
|
@ -384,29 +383,61 @@ escape( char * out, const uint8_t * in, size_t in_len ) /* rfc2396 */
|
|||
}
|
||||
|
||||
static const char*
|
||||
tr_metainfoParseImpl( const tr_session * session,
|
||||
tr_info * inf,
|
||||
int * infoDictOffset,
|
||||
int * infoDictLength,
|
||||
const tr_benc * meta_in )
|
||||
tr_metainfoParseImpl( const tr_session * session,
|
||||
tr_info * inf,
|
||||
tr_bool * hasInfoDict,
|
||||
int * infoDictOffset,
|
||||
int * infoDictLength,
|
||||
const tr_benc * meta_in )
|
||||
{
|
||||
int64_t i;
|
||||
size_t raw_len;
|
||||
const char * str;
|
||||
const uint8_t * raw;
|
||||
tr_benc * beInfo;
|
||||
tr_benc * d;
|
||||
tr_benc * infoDict = NULL;
|
||||
tr_benc * meta = (tr_benc *) meta_in;
|
||||
tr_bool err;
|
||||
tr_bool b;
|
||||
tr_bool isMagnet = FALSE;
|
||||
|
||||
/* info_hash: urlencoded 20-byte SHA1 hash of the value of the info key
|
||||
* from the Metainfo file. Note that the value will be a bencoded
|
||||
* dictionary, given the definition of the info key above. */
|
||||
if( !tr_bencDictFindDict( meta, "info", &beInfo ) )
|
||||
return "info";
|
||||
b = tr_bencDictFindDict( meta, "info", &infoDict );
|
||||
if( hasInfoDict != NULL )
|
||||
*hasInfoDict = b;
|
||||
if( !b )
|
||||
{
|
||||
/* no info dictionary... is this a magnet link? */
|
||||
if( tr_bencDictFindDict( meta, "magnet-info", &d ) )
|
||||
{
|
||||
isMagnet = TRUE;
|
||||
|
||||
/* get the info-hash */
|
||||
if( !tr_bencDictFindRaw( d, "info_hash", &raw, &raw_len ) )
|
||||
return "info_hash";
|
||||
if( raw_len != SHA_DIGEST_LENGTH )
|
||||
return "info_hash";
|
||||
memcpy( inf->hash, raw, raw_len );
|
||||
tr_sha1_to_hex( inf->hashString, inf->hash );
|
||||
escape( inf->hashEscaped, inf->hash, SHA_DIGEST_LENGTH );
|
||||
|
||||
/* maybe get the display name */
|
||||
if( tr_bencDictFindStr( d, "display-name", &str ) ) {
|
||||
tr_free( inf->name );
|
||||
inf->name = tr_strdup( str );
|
||||
}
|
||||
}
|
||||
else /* not a magnet link and has no info dict... */
|
||||
{
|
||||
return "info";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int len;
|
||||
char * bstr = tr_bencToStr( beInfo, TR_FMT_BENC, &len );
|
||||
char * bstr = tr_bencToStr( infoDict, TR_FMT_BENC, &len );
|
||||
tr_sha1( inf->hash, bstr, len, NULL );
|
||||
tr_sha1_to_hex( inf->hashString, inf->hash );
|
||||
escape( inf->hashEscaped, inf->hash, SHA_DIGEST_LENGTH );
|
||||
|
@ -430,13 +461,15 @@ tr_metainfoParseImpl( const tr_session * session,
|
|||
}
|
||||
|
||||
/* name */
|
||||
if( !tr_bencDictFindStr( beInfo, "name.utf-8", &str ) )
|
||||
if( !tr_bencDictFindStr( beInfo, "name", &str ) )
|
||||
str = "";
|
||||
if( !str || !*str )
|
||||
return "name";
|
||||
tr_free( inf->name );
|
||||
inf->name = tr_utf8clean( str, -1, &err );
|
||||
if( !isMagnet ) {
|
||||
if( !tr_bencDictFindStr( infoDict, "name.utf-8", &str ) )
|
||||
if( !tr_bencDictFindStr( infoDict, "name", &str ) )
|
||||
str = "";
|
||||
if( !str || !*str )
|
||||
return "name";
|
||||
tr_free( inf->name );
|
||||
inf->name = tr_utf8clean( str, -1, &err );
|
||||
}
|
||||
|
||||
/* comment */
|
||||
if( !tr_bencDictFindStr( meta, "comment.utf-8", &str ) )
|
||||
|
@ -458,35 +491,42 @@ tr_metainfoParseImpl( const tr_session * session,
|
|||
inf->dateCreated = i;
|
||||
|
||||
/* private */
|
||||
if( !tr_bencDictFindInt( beInfo, "private", &i ) )
|
||||
if( !tr_bencDictFindInt( infoDict, "private", &i ) )
|
||||
if( !tr_bencDictFindInt( meta, "private", &i ) )
|
||||
i = 0;
|
||||
inf->isPrivate = i != 0;
|
||||
|
||||
/* piece length */
|
||||
if( !tr_bencDictFindInt( beInfo, "piece length", &i ) || ( i < 1 ) )
|
||||
return "piece length";
|
||||
inf->pieceSize = i;
|
||||
if( !isMagnet ) {
|
||||
if( !tr_bencDictFindInt( infoDict, "piece length", &i ) || ( i < 1 ) )
|
||||
return "piece length";
|
||||
inf->pieceSize = i;
|
||||
}
|
||||
|
||||
/* pieces */
|
||||
if( !tr_bencDictFindRaw( beInfo, "pieces", &raw,
|
||||
&raw_len ) || ( raw_len % SHA_DIGEST_LENGTH ) )
|
||||
return "pieces";
|
||||
inf->pieceCount = raw_len / SHA_DIGEST_LENGTH;
|
||||
inf->pieces = tr_new0( tr_piece, inf->pieceCount );
|
||||
for( i = 0; i < inf->pieceCount; ++i )
|
||||
memcpy( inf->pieces[i].hash, &raw[i * SHA_DIGEST_LENGTH],
|
||||
SHA_DIGEST_LENGTH );
|
||||
if( !isMagnet ) {
|
||||
if( !tr_bencDictFindRaw( infoDict, "pieces", &raw, &raw_len ) )
|
||||
return "pieces";
|
||||
if( raw_len % SHA_DIGEST_LENGTH )
|
||||
return "pieces";
|
||||
inf->pieceCount = raw_len / SHA_DIGEST_LENGTH;
|
||||
inf->pieces = tr_new0( tr_piece, inf->pieceCount );
|
||||
for( i = 0; i < inf->pieceCount; ++i )
|
||||
memcpy( inf->pieces[i].hash, &raw[i * SHA_DIGEST_LENGTH],
|
||||
SHA_DIGEST_LENGTH );
|
||||
}
|
||||
|
||||
/* files */
|
||||
if( ( str = parseFiles( inf, tr_bencDictFind( beInfo, "files" ),
|
||||
tr_bencDictFind( beInfo, "length" ) ) ) )
|
||||
return str;
|
||||
if( !inf->fileCount || !inf->totalSize )
|
||||
return "files";
|
||||
if( (uint64_t) inf->pieceCount !=
|
||||
( inf->totalSize + inf->pieceSize - 1 ) / inf->pieceSize )
|
||||
return "files";
|
||||
if( !isMagnet ) {
|
||||
if( ( str = parseFiles( inf, tr_bencDictFind( infoDict, "files" ),
|
||||
tr_bencDictFind( infoDict, "length" ) ) ) )
|
||||
return str;
|
||||
if( !inf->fileCount || !inf->totalSize )
|
||||
return "files";
|
||||
if( (uint64_t) inf->pieceCount !=
|
||||
( inf->totalSize + inf->pieceSize - 1 ) / inf->pieceSize )
|
||||
return "files";
|
||||
}
|
||||
|
||||
/* get announce or announce-list */
|
||||
if( ( str = getannounce( inf, meta ) ) )
|
||||
|
@ -504,13 +544,15 @@ tr_metainfoParseImpl( const tr_session * session,
|
|||
|
||||
tr_bool
|
||||
tr_metainfoParse( const tr_session * session,
|
||||
const tr_benc * meta_in,
|
||||
tr_info * inf,
|
||||
tr_bool * hasInfoDict,
|
||||
int * infoDictOffset,
|
||||
int * infoDictLength,
|
||||
const tr_benc * meta_in )
|
||||
int * infoDictLength )
|
||||
{
|
||||
const char * badTag = tr_metainfoParseImpl( session,
|
||||
inf,
|
||||
hasInfoDict,
|
||||
infoDictOffset,
|
||||
infoDictLength,
|
||||
meta_in );
|
||||
|
@ -569,49 +611,3 @@ tr_metainfoRemoveSaved( const tr_session * session,
|
|||
unlink( filename );
|
||||
tr_free( filename );
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
void
|
||||
tr_metainfoSetFromMagnet( tr_info * inf, const tr_magnet_info * m )
|
||||
{
|
||||
/* hash */
|
||||
memcpy( inf->hash, m->hash, 20 );
|
||||
tr_sha1_to_hex( inf->hashString, inf->hash );
|
||||
escape( inf->hashEscaped, inf->hash, SHA_DIGEST_LENGTH );
|
||||
|
||||
/* name */
|
||||
if( m->displayName && *m->displayName )
|
||||
inf->name = tr_strdup( m->displayName );
|
||||
else
|
||||
inf->name = tr_strdup( inf->hashString );
|
||||
|
||||
/* trackers */
|
||||
if(( inf->trackerCount = m->trackerCount ))
|
||||
{
|
||||
int i;
|
||||
const int n = m->trackerCount;
|
||||
|
||||
inf->trackers = tr_new0( tr_tracker_info, n );
|
||||
for( i=0; i<n; ++i ) {
|
||||
const char * url = m->trackers[i];
|
||||
inf->trackers[i].tier = i;
|
||||
inf->trackers[i].announce = tr_strdup( url );
|
||||
inf->trackers[i].scrape = tr_convertAnnounceToScrape( url );
|
||||
inf->trackers[i].id = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* webseeds */
|
||||
if(( inf->webseedCount = m->webseedCount ))
|
||||
{
|
||||
int i;
|
||||
const int n = m->webseedCount;
|
||||
|
||||
inf->webseeds = tr_new0( char*, n );
|
||||
for( i=0; i<n; ++i )
|
||||
inf->webseeds[i] = tr_strdup( m->webseeds[i] );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,13 @@
|
|||
#include "transmission.h"
|
||||
|
||||
struct tr_benc;
|
||||
struct tr_magnet_info;
|
||||
|
||||
tr_bool tr_metainfoParse( const tr_session * session,
|
||||
const struct tr_benc * benc,
|
||||
tr_info * setmeInfo,
|
||||
tr_bool * setmeHasInfoDict,
|
||||
int * setmeInfoDictOffset,
|
||||
int * setmeInfoDictLength,
|
||||
const struct tr_benc * benc );
|
||||
int * setmeInfoDictLength );
|
||||
|
||||
void tr_metainfoRemoveSaved( const tr_session * session,
|
||||
const tr_info * info );
|
||||
|
@ -34,7 +34,5 @@ void tr_metainfoRemoveSaved( const tr_session * session,
|
|||
void tr_metainfoMigrate( tr_session * session,
|
||||
tr_info * inf );
|
||||
|
||||
void tr_metainfoSetFromMagnet( tr_info * inf, const struct tr_magnet_info * m );
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -517,9 +517,12 @@ tr_torrentSaveResume( const tr_torrent * tor )
|
|||
tr_bencDictAddInt( &top, KEY_BANDWIDTH_PRIORITY, tr_torrentGetPriority( tor ) );
|
||||
tr_bencDictAddBool( &top, KEY_PAUSED, !tor->isRunning );
|
||||
savePeers( &top, tor );
|
||||
saveFilePriorities( &top, tor );
|
||||
saveDND( &top, tor );
|
||||
saveProgress( &top, tor );
|
||||
if( tr_torrentHasMetadata( tor ) )
|
||||
{
|
||||
saveFilePriorities( &top, tor );
|
||||
saveDND( &top, tor );
|
||||
saveProgress( &top, tor );
|
||||
}
|
||||
saveSpeedLimits( &top, tor );
|
||||
saveRatioLimits( &top, tor );
|
||||
|
||||
|
|
|
@ -1118,7 +1118,7 @@ torrentAdd( tr_session * session,
|
|||
}
|
||||
else if( !strncmp( fname, "magnet:?", 8 ) )
|
||||
{
|
||||
tr_ctorSetMagnet( ctor, fname );
|
||||
tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -44,8 +44,6 @@ struct tr_ctor
|
|||
tr_benc metainfo;
|
||||
char * sourceFile;
|
||||
|
||||
tr_magnet_info * magnetInfo;
|
||||
|
||||
struct optional_args optionalArgs[2];
|
||||
|
||||
char * incompleteDir;
|
||||
|
@ -106,16 +104,26 @@ tr_ctorGetSourceFile( const tr_ctor * ctor )
|
|||
}
|
||||
|
||||
int
|
||||
tr_ctorSetMagnet( tr_ctor * ctor, const char * uri )
|
||||
tr_ctorSetMetainfoFromMagnetLink( tr_ctor * ctor, const char * magnet_link )
|
||||
{
|
||||
int err;
|
||||
tr_magnet_info * magnet_info = tr_magnetParse( magnet_link );
|
||||
|
||||
if( ctor->magnetInfo != NULL )
|
||||
tr_magnetFree( ctor->magnetInfo );
|
||||
if( magnet_info == NULL )
|
||||
err = -1;
|
||||
else {
|
||||
int len;
|
||||
tr_benc tmp;
|
||||
char * str;
|
||||
|
||||
ctor->magnetInfo = tr_magnetParse( uri );
|
||||
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 );
|
||||
}
|
||||
|
||||
err = ctor->magnetInfo == NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -389,19 +397,6 @@ tr_ctorGetIncompleteDir( const tr_ctor * ctor,
|
|||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tr_ctorGetMagnet( const tr_ctor * ctor, const tr_magnet_info ** setme )
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if( ctor->magnetInfo == NULL )
|
||||
err = 1;
|
||||
else
|
||||
*setme = ctor->magnetInfo;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tr_ctorGetMetainfo( const tr_ctor * ctor,
|
||||
const tr_benc ** setme )
|
||||
|
|
|
@ -138,10 +138,7 @@ tr_torrentGetMetadataPiece( const tr_torrent * tor, int piece, int * len )
|
|||
}
|
||||
|
||||
void
|
||||
tr_torrentSetMetadataPiece( tr_torrent * tor,
|
||||
int piece,
|
||||
const void * data,
|
||||
int len )
|
||||
tr_torrentSetMetadataPiece( tr_torrent * tor, int piece, const void * data, int len )
|
||||
{
|
||||
int i;
|
||||
struct tr_incomplete_metadata * m;
|
||||
|
@ -179,58 +176,48 @@ tr_torrentSetMetadataPiece( tr_torrent * tor,
|
|||
if( m->piecesNeededCount == 0 )
|
||||
{
|
||||
tr_bool success = FALSE;
|
||||
tr_bool checksumPassed = FALSE;
|
||||
tr_bool metainfoParsed = FALSE;
|
||||
uint8_t sha1[SHA_DIGEST_LENGTH];
|
||||
|
||||
/* we've got a complete set of metainfo... see if it passes the checksum test */
|
||||
dbgmsg( tor, "metainfo piece %d was the last one", piece );
|
||||
tr_sha1( sha1, m->metadata, m->metadata_size, NULL );
|
||||
if( !memcmp( sha1, tor->info.hash, SHA_DIGEST_LENGTH ) )
|
||||
if(( checksumPassed = !memcmp( sha1, tor->info.hash, SHA_DIGEST_LENGTH )))
|
||||
{
|
||||
int err;
|
||||
tr_benc dict;
|
||||
struct evbuffer * buf = evbuffer_new( );
|
||||
dbgmsg( tor, "metadata checksum passed! (length: %d)", (int)m->metadata_size );
|
||||
|
||||
/* add a wrapper dictionary to the benc.
|
||||
* include the announce-list too,
|
||||
* so we can save it in the .torrent for future sessions */
|
||||
evbuffer_add_printf( buf, "d" );
|
||||
evbuffer_add_printf( buf, "13:announce-list" );
|
||||
evbuffer_add_printf( buf, "l" );
|
||||
for( i=0; i<tor->info.trackerCount; ++i ) {
|
||||
const char * url = tor->info.trackers[i].announce;
|
||||
evbuffer_add_printf( buf, "l%zu:%se", strlen( url ), url );
|
||||
}
|
||||
evbuffer_add_printf( buf, "e" );
|
||||
evbuffer_add_printf( buf, "4:info" );
|
||||
evbuffer_add( buf, m->metadata, m->metadata_size );
|
||||
evbuffer_add_printf( buf, "e" );
|
||||
|
||||
/* does it parse? */
|
||||
err = tr_bencLoad( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ), &dict, NULL );
|
||||
/* checksum passed; now try to parse it as benc */
|
||||
tr_benc infoDict;
|
||||
const int err = tr_bencLoad( m->metadata, m->metadata_size, &infoDict, NULL );
|
||||
dbgmsg( tor, "err is %d", err );
|
||||
if( !err )
|
||||
if(( metainfoParsed = !err ))
|
||||
{
|
||||
if( tr_metainfoParse( tor->session,
|
||||
&tor->info,
|
||||
&tor->infoDictOffset,
|
||||
&tor->infoDictLength,
|
||||
&dict ) )
|
||||
/* yay we have bencoded metainfo... merge it into our .torrnet file */
|
||||
tr_benc newMetainfo;
|
||||
const char * path = tor->info.torrent;
|
||||
if( !tr_bencLoadFile( &newMetainfo, TR_FMT_BENC, path ) )
|
||||
{
|
||||
const char * path = tor->info.torrent;
|
||||
dbgmsg( tor, "saving completed metadata to \"%s\"", path );
|
||||
tr_bool hasInfo;
|
||||
tr_benc * tmp;
|
||||
|
||||
dbgmsg( tor, "Saving completed metadata to \"%s\"", path );
|
||||
assert( !tr_bencDictFindDict( &newMetainfo, "info", &tmp ) );
|
||||
tr_bencMergeDicts( tr_bencDictAddDict( &newMetainfo, "info", 0 ), &infoDict );
|
||||
tr_bencToFile( &newMetainfo, TR_FMT_BENC, path );
|
||||
|
||||
success = tr_metainfoParse( tor->session, &newMetainfo, &tor->info,
|
||||
&hasInfo, &tor->infoDictOffset, &tor->infoDictLength );
|
||||
|
||||
assert( hasInfo );
|
||||
assert( success );
|
||||
|
||||
success = TRUE;
|
||||
tr_torrentGotNewInfoDict( tor );
|
||||
|
||||
tr_bencToFile( &dict, TR_FMT_BENC, path );
|
||||
tr_sessionSetTorrentFile( tor->session,
|
||||
tor->info.hashString, path );
|
||||
tr_torrentSetDirty( tor );
|
||||
|
||||
tr_bencFree( &newMetainfo );
|
||||
}
|
||||
|
||||
tr_bencFree( &dict );
|
||||
|
||||
tr_bencFree( &infoDict );
|
||||
}
|
||||
|
||||
evbuffer_free( buf );
|
||||
}
|
||||
|
||||
if( success )
|
||||
|
@ -241,12 +228,16 @@ tr_torrentSetMetadataPiece( tr_torrent * tor,
|
|||
else /* drat. */
|
||||
{
|
||||
const int n = m->pieceCount;
|
||||
for( i=0; i<n; ++i ) {
|
||||
for( i=0; i<n; ++i )
|
||||
{
|
||||
m->piecesNeeded[i].piece = i;
|
||||
m->piecesNeeded[i].requestedAt = 0;
|
||||
}
|
||||
m->piecesNeededCount = n;
|
||||
dbgmsg( tor, "metadata error; trying again. %d pieces left", n );
|
||||
|
||||
tr_err( "magnet status: checksum passed %d, metainfo parsed %d",
|
||||
(int)checksumPassed, (int)metainfoParsed );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -712,10 +712,11 @@ torrentInit( tr_torrent * tor, const tr_ctor * ctor )
|
|||
|
||||
static tr_parse_result
|
||||
torrentParseImpl( const tr_ctor * ctor, tr_info * setmeInfo,
|
||||
int * dictOffset, int * dictLength )
|
||||
tr_bool * setmeHasInfo, int * dictOffset, int * dictLength )
|
||||
{
|
||||
int doFree;
|
||||
tr_bool didParse;
|
||||
tr_bool hasInfo = FALSE;
|
||||
tr_info tmp;
|
||||
const tr_benc * metainfo;
|
||||
tr_session * session = tr_ctorGetSession( ctor );
|
||||
|
@ -728,13 +729,14 @@ torrentParseImpl( const tr_ctor * ctor, tr_info * setmeInfo,
|
|||
if( tr_ctorGetMetainfo( ctor, &metainfo ) )
|
||||
return TR_PARSE_ERR;
|
||||
|
||||
didParse = tr_metainfoParse( session, setmeInfo, dictOffset, dictLength, metainfo );
|
||||
didParse = tr_metainfoParse( session, metainfo, setmeInfo,
|
||||
&hasInfo, dictOffset, dictLength );
|
||||
doFree = didParse && ( setmeInfo == &tmp );
|
||||
|
||||
if( !didParse )
|
||||
result = TR_PARSE_ERR;
|
||||
|
||||
if( didParse && !getBlockSize( setmeInfo->pieceSize ) )
|
||||
if( didParse && hasInfo && !getBlockSize( setmeInfo->pieceSize ) )
|
||||
result = TR_PARSE_ERR;
|
||||
|
||||
if( didParse && session && tr_torrentExists( session, setmeInfo->hash ) )
|
||||
|
@ -743,56 +745,46 @@ torrentParseImpl( const tr_ctor * ctor, tr_info * setmeInfo,
|
|||
if( doFree )
|
||||
tr_metainfoFree( setmeInfo );
|
||||
|
||||
if( setmeHasInfo != NULL )
|
||||
*setmeHasInfo = hasInfo;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
tr_parse_result
|
||||
tr_torrentParse( const tr_ctor * ctor, tr_info * setmeInfo )
|
||||
{
|
||||
return torrentParseImpl( ctor, setmeInfo, NULL, NULL );
|
||||
return torrentParseImpl( ctor, setmeInfo, NULL, NULL, NULL );
|
||||
}
|
||||
|
||||
tr_torrent *
|
||||
tr_torrentNew( const tr_ctor * ctor, int * setmeError )
|
||||
{
|
||||
int off, len;
|
||||
tr_bool hasInfo;
|
||||
tr_info tmpInfo;
|
||||
tr_parse_result r;
|
||||
tr_torrent * tor = NULL;
|
||||
const tr_magnet_info * magnetInfo;
|
||||
tr_session * session = tr_ctorGetSession( ctor );
|
||||
|
||||
assert( ctor != NULL );
|
||||
assert( tr_isSession( session ) );
|
||||
|
||||
if( !tr_ctorGetMagnet( ctor, &magnetInfo ) )
|
||||
r = torrentParseImpl( ctor, &tmpInfo, &hasInfo, &off, &len );
|
||||
if( r == TR_PARSE_OK )
|
||||
{
|
||||
if( tr_torrentFindFromHash( session, magnetInfo->hash ) != NULL )
|
||||
tor = tr_new0( tr_torrent, 1 );
|
||||
tor->info = tmpInfo;
|
||||
if( hasInfo )
|
||||
{
|
||||
if( setmeError )
|
||||
*setmeError = TR_PARSE_DUPLICATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
tor = tr_new0( tr_torrent, 1 );
|
||||
tr_metainfoSetFromMagnet( &tor->info, magnetInfo );
|
||||
torrentInit( tor, ctor );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int off, len;
|
||||
tr_parse_result r = torrentParseImpl( ctor, &tmpInfo, &off, &len );
|
||||
if( r == TR_PARSE_OK )
|
||||
{
|
||||
tor = tr_new0( tr_torrent, 1 );
|
||||
tor->info = tmpInfo;
|
||||
tor->infoDictOffset = off;
|
||||
tor->infoDictLength = len;
|
||||
torrentInit( tor, ctor );
|
||||
}
|
||||
else if( setmeError )
|
||||
{
|
||||
*setmeError = r;
|
||||
}
|
||||
torrentInit( tor, ctor );
|
||||
}
|
||||
else if( setmeError )
|
||||
{
|
||||
*setmeError = r;
|
||||
}
|
||||
|
||||
return tor;
|
||||
|
@ -1469,7 +1461,7 @@ tr_torrentSave( tr_torrent * tor )
|
|||
{
|
||||
assert( tr_isTorrent( tor ) );
|
||||
|
||||
if( tor->isDirty && tr_torrentHasMetadata( tor ) )
|
||||
if( tor->isDirty )
|
||||
{
|
||||
tor->isDirty = FALSE;
|
||||
tr_torrentSaveResume( tor );
|
||||
|
@ -2131,7 +2123,8 @@ tr_torrentSetAnnounceList( tr_torrent * tor,
|
|||
/* save to the .torrent file */
|
||||
if( ok && !tr_bencLoadFile( &metainfo, TR_FMT_BENC, tor->info.torrent ) )
|
||||
{
|
||||
tr_info tmpInfo;
|
||||
tr_bool hasInfo;
|
||||
tr_info tmpInfo;
|
||||
|
||||
/* remove the old fields */
|
||||
tr_bencDictRemove( &metainfo, "announce" );
|
||||
|
@ -2160,10 +2153,8 @@ tr_torrentSetAnnounceList( tr_torrent * tor,
|
|||
|
||||
/* try to parse it back again, to make sure it's good */
|
||||
memset( &tmpInfo, 0, sizeof( tr_info ) );
|
||||
if( tr_metainfoParse( tor->session, &tmpInfo,
|
||||
&tor->infoDictOffset,
|
||||
&tor->infoDictLength,
|
||||
&metainfo ) )
|
||||
if( tr_metainfoParse( tor->session, &metainfo, &tmpInfo,
|
||||
&hasInfo, &tor->infoDictOffset, &tor->infoDictLength ) )
|
||||
{
|
||||
/* it's good, so keep these new trackers and free the old ones */
|
||||
|
||||
|
|
|
@ -34,8 +34,6 @@ void tr_ctorSetSave( tr_ctor * ctor,
|
|||
|
||||
int tr_ctorGetSave( const tr_ctor * ctor );
|
||||
|
||||
int tr_ctorGetMagnet( const tr_ctor * ctor, const struct tr_magnet_info ** setme );
|
||||
|
||||
void tr_ctorInitTorrentPriorities( const tr_ctor * ctor, tr_torrent * tor );
|
||||
|
||||
void tr_ctorInitTorrentWanted( const tr_ctor * ctor, tr_torrent * tor );
|
||||
|
|
|
@ -879,8 +879,8 @@ void tr_ctorFree( tr_ctor * ctor );
|
|||
/** @brief Set whether or not to delete the source .torrent file when a torrent is added. (Default: False) */
|
||||
void tr_ctorSetDeleteSource( tr_ctor * ctor, tr_bool doDelete );
|
||||
|
||||
/** @brief Set the link for creating a tr_torrent from a magnet link */
|
||||
int tr_ctorSetMagnet( tr_ctor * ctor, const char * magnet_link );
|
||||
/** @brief Set the constructor's metainfo from a magnet link */
|
||||
int tr_ctorSetMetainfoFromMagnetLink( tr_ctor * ctor, const char * magnet_link );
|
||||
|
||||
/** @brief Set the constructor's metainfo from a raw benc already in memory */
|
||||
int tr_ctorSetMetainfo( tr_ctor * ctor, const uint8_t * metainfo, size_t len );
|
||||
|
|
|
@ -1563,7 +1563,7 @@ int trashDataFile(const char * filename)
|
|||
result = tr_ctorSetMetainfoFromFile(ctor, [path UTF8String]);
|
||||
|
||||
if (result != TR_PARSE_OK && magnetAddress)
|
||||
result = tr_ctorSetMagnet(ctor, [magnetAddress UTF8String]);
|
||||
result = tr_ctorSetMetainfoFromMagnetLink(ctor, [magnetAddress UTF8String]);
|
||||
|
||||
//backup - shouldn't be needed after upgrade to 1.70
|
||||
if (result != TR_PARSE_OK && hashString)
|
||||
|
|
Loading…
Reference in New Issue