(trunk) #2096: Magnet, BEP #9 support

This commit is contained in:
Charles Kerr 2009-11-24 02:16:31 +00:00
parent 1cbbcf9fa1
commit f69e335291
23 changed files with 967 additions and 140 deletions

View File

@ -492,15 +492,15 @@ onAddURLResponse( GtkDialog * dialog, int response, gpointer user_data )
}
else
{
GtkWidget * w = gtk_message_dialog_new( GTK_WINDOW( dialog ),
GTK_DIALOG_MODAL,
GtkWidget * w = gtk_message_dialog_new( GTK_WINDOW( dialog ), 0,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"%s", _( "Unrecognized URL" ) );
gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ),
_( "Transmission doesn't know how to use \"%s\"" ), url );
gtk_dialog_run( GTK_DIALOG( w ) );
gtk_widget_destroy( w );
g_signal_connect_swapped( w, "response",
G_CALLBACK( gtk_widget_destroy ), w );
gtk_widget_show( w );
destroy = FALSE;
}
}

View File

@ -230,7 +230,9 @@ icon_cache_get_mime_type_icon( IconCache * icon_cache,
GdkPixbuf * pixbuf;
const char * stock_name;
if( !strcmp( mime_type, DIRECTORY_MIME_TYPE ) )
if( !strcmp( mime_type, UNKNOWN_MIME_TYPE ) )
stock_name = GTK_STOCK_MISSING_IMAGE;
else if( !strcmp( mime_type, DIRECTORY_MIME_TYPE ) )
stock_name = GTK_STOCK_DIRECTORY;
else
stock_name = GTK_STOCK_FILE;

View File

@ -7,6 +7,7 @@
#define ICONS_H
#define DIRECTORY_MIME_TYPE "folder"
#define UNKNOWN_MIME_TYPE "unknown"
const char * get_mime_type_from_filename( const char *file );

View File

@ -306,7 +306,9 @@ get_icon( const tr_torrent * tor, GtkIconSize icon_size, GtkWidget * for_widget
const char * mime_type;
const tr_info * info = tr_torrentInfo( tor );
if( info->fileCount > 1 )
if( info->fileCount == 0 )
mime_type = UNKNOWN_MIME_TYPE;
else if( info->fileCount > 1 )
mime_type = DIRECTORY_MIME_TYPE;
else
mime_type = get_mime_type_from_filename( info->files[0].name );

View File

@ -1023,15 +1023,40 @@ onURLDone( tr_session * session,
void
tr_core_add_from_url( TrCore * core, const char * url )
{
tr_session * session = tr_core_session( core );
if( gtr_is_magnet_link( url ) )
{
g_message( "FIXME: magnet link \"%s\" not handled", url );
tr_ctor * ctor = tr_ctorNew( session );
int err = tr_ctorSetMagnet( ctor, url );
if( !err )
{
tr_session * session = tr_core_session( core );
TrTorrent * gtor = tr_torrent_new_ctor( session, ctor, &err );
if( !err )
tr_core_add_torrent( core, gtor, FALSE );
else
g_message( "tr_torrent_new_ctor err %d", err );
}
else
{
GtkWidget * w = gtk_message_dialog_new( NULL, 0,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"%s", _( "Unrecognized URL" ) );
gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ),
_( "Transmission doesn't know how to use \"%s\"" ), url );
g_signal_connect_swapped( w, "response",
G_CALLBACK( gtk_widget_destroy ), w );
gtk_widget_show( w );
tr_ctorFree( ctor );
}
}
else
{
struct url_dialog_data * data = g_new( struct url_dialog_data, 1 );
data->core = core;
tr_webRun( tr_core_session( core ), url, NULL, onURLDone, data );
tr_webRun( session, url, NULL, onURLDone, data );
}
}
@ -1051,6 +1076,7 @@ add_filename( TrCore * core,
if( session == NULL )
return;
g_message( "filename [%s]", filename );
if( gtr_is_supported_url( filename ) || gtr_is_magnet_link( filename ) )
{
tr_core_add_from_url( core, filename );

View File

@ -346,7 +346,7 @@ checkfilenames( int argc, char **argv )
for( i=0; i<argc; ++i )
{
if( gtr_is_supported_url( argv[i] ) )
if( gtr_is_supported_url( argv[i] ) || gtr_is_magnet_link( argv[i] ) )
{
ret = g_slist_prepend( ret, g_strdup( argv[i] ) );
}

View File

@ -47,6 +47,7 @@ libtransmission_a_SOURCES = \
stats.c \
torrent.c \
torrent-ctor.c \
torrent-magnet.c \
tr-dht.c \
tr-getopt.c \
trevent.c \
@ -94,6 +95,7 @@ noinst_HEADERS = \
session.h \
stats.h \
torrent.h \
torrent-magnet.h \
tr-getopt.h \
transmission.h \
tr-dht.h \

View File

@ -16,6 +16,7 @@
#include "transmission.h"
#include "completion.h"
#include "torrent.h"
#include "torrent-magnet.h"
#include "utils.h"
static void
@ -240,6 +241,7 @@ tr_cpBlockBitfieldSet( tr_completion * cp, tr_bitfield * blockBitfield )
tr_completeness
tr_cpGetStatus( const tr_completion * cp )
{
if( !tr_torrentHasMetadata( cp->tor ) ) return TR_LEECH;
if( cp->sizeNow == cp->tor->info.totalSize ) return TR_SEED;
if( cp->sizeNow == tr_cpSizeWhenDone( cp ) ) return TR_PARTIAL_SEED;
return TR_LEECH;
@ -255,6 +257,9 @@ calculateHaveValid( const tr_completion * ccp )
const uint64_t lastPieceSize = tor->lastPieceSize;
const tr_piece_index_t lastPiece = tor->info.pieceCount - 1;
if( !tr_torrentHasMetadata( tor ) )
return 0;
for( i=0; i!=lastPiece; ++i )
if( tr_cpPieceIsComplete( ccp, i ) )
b += pieceSize;

View File

@ -37,6 +37,7 @@
#include "session.h"
#include "bencode.h"
#include "crypto.h" /* tr_sha1 */
#include "magnet.h"
#include "metainfo.h"
#include "platform.h"
#include "utils.h"
@ -227,7 +228,7 @@ parseFiles( tr_info * inf,
}
static char *
announceToScrape( const char * announce )
tr_convertAnnounceToScrape( const char * announce )
{
char * scrape = NULL;
const char * s;
@ -294,7 +295,7 @@ getannounce( tr_info * inf,
tr_tracker_info * t = trackers + trackerCount++;
t->tier = validTiers;
t->announce = tr_strdup( url );
t->scrape = announceToScrape( url );
t->scrape = tr_convertAnnounceToScrape( url );
anyAdded = TRUE;
}
@ -324,7 +325,7 @@ getannounce( tr_info * inf,
trackers = tr_new0( tr_tracker_info, 1 );
trackers[trackerCount].tier = 0;
trackers[trackerCount].announce = tr_strdup( url );
trackers[trackerCount++].scrape = announceToScrape( url );
trackers[trackerCount++].scrape = tr_convertAnnounceToScrape( url );
/*fprintf( stderr, "single announce: [%s]\n", url );*/
}
tr_free( url );
@ -381,8 +382,10 @@ escape( char * out, const uint8_t * in, size_t in_len ) /* rfc2396 */
static const char*
tr_metainfoParseImpl( const tr_session * session,
tr_info * inf,
const tr_benc * meta_in )
tr_info * inf,
int * infoDictOffset,
int * infoDictLength,
const tr_benc * meta_in )
{
int64_t i;
size_t raw_len;
@ -404,6 +407,22 @@ tr_metainfoParseImpl( const tr_session * session,
tr_sha1( inf->hash, bstr, len, NULL );
tr_sha1_to_hex( inf->hashString, inf->hash );
escape( inf->hashEscaped, inf->hash, SHA_DIGEST_LENGTH );
if( infoDictLength != NULL )
*infoDictLength = len;
if( infoDictOffset != NULL )
{
int mlen = 0;
char * mstr = tr_bencToStr( meta_in, TR_FMT_BENC, &mlen );
const char * offset = tr_memmem( mstr, mlen, bstr, len );
if( offset != NULL )
*infoDictOffset = offset - mstr;
tr_free( mstr );
if( offset == NULL )
return "info";
}
tr_free( bstr );
}
@ -483,9 +502,15 @@ tr_metainfoParseImpl( const tr_session * session,
tr_bool
tr_metainfoParse( const tr_session * session,
tr_info * inf,
int * infoDictOffset,
int * infoDictLength,
const tr_benc * meta_in )
{
const char * badTag = tr_metainfoParseImpl( session, inf, meta_in );
const char * badTag = tr_metainfoParseImpl( session,
inf,
infoDictOffset,
infoDictLength,
meta_in );
const tr_bool success = badTag == NULL;
if( badTag )
@ -500,8 +525,8 @@ tr_metainfoParse( const tr_session * session,
void
tr_metainfoFree( tr_info * inf )
{
int i;
tr_file_index_t ff;
int i;
for( i = 0; i < inf->webseedCount; ++i )
tr_free( inf->webseeds[i] );
@ -542,3 +567,37 @@ tr_metainfoRemoveSaved( const tr_session * session,
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 )
inf->name = tr_strdup( m->displayName );
else
inf->name = tr_strdup( inf->hashString );
/* trackers */
if( m->announceCount > 0 )
{
int i;
const int n = m->announceCount;
inf->trackerCount = n;
inf->trackers = tr_new0( tr_tracker_info, n );
for( i=0; i<n; ++i ) {
const char * url = m->announceURLs[i];
inf->trackers[i].tier = i;
inf->trackers[i].announce = tr_strdup( url );
inf->trackers[i].scrape = tr_convertAnnounceToScrape( url );
}
}
}

View File

@ -23,7 +23,7 @@
*****************************************************************************/
#ifndef __TRANSMISSION__
#error only libtransmission should #include this header.
#error only libtransmission should #include this header.
#endif
#ifndef TR_METAINFO_H
@ -32,9 +32,12 @@
#include "transmission.h"
struct tr_benc;
struct tr_magnet_info;
tr_bool tr_metainfoParse( const tr_session * session,
tr_info * info,
tr_info * setmeInfo,
int * setmeInfoDictLength,
int * setmeInfoDictOffset,
const struct tr_benc * benc );
void tr_metainfoRemoveSaved( const tr_session * session,
@ -43,4 +46,7 @@ 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

View File

@ -343,6 +343,7 @@ static tr_peer*
peerConstructor( struct peer_atom * atom )
{
tr_peer * peer = tr_new0( tr_peer, 1 );
tr_bitsetConstructor( &peer->have, 0 );
peer->atom = atom;
return peer;
}
@ -383,7 +384,7 @@ peerDestructor( Torrent * t, tr_peer * peer )
tr_peerIoClear( peer->io );
tr_peerIoUnref( peer->io ); /* balanced by the ref in handshakeDoneCB() */
tr_bitfieldFree( peer->have );
tr_bitsetDestructor( &peer->have );
tr_bitfieldFree( peer->blame );
tr_free( peer->client );
@ -906,7 +907,7 @@ tr_peerMgrGetNextRequests( tr_torrent * tor,
int got;
Torrent * t;
struct weighted_piece * pieces;
const tr_bitfield * have = peer->have;
const tr_bitset * have = &peer->have;
const time_t now = time( NULL );
/* sanity clause */
@ -928,7 +929,7 @@ tr_peerMgrGetNextRequests( tr_torrent * tor,
struct weighted_piece * p = pieces + i;
/* if the peer has this piece that we want... */
if( tr_bitfieldHasFast( have, p->index ) )
if( tr_bitsetHasFast( have, p->index ) )
{
tr_block_index_t b = tr_torPieceFirstBlock( tor, p->index );
const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, p->index );
@ -1941,7 +1942,7 @@ tr_peerMgrTorrentAvailability( const tr_torrent * tor,
else if( peerCount ) {
int j;
for( j = 0; j < peerCount; ++j )
if( tr_bitfieldHas( peers[j]->have, i ) )
if( tr_bitsetHas( &peers[j]->have, i ) )
++tab[i];
}
}
@ -1964,7 +1965,7 @@ tr_peerMgrGetAvailable( const tr_torrent * tor )
peerCount = tr_ptrArraySize( &t->peers );
peers = (const tr_peer**) tr_ptrArrayBase( &t->peers );
for( i=0; i<peerCount; ++i )
tr_bitfieldOr( pieces, peers[i]->have );
tr_bitsetOr( pieces, &peers[i]->have );
managerUnlock( t->manager );
return pieces;
@ -2351,7 +2352,7 @@ shouldPeerBeClosed( const Torrent * t,
peerHasEverything = FALSE;
else {
tr_bitfield * tmp = tr_bitfieldDup( tr_cpPieceBitfield( &tor->completion ) );
tr_bitfieldDifference( tmp, peer->have );
tr_bitsetDifference( tmp, &peer->have );
peerHasEverything = tr_bitfieldCountTrueBits( tmp ) == 0;
tr_bitfieldFree( tmp );
}

View File

@ -24,6 +24,7 @@
#endif
#include "bitfield.h"
#include "bitset.h"
#include "net.h"
#include "peer-common.h" /* struct peer_request */
#include "publish.h" /* tr_publisher_tag */
@ -93,7 +94,7 @@ typedef struct tr_peer
struct peer_atom * atom;
struct tr_bitfield * blame;
struct tr_bitfield * have;
struct tr_bitset have;
/** how complete the peer's copy of the torrent is. [0.0...1.0] */
float progress;

View File

@ -35,6 +35,7 @@
#include "session.h"
#include "stats.h"
#include "torrent.h"
#include "torrent-magnet.h"
#include "tr-dht.h"
#include "utils.h"
#include "version.h"
@ -66,11 +67,12 @@ enum
LTEP_HANDSHAKE = 0,
TR_LTEP_PEX = 1,
UT_PEX_ID = 1,
UT_METADATA_ID = 3,
MAX_PEX_PEER_COUNT = 50,
MIN_CHOKE_PERIOD_SEC = ( 10 ),
MIN_CHOKE_PERIOD_SEC = 10,
/* idle seconds before we send a keepalive */
KEEPALIVE_INTERVAL_SECS = 100,
@ -79,6 +81,8 @@ enum
REQQ = 512,
METADATA_REQQ = 64,
MAX_BLOCK_SIZE = ( 1024 * 16 ),
/* used in lowering the outMessages queue period */
@ -91,7 +95,12 @@ enum
LAZY_PIECE_COUNT = 26,
/* number of pieces we'll allow in our fast set */
MAX_FAST_SET_SIZE = 3
MAX_FAST_SET_SIZE = 3,
/* defined in BEP #9 */
METADATA_MSG_TYPE_REQUEST = 0,
METADATA_MSG_TYPE_DATA = 1,
METADATA_MSG_TYPE_REJECT = 2
};
enum
@ -165,8 +174,11 @@ struct tr_incoming
struct tr_peermsgs
{
tr_bool peerSupportsPex;
tr_bool peerSupportsMetadataXfer;
tr_bool clientSentLtepHandshake;
tr_bool peerSentLtepHandshake;
tr_bool requestingMetadataFromPeer;
/*tr_bool haveFastSet;*/
int activeRequestCount;
@ -181,6 +193,7 @@ struct tr_peermsgs
uint8_t state;
uint8_t ut_pex_id;
uint8_t ut_metadata_id;
uint16_t pexCount;
uint16_t pexCount6;
@ -199,7 +212,10 @@ struct tr_peermsgs
struct peer_request peerAskedFor[REQQ];
int peerAskedForCount;
int peerAskedForMetadata[METADATA_REQQ];
int peerAskedForMetadataCount;
tr_pex * pex;
tr_pex * pex6;
@ -219,6 +235,20 @@ struct tr_peermsgs
struct event pexTimer;
};
/**
***
**/
#if 0
static tr_bitfield*
getHave( const struct tr_peermsgs * msgs )
{
if( msgs->peer->have == NULL )
msgs->peer->have = tr_bitfieldNew( msgs->torrent->info.pieceCount );
return msgs->peer->have;
}
#endif
static TR_INLINE tr_session*
getSession( struct tr_peermsgs * msgs )
{
@ -654,9 +684,9 @@ isPieceInteresting( const tr_peermsgs * msgs,
{
const tr_torrent * torrent = msgs->torrent;
return ( !torrent->info.pieces[piece].dnd ) /* we want it */
return ( !torrent->info.pieces[piece].dnd ) /* we want it */
&& ( !tr_cpPieceIsComplete( &torrent->completion, piece ) ) /* !have */
&& ( tr_bitfieldHas( msgs->peer->have, piece ) ); /* peer has it */
&& ( tr_bitsetHas( &msgs->peer->have, piece ) ); /* peer has it */
}
/* "interested" means we'll ask for piece data if they unchoke us */
@ -677,11 +707,6 @@ isPeerInteresting( const tr_peermsgs * msgs )
torrent = msgs->torrent;
bitfield = tr_cpPieceBitfield( &torrent->completion );
if( !msgs->peer->have )
return TRUE;
assert( bitfield->byteCount == msgs->peer->have->byteCount );
for( i = 0; i < torrent->info.pieceCount; ++i )
if( isPieceInteresting( msgs, i ) )
return TRUE;
@ -716,6 +741,21 @@ updateInterest( tr_peermsgs * msgs )
sendInterest( msgs, i );
}
static tr_bool
popNextMetadataRequest( tr_peermsgs * msgs, int * piece )
{
if( msgs->peerAskedForMetadataCount == 0 )
return FALSE;
*piece = msgs->peerAskedForMetadata[0];
memmove( msgs->peerAskedForMetadata,
msgs->peerAskedForMetadata + 1,
sizeof( int ) * --msgs->peerAskedForMetadataCount );
return TRUE;
}
static tr_bool
popNextRequest( tr_peermsgs * msgs, struct peer_request * setme )
{
@ -819,7 +859,8 @@ sendLtepHandshake( tr_peermsgs * msgs )
tr_benc val, *m;
char * buf;
int len;
int pex;
tr_bool allow_pex;
tr_bool allow_metadata_xfer;
struct evbuffer * out = msgs->outMessages;
const unsigned char * ipv6 = tr_globalIPv6();
@ -829,25 +870,37 @@ sendLtepHandshake( tr_peermsgs * msgs )
dbgmsg( msgs, "sending an ltep handshake" );
msgs->clientSentLtepHandshake = 1;
/* decide if we want to advertise metadata xfer support (BEP 9) */
if( tr_torrentIsPrivate( msgs->torrent ) )
allow_metadata_xfer = 0;
else
allow_metadata_xfer = 1;
/* decide if we want to advertise pex support */
if( !tr_torrentAllowsPex( msgs->torrent ) )
pex = 0;
allow_pex = 0;
else if( msgs->peerSentLtepHandshake )
pex = msgs->peerSupportsPex ? 1 : 0;
allow_pex = msgs->peerSupportsPex ? 1 : 0;
else
pex = 1;
allow_pex = 1;
tr_bencInitDict( &val, 7 );
tr_bencInitDict( &val, 8 );
tr_bencDictAddInt( &val, "e", getSession(msgs)->encryptionMode != TR_CLEAR_PREFERRED );
if( ipv6 )
if( ipv6 != NULL )
tr_bencDictAddRaw( &val, "ipv6", ipv6, 16 );
if( allow_metadata_xfer && tr_torrentHasMetadata( msgs->torrent )
&& ( msgs->torrent->infoDictLength > 0 ) )
tr_bencDictAddInt( &val, "metadata_size", msgs->torrent->infoDictLength );
tr_bencDictAddInt( &val, "p", tr_sessionGetPeerPort( getSession(msgs) ) );
tr_bencDictAddInt( &val, "reqq", REQQ );
tr_bencDictAddInt( &val, "upload_only", tr_torrentIsSeed( msgs->torrent ) );
tr_bencDictAddStr( &val, "v", TR_NAME " " USERAGENT_PREFIX );
m = tr_bencDictAddDict( &val, "m", 1 );
if( pex )
tr_bencDictAddInt( m, "ut_pex", TR_LTEP_PEX );
m = tr_bencDictAddDict( &val, "m", 2 );
if( allow_metadata_xfer )
tr_bencDictAddInt( m, "ut_metadata", UT_METADATA_ID );
if( allow_pex )
tr_bencDictAddInt( m, "ut_pex", UT_PEX_ID );
buf = tr_bencToStr( &val, TR_FMT_BENC, &len );
tr_peerIoWriteUint32( msgs->peer->io, out, 2 * sizeof( uint8_t ) + len );
@ -898,14 +951,25 @@ parseLtepHandshake( tr_peermsgs * msgs,
/* check supported messages for utorrent pex */
msgs->peerSupportsPex = 0;
msgs->peerSupportsMetadataXfer = 0;
if( tr_bencDictFindDict( &val, "m", &sub ) ) {
if( tr_bencDictFindInt( sub, "ut_pex", &i ) ) {
msgs->peerSupportsPex = i != 0;
msgs->ut_pex_id = (uint8_t) i;
msgs->peerSupportsPex = msgs->ut_pex_id == 0 ? 0 : 1;
dbgmsg( msgs, "msgs->ut_pex is %d", (int)msgs->ut_pex_id );
}
if( tr_bencDictFindInt( sub, "ut_metadata", &i ) ) {
msgs->peerSupportsMetadataXfer = i != 0;
msgs->ut_metadata_id = (uint8_t) i;
dbgmsg( msgs, "msgs->ut_metadata_id is %d", (int)msgs->ut_metadata_id );
}
}
/* look for metainfo size (BEP 9) */
if( tr_bencDictFindInt( &val, "metadata_size", &i ) )
tr_torrentSetMetadataSizeHint( msgs->torrent, i );
/* look for upload_only (BEP 21) */
if( tr_bencDictFindInt( &val, "upload_only", &i ) ) {
fireUploadOnly( msgs, i!=0 );
@ -925,7 +989,7 @@ parseLtepHandshake( tr_peermsgs * msgs,
memcpy( &pex.addr.addr.addr4, addr, 4 );
tr_peerMgrAddPex( msgs->torrent, TR_PEER_FROM_ALT, &pex );
}
if( tr_bencDictFindRaw( &val, "ipv6", &addr, &addr_len) && addr_len == 16 ) {
pex.addr.type = TR_AF_INET6;
memcpy( &pex.addr.addr.addr6, addr, 16 );
@ -940,6 +1004,86 @@ parseLtepHandshake( tr_peermsgs * msgs,
tr_free( tmp );
}
static void
parseUtMetadata( tr_peermsgs * msgs, int msglen, struct evbuffer * inbuf )
{
tr_benc dict;
char * msg_end;
char * benc_end;
int64_t msg_type = -1;
int64_t piece = -1;
int64_t total_size = 0;
uint8_t * tmp = tr_new( uint8_t, msglen );
tr_peerIoReadBytes( msgs->peer->io, inbuf, tmp, msglen );
msg_end = (char*)tmp + msglen;
if( !tr_bencLoad( tmp, msglen, &dict, &benc_end ) )
{
tr_bencDictFindInt( &dict, "msg_type", &msg_type );
tr_bencDictFindInt( &dict, "piece", &piece );
tr_bencDictFindInt( &dict, "total_size", &total_size );
tr_bencFree( &dict );
}
dbgmsg( msgs, "got ut_metadata msg: type %d, piece %d, total_size %d",
(int)msg_type, (int)piece, (int)total_size );
if( msg_type == METADATA_MSG_TYPE_REJECT )
{
/* NOOP */
}
if( ( msg_type == METADATA_MSG_TYPE_DATA )
&& ( !tr_torrentHasMetadata( msgs->torrent ) )
&& ( msg_end - benc_end <= METADATA_PIECE_SIZE )
&& ( piece * METADATA_PIECE_SIZE + (msg_end - benc_end) <= total_size ) )
{
const int pieceLen = msg_end - benc_end;
dbgmsg( msgs, "got a metadata piece... calling tr_torrentSetMetadataPiece" );
msgs->requestingMetadataFromPeer = FALSE;
tr_torrentSetMetadataPiece( msgs->torrent, piece, benc_end, pieceLen );
}
if( msg_type == METADATA_MSG_TYPE_REQUEST )
{
if( ( piece >= 0 )
&& tr_torrentHasMetadata( msgs->torrent )
&& !tr_torrentIsPrivate( msgs->torrent )
&& ( msgs->peerAskedForMetadataCount < METADATA_REQQ ) )
{
msgs->peerAskedForMetadata[msgs->peerAskedForMetadataCount++] = piece;
}
else
{
tr_benc tmp;
int payloadLen;
char * payload;
tr_peerIo * io = msgs->peer->io;
struct evbuffer * out = msgs->outMessages;
/* build the rejection message */
tr_bencInitDict( &tmp, 2 );
tr_bencDictAddInt( &tmp, "msg_type", METADATA_MSG_TYPE_REJECT );
tr_bencDictAddInt( &tmp, "piece", piece );
payload = tr_bencToStr( &tmp, TR_FMT_BENC, &payloadLen );
tr_bencFree( &tmp );
/* write it out as a LTEP message to our outMessages buffer */
tr_peerIoWriteUint32( io, out, 2 * sizeof( uint8_t ) + payloadLen );
tr_peerIoWriteUint8 ( io, out, BT_LTEP );
tr_peerIoWriteUint8 ( io, out, msgs->ut_metadata_id );
tr_peerIoWriteBytes ( io, out, payload, payloadLen );
pokeBatchPeriod( msgs, HIGH_PRIORITY_INTERVAL_SECS );
dbgOutMessageLen( msgs );
tr_free( payload );
}
}
tr_free( tmp );
}
static void
parseUtPex( tr_peermsgs * msgs, int msglen, struct evbuffer * inbuf )
{
@ -1017,12 +1161,18 @@ parseLtep( tr_peermsgs * msgs,
sendPex( msgs );
}
}
else if( ltep_msgid == TR_LTEP_PEX )
else if( ltep_msgid == UT_PEX_ID )
{
dbgmsg( msgs, "got ut pex" );
msgs->peerSupportsPex = 1;
parseUtPex( msgs, msglen, inbuf );
}
else if( ltep_msgid == UT_METADATA_ID )
{
dbgmsg( msgs, "got ut metadata" );
msgs->peerSupportsMetadataXfer = 1;
parseUtMetadata( msgs, msglen, inbuf );
}
else
{
dbgmsg( msgs, "skipping unknown ltep message (%d)", (int)ltep_msgid );
@ -1087,7 +1237,7 @@ readBtId( tr_peermsgs * msgs,
static void
updatePeerProgress( tr_peermsgs * msgs )
{
msgs->peer->progress = tr_bitfieldCountTrueBits( msgs->peer->have ) / (float)msgs->torrent->info.pieceCount;
msgs->peer->progress = tr_bitsetPercent( &msgs->peer->have );
dbgmsg( msgs, "peer progress is %f", msgs->peer->progress );
updateFastSet( msgs );
updateInterest( msgs );
@ -1286,7 +1436,7 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen )
case BT_HAVE:
tr_peerIoReadUint32( msgs->peer->io, inbuf, &ui32 );
dbgmsg( msgs, "got Have: %u", ui32 );
if( tr_bitfieldAdd( msgs->peer->have, ui32 ) ) {
if( tr_bitsetAdd( &msgs->peer->have, ui32 ) ) {
fireError( msgs, ERANGE );
return READ_ERR;
}
@ -1295,7 +1445,8 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen )
case BT_BITFIELD:
dbgmsg( msgs, "got a bitfield" );
tr_peerIoReadBytes( msgs->peer->io, inbuf, msgs->peer->have->bits, msglen );
tr_bitsetReserve( &msgs->peer->have, msglen*8 );
tr_peerIoReadBytes( msgs->peer->io, inbuf, msgs->peer->have.bitfield.bits, msglen );
updatePeerProgress( msgs );
break;
@ -1318,13 +1469,17 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen )
tr_peerIoReadUint32( msgs->peer->io, inbuf, &r.offset );
tr_peerIoReadUint32( msgs->peer->io, inbuf, &r.length );
dbgmsg( msgs, "got a Cancel %u:%u->%u", r.index, r.offset, r.length );
for( i=0; i<msgs->peerAskedForCount; ++i ) {
const struct peer_request * req = msgs->peerAskedFor + i;
if( ( req->index == r.index ) && ( req->offset == r.offset ) && ( req->length == r.length ) )
break;
}
if( i < msgs->peerAskedForCount )
memmove( msgs->peerAskedFor+i, msgs->peerAskedFor+i+1, --msgs->peerAskedForCount-i );
memmove( msgs->peerAskedFor+i,
msgs->peerAskedFor+i+1,
sizeof(struct peer_request) *( --msgs->peerAskedForCount-i) );
break;
}
@ -1366,7 +1521,7 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen )
case BT_FEXT_HAVE_ALL:
dbgmsg( msgs, "Got a BT_FEXT_HAVE_ALL" );
if( fext ) {
tr_bitfieldAddRange( msgs->peer->have, 0, msgs->torrent->info.pieceCount );
tr_bitsetSetHaveAll( &msgs->peer->have );
updatePeerProgress( msgs );
} else {
fireError( msgs, EMSGSIZE );
@ -1377,7 +1532,7 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen )
case BT_FEXT_HAVE_NONE:
dbgmsg( msgs, "Got a BT_FEXT_HAVE_NONE" );
if( fext ) {
tr_bitfieldClear( msgs->peer->have );
tr_bitsetSetHaveNone( &msgs->peer->have );
updatePeerProgress( msgs );
} else {
fireError( msgs, EMSGSIZE );
@ -1586,7 +1741,45 @@ updateDesiredRequestCount( tr_peermsgs * msgs, uint64_t now )
}
static void
updateRequests( tr_peermsgs * msgs )
updateMetadataRequests( tr_peermsgs * msgs, time_t now )
{
int piece;
if( msgs->peerSupportsMetadataXfer
&& !msgs->requestingMetadataFromPeer
&& tr_torrentGetNextMetadataRequest( msgs->torrent, now, &piece ) )
{
tr_benc tmp;
int payloadLen;
char * payload;
tr_peerIo * io = msgs->peer->io;
struct evbuffer * out = msgs->outMessages;
/* build the data message */
tr_bencInitDict( &tmp, 3 );
tr_bencDictAddInt( &tmp, "msg_type", METADATA_MSG_TYPE_REQUEST );
tr_bencDictAddInt( &tmp, "piece", piece );
payload = tr_bencToStr( &tmp, TR_FMT_BENC, &payloadLen );
tr_bencFree( &tmp );
dbgmsg( msgs, "requesting metadata piece #%d", piece );
/* write it out as a LTEP message to our outMessages buffer */
tr_peerIoWriteUint32( io, out, 2 * sizeof( uint8_t ) + payloadLen );
tr_peerIoWriteUint8 ( io, out, BT_LTEP );
tr_peerIoWriteUint8 ( io, out, msgs->ut_metadata_id );
tr_peerIoWriteBytes ( io, out, payload, payloadLen );
pokeBatchPeriod( msgs, HIGH_PRIORITY_INTERVAL_SECS );
dbgOutMessageLen( msgs );
msgs->requestingMetadataFromPeer = TRUE;
tr_free( payload );
}
}
static void
updateBlockRequests( tr_peermsgs * msgs )
{
const int MIN_BATCH_SIZE = 4;
const int numwant = msgs->desiredRequestCount - msgs->activeRequestCount;
@ -1641,6 +1834,7 @@ prefetchPieces( tr_peermsgs *msgs )
static size_t
fillOutputBuffer( tr_peermsgs * msgs, time_t now )
{
int piece;
size_t bytesWritten = 0;
struct peer_request req;
const tr_bool haveMessages = EVBUFFER_LENGTH( msgs->outMessages ) != 0;
@ -1668,7 +1862,77 @@ fillOutputBuffer( tr_peermsgs * msgs, time_t now )
}
/**
*** Blocks
*** Metadata Pieces
**/
if( ( tr_peerIoGetWriteBufferSpace( msgs->peer->io, now ) >= METADATA_PIECE_SIZE )
&& popNextMetadataRequest( msgs, &piece ) )
{
char * data;
int dataLen;
tr_bool ok = FALSE;
data = tr_torrentGetMetadataPiece( msgs->torrent, piece, &dataLen );
if( ( dataLen > 0 ) && ( data != NULL ) )
{
tr_benc tmp;
int payloadLen;
char * payload;
tr_peerIo * io = msgs->peer->io;
struct evbuffer * out = msgs->outMessages;
/* build the data message */
tr_bencInitDict( &tmp, 3 );
tr_bencDictAddInt( &tmp, "msg_type", METADATA_MSG_TYPE_DATA );
tr_bencDictAddInt( &tmp, "piece", piece );
tr_bencDictAddInt( &tmp, "total_size", dataLen );
payload = tr_bencToStr( &tmp, TR_FMT_BENC, &payloadLen );
tr_bencFree( &tmp );
/* write it out as a LTEP message to our outMessages buffer */
tr_peerIoWriteUint32( io, out, 2 * sizeof( uint8_t ) + payloadLen + dataLen );
tr_peerIoWriteUint8 ( io, out, BT_LTEP );
tr_peerIoWriteUint8 ( io, out, msgs->ut_metadata_id );
tr_peerIoWriteBytes ( io, out, payload, payloadLen );
tr_peerIoWriteBytes ( io, out, data, dataLen );
pokeBatchPeriod( msgs, HIGH_PRIORITY_INTERVAL_SECS );
dbgOutMessageLen( msgs );
tr_free( payload );
tr_free( data );
ok = TRUE;
}
if( !ok ) /* send a rejection message */
{
tr_benc tmp;
int payloadLen;
char * payload;
tr_peerIo * io = msgs->peer->io;
struct evbuffer * out = msgs->outMessages;
/* build the rejection message */
tr_bencInitDict( &tmp, 2 );
tr_bencDictAddInt( &tmp, "msg_type", METADATA_MSG_TYPE_REJECT );
tr_bencDictAddInt( &tmp, "piece", piece );
payload = tr_bencToStr( &tmp, TR_FMT_BENC, &payloadLen );
tr_bencFree( &tmp );
/* write it out as a LTEP message to our outMessages buffer */
tr_peerIoWriteUint32( io, out, 2 * sizeof( uint8_t ) + payloadLen );
tr_peerIoWriteUint8 ( io, out, BT_LTEP );
tr_peerIoWriteUint8 ( io, out, msgs->ut_metadata_id );
tr_peerIoWriteBytes ( io, out, payload, payloadLen );
pokeBatchPeriod( msgs, HIGH_PRIORITY_INTERVAL_SECS );
dbgOutMessageLen( msgs );
tr_free( payload );
}
}
/**
*** Data Blocks
**/
if( ( tr_peerIoGetWriteBufferSpace( msgs->peer->io, now ) >= msgs->torrent->blockSize )
@ -1749,7 +2013,8 @@ peerPulse( void * vmsgs )
if ( tr_isPeerIo( msgs->peer->io ) ) {
updateDesiredRequestCount( msgs, now );
updateRequests( msgs );
updateBlockRequests( msgs );
updateMetadataRequests( msgs, now );
}
for( ;; )
@ -2111,7 +2376,6 @@ tr_peerMsgsNew( struct tr_torrent * torrent,
m->peer->peerIsChoked = 1;
m->peer->clientIsInterested = 0;
m->peer->peerIsInterested = 0;
m->peer->have = tr_bitfieldNew( torrent->info.pieceCount );
m->state = AWAITING_BT_LENGTH;
m->outMessages = evbuffer_new( );
m->outMessagesBatchedAt = 0;
@ -2167,4 +2431,3 @@ tr_peerMsgsUnsubscribe( tr_peermsgs * peer,
{
tr_publisherUnsubscribe( &peer->publisher, tag );
}

View File

@ -1093,14 +1093,22 @@ torrentAdd( tr_session * session,
}
else
{
if( filename != NULL )
tr_ctorSetMetainfoFromFile( ctor, filename );
else {
if( filename == NULL )
{
int len;
char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
tr_free( metainfo );
}
else if( !strncmp( filename, "magnet:?", 8 ) )
{
tr_ctorSetMagnet( ctor, filename );
}
else
{
tr_ctorSetMetainfoFromFile( ctor, filename );
}
addTorrentImpl( idle_data, ctor );
}

View File

@ -13,6 +13,7 @@
#include <errno.h>
#include "transmission.h"
#include "bencode.h"
#include "magnet.h"
#include "platform.h"
#include "session.h" /* tr_sessionFindTorrentFile() */
#include "torrent.h" /* tr_ctorGetSave() */
@ -42,6 +43,8 @@ struct tr_ctor
tr_benc metainfo;
char * sourceFile;
tr_magnet_info * magnetInfo;
struct optional_args optionalArgs[2];
char * incompleteDir;
@ -101,6 +104,20 @@ tr_ctorGetSourceFile( const tr_ctor * ctor )
return ctor->sourceFile;
}
int
tr_ctorSetMagnet( tr_ctor * ctor, const char * uri )
{
int err;
if( ctor->magnetInfo != NULL )
tr_magnetFree( ctor->magnetInfo );
ctor->magnetInfo = tr_magnetParse( uri );
err = ctor->magnetInfo == NULL;
return err;
}
int
tr_ctorSetMetainfoFromFile( tr_ctor * ctor,
const char * filename )
@ -371,6 +388,19 @@ 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 )

View File

@ -0,0 +1,284 @@
/*
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
*
* 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 <assert.h>
#include <event.h> /* struct evbuffer */
#include "transmission.h"
#include "bencode.h"
#include "crypto.h"
#include "magnet.h"
#include "metainfo.h"
#include "torrent.h"
#include "torrent-magnet.h"
#include "utils.h"
#include "web.h"
#define dbgmsg( tor, ... ) \
do { \
if( tr_deepLoggingIsActive( ) ) \
tr_deepLog( __FILE__, __LINE__, tor->info.name, __VA_ARGS__ ); \
} while( 0 )
/***
****
***/
enum
{
/* don't ask for the same metadata piece more than this often */
MIN_REPEAT_INTERVAL_SECS = 5
};
struct metadata_node
{
time_t requestedAt;
int piece;
};
struct tr_incomplete_metadata
{
uint8_t * metadata;
int metadata_size;
int pieceCount;
/** sorted from least to most recently requested */
struct metadata_node * piecesNeeded;
int piecesNeededCount;
};
static void
incompleteMetadataFree( struct tr_incomplete_metadata * m )
{
tr_free( m->metadata );
tr_free( m->piecesNeeded );
tr_free( m );
}
void
tr_torrentSetMetadataSizeHint( tr_torrent * tor, int size )
{
if( !tr_torrentHasMetadata( tor ) )
{
if( tor->incompleteMetadata == NULL )
{
int i;
struct tr_incomplete_metadata * m;
int n = ( size + ( METADATA_PIECE_SIZE - 1 ) ) / METADATA_PIECE_SIZE;
dbgmsg( tor, "there are %d pieces", n );
m = tr_new( struct tr_incomplete_metadata, 1 );
m->pieceCount = n;
m->metadata = tr_new( uint8_t, size );
m->metadata_size = size;
m->piecesNeededCount = n;
m->piecesNeeded = tr_new( struct metadata_node, n );
for( i=0; i<n; ++i ) {
m->piecesNeeded[i].piece = i;
m->piecesNeeded[i].requestedAt = 0;
}
tor->incompleteMetadata = m;
}
}
}
tr_bool
tr_torrentHasMetadata( const tr_torrent * tor )
{
return tor->info.fileCount > 0;
}
void*
tr_torrentGetMetadataPiece( const tr_torrent * tor, int piece, int * len )
{
char * ret = NULL;
assert( tr_isTorrent( tor ) );
assert( piece >= 0 );
assert( len != NULL );
if( tor->infoDictLength > 0 )
{
FILE * fp = fopen( tor->info.torrent, "rb" );
if( fp != NULL )
{
const int offset = piece * METADATA_PIECE_SIZE;
if( !fseek( fp, tor->infoDictOffset + offset, SEEK_SET ) )
{
const int l = offset + METADATA_PIECE_SIZE <= tor->infoDictLength
? METADATA_PIECE_SIZE
: tor->infoDictLength - offset;
char * buf = tr_new( char, l );
const int n = fread( buf, 1, l, fp );
if( n != l )
{
*len = l;
ret = buf;
buf = NULL;
}
tr_free( buf );
}
fclose( fp );
}
}
return ret;
}
void
tr_torrentSetMetadataPiece( tr_torrent * tor,
int piece,
const void * data,
int len )
{
int i;
struct tr_incomplete_metadata * m;
const int offset = piece * METADATA_PIECE_SIZE;
assert( tr_isTorrent( tor ) );
dbgmsg( tor, "got metadata piece %d", piece );
/* are we set up to download metadata? */
m = tor->incompleteMetadata;
if( m == NULL )
return;
/* does this data pass the smell test? */
if( offset + len > m->metadata_size )
return;
/* do we need this piece? */
for( i=0; i<m->piecesNeededCount; ++i )
if( m->piecesNeeded[i].piece == piece )
break;
if( i==m->piecesNeededCount )
return;
memcpy( m->metadata + offset, data, len );
tr_removeElementFromArray( m->piecesNeeded, i,
sizeof( struct metadata_node ),
m->piecesNeededCount-- );
dbgmsg( tor, "saving metainfo piece %d... %d remain", piece, m->piecesNeededCount );
/* are we done? */
if( m->piecesNeededCount == 0 )
{
tr_bool success = FALSE;
uint8_t sha1[SHA_DIGEST_LENGTH];
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 ) )
{
int err;
tr_benc dict;
struct evbuffer * buf = evbuffer_new( );
dbgmsg( tor, "metadata checksum passed! (length: %d)", 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%d:%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 );
dbgmsg( tor, "err is %d", err );
if( !err )
{
if( tr_metainfoParse( tor->session,
&tor->info,
&tor->infoDictLength,
&tor->infoDictOffset,
&dict ) )
{
const char * path = tor->info.torrent;
dbgmsg( tor, "saving completed metadata to \"%s\"", path );
success = TRUE;
tr_torrentGotNewInfoDict( tor );
tr_bencToFile( &dict, TR_FMT_BENC, path );
tr_sessionSetTorrentFile( tor->session,
tor->info.hashString, path );
}
}
evbuffer_free( buf );
}
if( success )
{
incompleteMetadataFree( tor->incompleteMetadata );
tor->incompleteMetadata = NULL;
}
else /* drat. */
{
const int n = m->pieceCount;
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_bool
tr_torrentGetNextMetadataRequest( tr_torrent * tor, time_t now, int * setme_piece )
{
tr_bool have_request = FALSE;
struct tr_incomplete_metadata * m;
assert( tr_isTorrent( tor ) );
m = tor->incompleteMetadata;
if( ( m != NULL )
&& ( m->piecesNeededCount > 0 )
&& ( m->piecesNeeded[0].requestedAt + MIN_REPEAT_INTERVAL_SECS <= now ) )
{
int i;
const int piece = m->piecesNeeded[0].piece;
tr_removeElementFromArray( m->piecesNeeded, 0,
sizeof( struct metadata_node ),
m->piecesNeededCount-- );
i = m->piecesNeededCount++;
m->piecesNeeded[0].piece = piece;
m->piecesNeeded[0].requestedAt = now;
dbgmsg( tor, "next piece to request: %d", piece );
*setme_piece = piece;
have_request = TRUE;
}
return have_request;
}

View File

@ -0,0 +1,38 @@
/*
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
*
* 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:$
*/
#ifndef __TRANSMISSION__
#error only libtransmission should #include this header.
#endif
#ifndef TR_TORRENT_MAGNET_H
#define TR_TORRENT_MAGNET_H 1
#include <time.h>
enum
{
/* defined by BEP #9 */
METADATA_PIECE_SIZE = ( 1024 * 16 )
};
tr_bool tr_torrentHasMetadata( const tr_torrent * tor );
void* tr_torrentGetMetadataPiece( const tr_torrent * tor, int piece, int * len );
void tr_torrentSetMetadataPiece( tr_torrent * tor, int piece, const void * data, int len );
tr_bool tr_torrentGetNextMetadataRequest( tr_torrent * tor, time_t now, int * setme );
void tr_torrentSetMetadataSizeHint( tr_torrent * tor, int metadata_size );
#endif

View File

@ -32,6 +32,7 @@
#include "crypto.h" /* for tr_sha1 */
#include "resume.h"
#include "fdlimit.h" /* tr_fdTorrentClose */
#include "magnet.h"
#include "metainfo.h"
#include "peer-mgr.h"
#include "platform.h" /* TR_PATH_DELIMITER_STR */
@ -43,7 +44,10 @@
#include "utils.h"
#include "verify.h"
#define MAX_BLOCK_SIZE ( 1024 * 16 )
enum
{
MAX_BLOCK_SIZE = 1024 * 16
};
/***
****
@ -550,63 +554,43 @@ getBlockSize( uint32_t pieceSize )
return b;
}
static void refreshCurrentDir( tr_torrent * tor );;
static void refreshCurrentDir( tr_torrent * tor );
static void
torrentRealInit( tr_torrent * tor, const tr_ctor * ctor )
torrentInitFromInfo( tr_torrent * tor )
{
int doStart;
uint64_t loaded;
uint64_t t;
const char * dir;
static int nextUniqueId = 1;
tr_info * info = &tor->info;
tr_session * session = tr_ctorGetSession( ctor );
assert( session != NULL );
tr_globalLock( session );
tor->session = session;
tor->uniqueId = nextUniqueId++;
tor->magicNumber = TORRENT_MAGIC_NUMBER;
randomizeTiers( info );
tor->bandwidth = tr_bandwidthNew( session, session->bandwidth );
uint64_t t;
tr_info * info = &tor->info;
tor->blockSize = getBlockSize( info->pieceSize );
if( !tr_ctorGetDownloadDir( ctor, TR_FORCE, &dir ) ||
!tr_ctorGetDownloadDir( ctor, TR_FALLBACK, &dir ) )
tor->downloadDir = tr_strdup( dir );
if( tr_ctorGetIncompleteDir( ctor, &dir ) )
dir = tr_sessionGetIncompleteDir( session );
if( tr_sessionIsIncompleteDirEnabled( session ) )
tor->incompleteDir = tr_strdup( dir );
tor->lastPieceSize = info->totalSize % info->pieceSize;
if( info->pieceSize )
tor->lastPieceSize = info->totalSize % info->pieceSize;
if( !tor->lastPieceSize )
tor->lastPieceSize = info->pieceSize;
tor->lastBlockSize = info->totalSize % tor->blockSize;
if( tor->blockSize )
tor->lastBlockSize = info->totalSize % tor->blockSize;
if( !tor->lastBlockSize )
tor->lastBlockSize = tor->blockSize;
tor->blockCount =
( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
tor->blockCount = tor->blockSize
? ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize
: 0;
tor->blockCountInPiece =
info->pieceSize / tor->blockSize;
tor->blockCountInPiece = tor->blockSize
? info->pieceSize / tor->blockSize
: 0;
tor->blockCountInLastPiece =
( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
tor->blockCountInLastPiece = tor->blockSize
? ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize
: 0;
/* check our work */
assert( ( info->pieceSize % tor->blockSize ) == 0 );
if( tor->blockSize != 0 )
assert( ( info->pieceSize % tor->blockSize ) == 0 );
t = info->pieceCount - 1;
t *= info->pieceSize;
t += tor->lastPieceSize;
@ -624,10 +608,53 @@ torrentRealInit( tr_torrent * tor, const tr_ctor * ctor )
tr_torrentInitFilePieces( tor );
tr_bitfieldConstruct( &tor->checkedPieces, tor->info.pieceCount );
tor->completeness = tr_cpGetStatus( &tor->completion );
}
void
tr_torrentGotNewInfoDict( tr_torrent * tor )
{
torrentInitFromInfo( tor );
}
static void
torrentInit( tr_torrent * tor, const tr_ctor * ctor )
{
int doStart;
uint64_t loaded;
const char * dir;
static int nextUniqueId = 1;
tr_session * session = tr_ctorGetSession( ctor );
assert( session != NULL );
tr_globalLock( session );
tor->session = session;
tor->uniqueId = nextUniqueId++;
tor->magicNumber = TORRENT_MAGIC_NUMBER;
randomizeTiers( &tor->info );
tr_sha1( tor->obfuscatedHash, "req2", 4,
info->hash, SHA_DIGEST_LENGTH,
tor->info.hash, SHA_DIGEST_LENGTH,
NULL );
if( !tr_ctorGetDownloadDir( ctor, TR_FORCE, &dir ) ||
!tr_ctorGetDownloadDir( ctor, TR_FALLBACK, &dir ) )
tor->downloadDir = tr_strdup( dir );
if( tr_ctorGetIncompleteDir( ctor, &dir ) )
dir = tr_sessionGetIncompleteDir( session );
if( tr_sessionIsIncompleteDirEnabled( session ) )
tor->incompleteDir = tr_strdup( dir );
tor->bandwidth = tr_bandwidthNew( session, session->bandwidth );
tor->error = TR_STAT_OK;
tr_peerMgrAddTorrent( session->peerMgr, tor );
assert( !tor->downloadedCur );
@ -637,9 +664,6 @@ torrentRealInit( tr_torrent * tor, const tr_ctor * ctor )
tr_ctorInitTorrentWanted( ctor, tor );
tor->error = TR_STAT_OK;
tr_bitfieldConstruct( &tor->checkedPieces, tor->info.pieceCount );
tr_torrentUncheck( tor );
tr_torrentSetAddedDate( tor, time( NULL ) ); /* this is a default value to be
@ -667,8 +691,6 @@ torrentRealInit( tr_torrent * tor, const tr_ctor * ctor )
tr_torrentSetRatioLimit( tor, tr_sessionGetRatioLimit( tor->session ) );
}
tor->completeness = tr_cpGetStatus( &tor->completion );
{
tr_torrent * it = NULL;
tr_torrent * last = NULL;
@ -682,7 +704,7 @@ torrentRealInit( tr_torrent * tor, const tr_ctor * ctor )
++session->torrentCount;
}
tr_globalUnlock( session );
torrentInitFromInfo( tor );
/* maybe save our own copy of the metainfo */
if( tr_ctorGetSave( ctor ) )
@ -690,19 +712,19 @@ torrentRealInit( tr_torrent * tor, const tr_ctor * ctor )
const tr_benc * val;
if( !tr_ctorGetMetainfo( ctor, &val ) )
{
const char * filename = tor->info.torrent;
tr_bencToFile( val, TR_FMT_BENC, filename );
tr_sessionSetTorrentFile( tor->session, tor->info.hashString, filename );
const char * path = tor->info.torrent;
tr_bencToFile( val, TR_FMT_BENC, path );
tr_sessionSetTorrentFile( tor->session, tor->info.hashString, path );
}
}
tor->tiers = tr_announcerAddTorrent( session->announcer, tor );
tor->tiers = tr_announcerAddTorrent( tor->session->announcer, tor );
tor->tiersSubscription = tr_announcerSubscribe( tor->tiers, onTrackerResponse, tor );
tr_metainfoMigrate( session, &tor->info );
if( doStart )
torrentStart( tor );
tr_globalUnlock( session );
}
tr_parse_result
@ -722,7 +744,7 @@ tr_torrentParse( const tr_ctor * ctor, tr_info * setmeInfo )
if( tr_ctorGetMetainfo( ctor, &metainfo ) )
return TR_PARSE_ERR;
didParse = tr_metainfoParse( session, setmeInfo, metainfo );
didParse = tr_metainfoParse( session, setmeInfo, NULL, NULL, metainfo );
doFree = didParse && ( setmeInfo == &tmp );
if( !didParse )
@ -744,23 +766,33 @@ tr_torrent *
tr_torrentNew( const tr_ctor * ctor,
int * setmeError )
{
int err;
tr_info tmpInfo;
int err;
tr_info tmpInfo;
tr_torrent * tor = NULL;
const tr_magnet_info * magnetInfo;
assert( ctor != NULL );
assert( tr_isSession( tr_ctorGetSession( ctor ) ) );
err = tr_torrentParse( ctor, &tmpInfo );
if( !err )
if( !tr_ctorGetMagnet( ctor, &magnetInfo ) )
{
tor = tr_new0( tr_torrent, 1 );
tor->info = tmpInfo;
torrentRealInit( tor, ctor );
tr_metainfoSetFromMagnet( &tor->info, magnetInfo );
torrentInit( tor, ctor );
}
else if( setmeError )
else
{
*setmeError = err;
err = tr_torrentParse( ctor, &tmpInfo );
if( !err )
{
tor = tr_new0( tr_torrent, 1 );
tor->info = tmpInfo;
torrentInit( tor, ctor );
}
else if( setmeError )
{
*setmeError = err;
}
}
return tor;
@ -988,7 +1020,7 @@ tr_torrentStat( tr_torrent * tor )
: 0.8*tor->etaDLSpeed + 0.2*s->pieceDownloadSpeed; /* smooth across 5 readings */
tor->etaDLSpeedCalculatedAt = now;
}
if( s->leftUntilDone > s->desiredAvailable )
s->eta = TR_ETA_NOT_AVAIL;
else if( s->pieceDownloadSpeed < 0.1 )
@ -1006,7 +1038,7 @@ tr_torrentStat( tr_torrent * tor )
: 0.8*tor->etaULSpeed + 0.2*s->pieceUploadSpeed; /* smooth across 5 readings */
tor->etaULSpeedCalculatedAt = now;
}
if( s->pieceUploadSpeed < 0.1 )
s->eta = TR_ETA_UNKNOWN;
else
@ -1020,13 +1052,13 @@ tr_torrentStat( tr_torrent * tor )
s->eta = TR_ETA_NOT_AVAIL;
break;
}
if( !checkSeedRatio || s->ratio >= seedRatio || s->ratio == TR_RATIO_INF )
s->percentRatio = 1.0;
else if( s->ratio == TR_RATIO_NA )
s->percentRatio = 0.0;
else
s->percentRatio = s->ratio / seedRatio;
if( !checkSeedRatio || s->ratio >= seedRatio || s->ratio == TR_RATIO_INF )
s->percentRatio = 1.0;
else if( s->ratio == TR_RATIO_NA )
s->percentRatio = 0.0;
else
s->percentRatio = s->ratio / seedRatio;
tr_torrentUnlock( tor );
@ -2096,7 +2128,10 @@ 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, &metainfo ) )
if( tr_metainfoParse( tor->session, &tmpInfo,
&tor->infoDictOffset,
&tor->infoDictLength,
&metainfo ) )
{
/* it's good, so keep these new trackers and free the old ones */
@ -2367,7 +2402,6 @@ tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
fileFunc( path );
tr_free( path );
tmp = tr_torrentBuildPartial( tor, 0 );
path = tr_buildPath( tor->currentDir, tmp, NULL );
fileFunc( path );
@ -2656,7 +2690,6 @@ tr_torrentFindFile2( const tr_torrent * tor, tr_file_index_t fileNum,
return b != NULL;
}
char*
tr_torrentFindFile( const tr_torrent * tor, tr_file_index_t fileNum )
{

View File

@ -25,6 +25,7 @@
struct tr_bandwidth;
struct tr_ratecontrol;
struct tr_torrent_tiers;
struct tr_magnet_info;
/**
*** Package-visible ctor API
@ -35,6 +36,8 @@ 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 );
@ -131,6 +134,8 @@ tr_verify_state;
void tr_torrentSetVerifyState( tr_torrent * tor,
tr_verify_state state );
struct tr_incomplete_metadata;
struct tr_torrent
{
tr_session * session;
@ -143,6 +148,11 @@ struct tr_torrent
uint8_t obfuscatedHash[SHA_DIGEST_LENGTH];
/* Used when the torrent has been created with a magnet link
* and we're in the process of downloading the metainfo from
* other peers */
struct tr_incomplete_metadata * incompleteMetadata;
/* If the initiator of the connection receives a handshake in which the
* peer_id does not match the expected peerid, then the initiator is
* expected to drop the connection. Note that the initiator presumably
@ -158,6 +168,12 @@ struct tr_torrent
/* Where the files are when the torrent is incomplete */
char * incompleteDir;
/* Length, in bytes, of the "info" dict in the .torrent file */
int infoDictLength;
/* Offset, in bytes, of the beginning of the "info" dict in the .torrent file */
int infoDictOffset;
/* Where the files are now.
* This pointer will be equal to downloadDir or incompleteDir */
const char * currentDir;
@ -339,14 +355,16 @@ static TR_INLINE tr_bool tr_isTorrent( const tr_torrent * tor )
/* set a flag indicating that the torrent's .resume file
* needs to be saved when the torrent is closed */
static TR_INLINE void tr_torrentSetDirty( tr_torrent * tor )
static TR_INLINE
void tr_torrentSetDirty( tr_torrent * tor )
{
assert( tr_isTorrent( tor ) );
tor->isDirty = TRUE;
}
static TR_INLINE const char * tr_torrentName( const tr_torrent * tor )
static TR_INLINE
const char * tr_torrentName( const tr_torrent * tor )
{
assert( tr_isTorrent( tor ) );
@ -382,5 +400,8 @@ tr_bool tr_torrentFindFile2( const tr_torrent *, tr_file_index_t fileNo,
* a la Firefox. */
char* tr_torrentBuildPartial( const tr_torrent *, tr_file_index_t fileNo );
/* for when the info dict has been fundamentally changed wrt files,
* piece size, etc. such as in BEP 9 where peers exchange metadata */
void tr_torrentGotNewInfoDict( tr_torrent * tor );
#endif

View File

@ -858,6 +858,9 @@ void tr_ctorFree( tr_ctor * ctor );
void tr_ctorSetDeleteSource( tr_ctor * ctor,
tr_bool doDelete );
int tr_ctorSetMagnet( tr_ctor * ctor,
const char * url );
int tr_ctorSetMetainfo( tr_ctor * ctor,
const uint8_t * metainfo,
size_t len );

View File

@ -297,6 +297,28 @@ test_hex( void )
return 0;
}
static int
test_array( void )
{
int i;
int array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int n = sizeof( array ) / sizeof( array[0] );
tr_removeElementFromArray( array, 5, sizeof( int ), n-- );
for( i=0; i<n; ++i )
check( array[i] == ( i<5 ? i : i+1 ) );
tr_removeElementFromArray( array, 0, sizeof( int ), n-- );
for( i=0; i<n; ++i )
check( array[i] == ( i<4 ? i+1 : i+2 ) );
tr_removeElementFromArray( array, n-1, sizeof( int ), n ); n--;
for( i=0; i<n; ++i )
check( array[i] == ( i<4 ? i+1 : i+2 ) );
return 0;
}
int
main( void )
{
@ -338,6 +360,8 @@ main( void )
return i;
if( ( i = test_memmem( ) ) )
return i;
if( ( i = test_array( ) ) )
return i;
/* test that tr_cryptoRandInt() stays in-bounds */
for( i = 0; i < 100000; ++i )

View File

@ -1415,3 +1415,18 @@ tr_moveFile( const char * oldpath, const char * newpath, tr_bool * renamed )
unlink( oldpath );
return 0;
}
/***
****
***/
void
tr_removeElementFromArray( void * array, int index_to_remove,
size_t sizeof_element, size_t nmemb )
{
char * a = array;
memmove( a + sizeof_element * index_to_remove,
a + sizeof_element * ( index_to_remove + 1 ),
sizeof_element * ( --nmemb - index_to_remove ) );
}

View File

@ -424,8 +424,11 @@ struct tm * tr_localtime_r( const time_t *_clock, struct tm *_result );
/** on success, return 0. on failure, return -1 and set errno */
int tr_moveFile( const char * oldpath, const char * newpath, tr_bool * renamed ) TR_GNUC_NONNULL(1,2);
int tr_moveFile( const char * oldpath, const char * newpath,
tr_bool * renamed ) TR_GNUC_NONNULL(1,2);
void tr_removeElementFromArray( void * array, int index_to_remove,
size_t sizeof_element, size_t nmemb );
#ifdef __cplusplus
}