tr_lock* tr_lockNew( void ) { tr_lock * l = tr_new0( tr_lock, 1 ); #ifdef WIN32 InitializeCriticalSection( &l->lock ); /* supports recursion */ #else pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); pthread_mutex_init( &l->lock, &attr ); #endif return l; }
void tr_eventInit( tr_session * session ) { tr_event_handle * eh; session->events = NULL; eh = tr_new0( tr_event_handle, 1 ); eh->lock = tr_lockNew( ); pipe( eh->fds ); eh->session = session; eh->thread = tr_threadNew( libeventThreadFunc, eh ); /* wait until the libevent thread is running */ while( session->events == NULL ) tr_wait( 100 ); }
tr_webseed* tr_webseedNew( struct tr_torrent * torrent, const char * url, tr_peer_callback * callback, void * callback_data ) { tr_webseed * w = tr_new0( tr_webseed, 1 ); memcpy( w->hash, torrent->info.hash, SHA_DIGEST_LENGTH ); w->session = torrent->session; w->content = evbuffer_new( ); w->url = tr_strdup( url ); w->callback = callback; w->callback_data = callback_data; tr_rcConstruct( &w->rateDown ); return w; }
static tr_peerIo* tr_peerIoNew( tr_session * session, tr_bandwidth * parent, const tr_address * addr, tr_port port, const uint8_t * torrentHash, tr_bool isIncoming, tr_bool isSeed, int socket ) { tr_peerIo * io; assert( session != NULL ); assert( session->events != NULL ); assert( tr_isBool( isIncoming ) ); assert( tr_isBool( isSeed ) ); assert( tr_amInEventThread( session ) ); if( socket >= 0 ) { tr_netSetTOS( socket, session->peerSocketTOS ); maybeSetCongestionAlgorithm( socket, session->peer_congestion_algorithm ); } io = tr_new0( tr_peerIo, 1 ); io->magicNumber = MAGIC_NUMBER; io->refCount = 1; io->crypto = tr_cryptoNew( torrentHash, isIncoming ); io->session = session; io->addr = *addr; io->isSeed = isSeed; io->port = port; io->socket = socket; io->isIncoming = isIncoming != 0; io->hasFinishedConnecting = FALSE; io->timeCreated = tr_time( ); io->inbuf = evbuffer_new( ); io->outbuf = evbuffer_new( ); tr_bandwidthConstruct( &io->bandwidth, session, parent ); tr_bandwidthSetPeer( &io->bandwidth, io ); dbgmsg( io, "bandwidth is %p; its parent is %p", &io->bandwidth, parent ); event_set( &io->event_read, io->socket, EV_READ, event_read_cb, io ); event_set( &io->event_write, io->socket, EV_WRITE, event_write_cb, io ); return io; }
void tr_eventInit (tr_session * session) { tr_event_handle * eh; session->events = NULL; eh = tr_new0 (tr_event_handle, 1); eh->lock = tr_lockNew (); if (pipe (eh->fds) == -1) tr_logAddError ("Unable to write to pipe() in libtransmission: %s", tr_strerror(errno)); eh->session = session; eh->thread = tr_threadNew (libeventThreadFunc, eh); /* wait until the libevent thread is running */ while (session->events == NULL) tr_wait_msec (100); }
// Recebe uma função e um ponteiro para seus argumentos, e executa a função dada em... // ...uma nova thread. tr_thread * tr_threadNew(void (*func)(void *), void * arg) { tr_thread * t = tr_new0(tr_thread, 1); // aloca espaço para uma estrutura 'tr_thread' t->func = func; t->arg = arg; #ifdef WIN32 // Se o Transmission for utilizado em um sistema operacional Windows... { // ...usa os comandos de thread específicos para ele. unsigned int id; t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id); t->thread = (DWORD) id; } #else // ...caso contrário, use pthread, que é padrão do Mac OS e Linux pthread_create(&t->thread, NULL, (void* (*)(void*)) ThreadFunc, t); pthread_detach(t->thread); #endif return t; }
tr_webseed* tr_webseedNew( struct tr_torrent * torrent, const char * url, tr_delivery_func callback, void * callback_userdata ) { tr_webseed * w = tr_new0( tr_webseed, 1 ); memcpy( w->hash, torrent->info.hash, SHA_DIGEST_LENGTH ); w->session = torrent->session; w->content = evbuffer_new( ); w->url = tr_strdup( url ); w->callback = callback; w->callback_userdata = callback_userdata; tr_rcConstruct( &w->rateDown ); /*fprintf( stderr, "w->callback_userdata is %p\n", w->callback_userdata );*/ return w; }
tr_handshake* tr_handshakeNew (tr_peerIo * io, tr_encryption_mode encryptionMode, handshakeDoneCB doneCB, void * doneUserData) { tr_handshake * handshake; tr_session * session = tr_peerIoGetSession (io); handshake = tr_new0 (tr_handshake, 1); handshake->io = io; handshake->crypto = tr_peerIoGetCrypto (io); handshake->encryptionMode = encryptionMode; handshake->doneCB = doneCB; handshake->doneUserData = doneUserData; handshake->session = session; handshake->timeout_timer = evtimer_new (session->event_base, handshakeTimeout, handshake); tr_timerAdd (handshake->timeout_timer, HANDSHAKE_TIMEOUT_SEC, 0); tr_peerIoRef (io); /* balanced by the unref in tr_handshakeFree */ tr_peerIoSetIOFuncs (handshake->io, canRead, NULL, gotError, handshake); tr_peerIoSetEncryption (io, PEER_ENCRYPTION_NONE); if (tr_peerIoIsIncoming (handshake->io)) { setReadState (handshake, AWAITING_HANDSHAKE); } else if (encryptionMode != TR_CLEAR_PREFERRED) { sendYa (handshake); } else { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage (handshake, msg); handshake->haveSentBitTorrentHandshake = 1; setReadState (handshake, AWAITING_HANDSHAKE); tr_peerIoWriteBytes (handshake->io, msg, sizeof (msg), false); } return handshake; }
static int test_single_directory_random_payload_impl (const tr_tracker_info * trackers, const size_t trackerCount, const size_t maxFileCount, const size_t maxFileSize, const char * comment, const bool isPrivate) { size_t i; void ** payloads; size_t * payloadSizes; size_t payloadCount; /* build random payloads */ payloadCount = 1 + tr_rand_int_weak (maxFileCount); payloads = tr_new0 (void*, payloadCount); payloadSizes = tr_new0 (size_t, payloadCount); for (i=0; i<payloadCount; i++) { const size_t n = 1 + tr_rand_int_weak (maxFileSize); payloads[i] = tr_new (char, n); tr_rand_buffer (payloads[i], n); payloadSizes[i] = n; } /* run the test */ test_single_directory_impl (trackers, trackerCount, (const void**) payloads, payloadSizes, payloadCount, comment, isPrivate); /* cleanup */ for (i=0; i<payloadCount; i++) tr_free (payloads[i]); tr_free (payloads); tr_free (payloadSizes); return 0; }
tr_thread * tr_threadNew (void (*func)(void *), void * arg) { tr_thread * t = tr_new0 (tr_thread, 1); t->func = func; t->arg = arg; #ifdef WIN32 { unsigned int id; t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id); t->thread = (DWORD) id; } #else pthread_create (&t->thread, NULL, (void* (*)(void*))ThreadFunc, t); pthread_detach (t->thread); #endif return t; }
tr_timer* tr_timerNew( tr_session * session, timer_func func, void * user_data, uint64_t interval_milliseconds ) { tr_timer * timer; assert( tr_amInEventThread( session ) ); timer = tr_new0( tr_timer, 1 ); timer->func = func; timer->user_data = user_data; timer->eh = session->events; tr_timevalMsec( interval_milliseconds, &timer->tv ); evtimer_set( &timer->event, timerCallback, timer ); evtimer_add( &timer->event, &timer->tv ); return timer; }
tr_shared * tr_sharedInit( tr_session * session ) { tr_shared * s = tr_new0( tr_shared, 1 ); s->session = session; s->isEnabled = FALSE; s->upnpStatus = TR_PORT_UNMAPPED; s->natpmpStatus = TR_PORT_UNMAPPED; #if 0 if( isEnabled ) { s->timer = tr_new0( struct event, 1 ); evtimer_set( s->timer, onTimer, s ); tr_timerAdd( s->timer, 0, 333000 ); } #endif return s; }
tr_timer* tr_timerNew( struct tr_handle * handle, timer_func func, void * user_data, uint64_t interval_milliseconds ) { tr_timer * timer; assert( handle ); assert( handle->events ); timer = tr_new0( tr_timer, 1 ); tr_timevalMsec( interval_milliseconds, &timer->tv ); timer->func = func; timer->user_data = user_data; timer->eh = handle->events; evtimer_set( &timer->event, timerCallback, timer ); if( tr_amInThread( handle->events->thread ) ) { evtimer_add( &timer->event, &timer->tv ); } else { const char ch = 't'; int fd = handle->events->fds[1]; tr_lock * lock = handle->events->lock; tr_lockLock( lock ); pipewrite( fd, &ch, 1 ); pipewrite( fd, &timer, sizeof( timer ) ); tr_lockUnlock( lock ); } return timer; }
static tr_pex* listToPex (tr_variant * peerList, size_t * setme_len) { size_t i; size_t n; const size_t len = tr_variantListSize (peerList); tr_pex * pex = tr_new0 (tr_pex, len); for (i=n=0; i<len; ++i) { int64_t port; const char * ip; tr_address addr; tr_variant * peer = tr_variantListChild (peerList, i); if (peer == NULL) continue; if (!tr_variantDictFindStr (peer, TR_KEY_ip, &ip, NULL)) continue; if (!tr_address_from_string (&addr, ip)) continue; if (!tr_variantDictFindInt (peer, TR_KEY_port, &port)) continue; if ((port < 0) || (port > USHRT_MAX)) continue; if (!tr_address_is_valid_for_peers (&addr, port)) continue; pex[n].addr = addr; pex[n].port = htons ((uint16_t)port); ++n; } *setme_len = n; return pex; }
void tr_logAddMessage (const char * file, int line, tr_log_level level, const char * name, const char * fmt, ...) { const int err = errno; /* message logging shouldn't affect errno */ char buf[1024]; va_list ap; tr_lockLock (getMessageLock ()); /* build the text message */ *buf = '\0'; va_start (ap, fmt); evutil_vsnprintf (buf, sizeof (buf), fmt, ap); va_end (ap); OutputDebugStringA (buf); if (*buf) { if (tr_logGetQueueEnabled ()) { tr_log_message * newmsg; newmsg = tr_new0 (tr_log_message, 1); newmsg->level = level; newmsg->when = tr_time (); newmsg->message = tr_strdup (buf); newmsg->file = file; newmsg->line = line; newmsg->name = tr_strdup (name); *myQueueTail = newmsg; myQueueTail = &newmsg->next; ++myQueueLength; if (myQueueLength > TR_LOG_MAX_QUEUE_LENGTH) { tr_log_message * old = myQueue; myQueue = old->next; old->next = NULL; tr_logFreeQueue (old); --myQueueLength; assert (myQueueLength == TR_LOG_MAX_QUEUE_LENGTH); } } else { FILE * fp; char timestr[64]; fp = tr_logGetFile (); if (fp == NULL) fp = stderr; tr_logGetTimeStr (timestr, sizeof (timestr)); if (name) fprintf (fp, "[%s] %s: %s\n", timestr, name, buf); else fprintf (fp, "[%s] %s\n", timestr, buf); fflush (fp); } } tr_lockUnlock (getMessageLock ()); errno = err; }
tr_publisher_t* tr_publisherNew( void ) { return tr_new0( tr_publisher_t, 1 ); }
tr_magnet_info * tr_magnetParse( const char * uri ) { bool got_checksum = false; int trCount = 0; int wsCount = 0; char * tr[MAX_TRACKERS]; char * ws[MAX_WEBSEEDS]; char * displayName = NULL; uint8_t sha1[SHA_DIGEST_LENGTH]; tr_magnet_info * info = NULL; if( ( uri != NULL ) && !memcmp( uri, "magnet:?", 8 ) ) { const char * walk; for( walk=uri+8; walk && *walk; ) { const char * key = walk; const char * delim = strchr( key, '=' ); const char * val = delim == NULL ? NULL : delim + 1; const char * next = strchr( delim == NULL ? key : val, '&' ); int keylen, vallen; if( delim != NULL ) keylen = delim - key; else if( next != NULL ) keylen = next - key; else keylen = strlen( key ); if( val == NULL ) vallen = 0; else if( next != NULL ) vallen = next - val; else vallen = strlen( val ); if( ( keylen==2 ) && !memcmp( key, "xt", 2 ) && val && !memcmp( val, "urn:btih:", 9 ) ) { const char * hash = val + 9; const int hashlen = vallen - 9; if( hashlen == 40 ) { tr_hex_to_sha1( sha1, hash ); got_checksum = true; } else if( hashlen == 32 ) { base32_to_sha1( sha1, hash, hashlen ); got_checksum = true; } } if( ( vallen > 0 ) && ( keylen==2 ) && !memcmp( key, "dn", 2 ) ) displayName = tr_http_unescape( val, vallen ); if( ( vallen > 0 ) && ( trCount < MAX_TRACKERS ) ) { int i; if( ( keylen==2 ) && !memcmp( key, "tr", 2 ) ) tr[trCount++] = tr_http_unescape( val, vallen ); else if( ( sscanf( key, "tr.%d=", &i ) == 1 ) && ( i > 0 ) ) /* ticket #3341 */ tr[trCount++] = tr_http_unescape( val, vallen ); } if( ( vallen > 0 ) && ( keylen==2 ) && !memcmp( key, "ws", 2 ) && ( wsCount < MAX_WEBSEEDS ) ) ws[wsCount++] = tr_http_unescape( val, vallen ); walk = next != NULL ? next + 1 : NULL; } } if( got_checksum ) { info = tr_new0( tr_magnet_info, 1 ); info->displayName = displayName; info->trackerCount = trCount; info->trackers = tr_memdup( tr, sizeof(char*) * trCount ); info->webseedCount = wsCount; info->webseeds = tr_memdup( ws, sizeof(char*) * wsCount ); memcpy( info->hash, sha1, sizeof(uint8_t) * SHA_DIGEST_LENGTH ); } return info; }
void tr_logAddMessage (const char * file, int line, tr_log_level level, const char * name, const char * fmt, ...) { const int err = errno; /* message logging shouldn't affect errno */ char buf[1024]; int buf_len; va_list ap; tr_lockLock (getMessageLock ()); /* build the text message */ *buf = '\0'; va_start (ap, fmt); buf_len = evutil_vsnprintf (buf, sizeof (buf), fmt, ap); va_end (ap); if (buf_len < 0) return; #ifdef _WIN32 if ((size_t) buf_len < sizeof (buf) - 3) { buf[buf_len + 0] = '\r'; buf[buf_len + 1] = '\n'; buf[buf_len + 2] = '\0'; OutputDebugStringA (buf); buf[buf_len + 0] = '\0'; } else { OutputDebugStringA (buf); } #endif if (*buf) { if (tr_logGetQueueEnabled ()) { tr_log_message * newmsg; newmsg = tr_new0 (tr_log_message, 1); newmsg->level = level; newmsg->when = tr_time (); newmsg->message = tr_strdup (buf); newmsg->file = file; newmsg->line = line; newmsg->name = tr_strdup (name); *myQueueTail = newmsg; myQueueTail = &newmsg->next; ++myQueueLength; if (myQueueLength > TR_LOG_MAX_QUEUE_LENGTH) { tr_log_message * old = myQueue; myQueue = old->next; old->next = NULL; tr_logFreeQueue (old); --myQueueLength; assert (myQueueLength == TR_LOG_MAX_QUEUE_LENGTH); } } else { tr_sys_file_t fp; char timestr[64]; fp = tr_logGetFile (); if (fp == TR_BAD_SYS_FILE) fp = tr_sys_file_get_std (TR_STD_SYS_FILE_ERR, NULL); tr_logGetTimeStr (timestr, sizeof (timestr)); if (name) tr_sys_file_write_fmt (fp, "[%s] %s: %s" TR_NATIVE_EOL_STR, NULL, timestr, name, buf); else tr_sys_file_write_fmt (fp, "[%s] %s" TR_NATIVE_EOL_STR, NULL, timestr, buf); tr_sys_file_flush (fp, NULL); } } tr_lockUnlock (getMessageLock ()); errno = err; }
tr_watchdir_backend * tr_watchdir_win32_new (tr_watchdir_t handle) { const char * const path = tr_watchdir_get_path (handle); wchar_t * wide_path; tr_watchdir_win32 * backend; backend = tr_new0 (tr_watchdir_win32, 1); backend->base.free_func = &tr_watchdir_win32_free; backend->fd = INVALID_HANDLE_VALUE; backend->notify_pipe[0] = backend->notify_pipe[1] = TR_BAD_SOCKET; if ((wide_path = tr_win32_utf8_to_native (path, -1)) == NULL) { log_error ("Failed to convert \"%s\" to native path", path); goto fail; } if ((backend->fd = CreateFileW (wide_path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL)) == INVALID_HANDLE_VALUE) { log_error ("Failed to open directory \"%s\"", path); goto fail; } tr_free (wide_path); wide_path = NULL; backend->overlapped.Pointer = handle; if (!ReadDirectoryChangesW (backend->fd, backend->buffer, sizeof (backend->buffer), FALSE, WIN32_WATCH_MASK, NULL, &backend->overlapped, NULL)) { log_error ("Failed to read directory changes"); goto fail; } if (evutil_socketpair (AF_INET, SOCK_STREAM, 0, backend->notify_pipe) == -1) { log_error ("Failed to create notify pipe: %s", tr_strerror (errno)); goto fail; } if ((backend->event = bufferevent_socket_new (tr_watchdir_get_event_base (handle), backend->notify_pipe[0], 0)) == NULL) { log_error ("Failed to create event buffer: %s", tr_strerror (errno)); goto fail; } bufferevent_setwatermark (backend->event, EV_READ, sizeof (FILE_NOTIFY_INFORMATION), 0); bufferevent_setcb (backend->event, &tr_watchdir_win32_on_event, NULL, NULL, handle); bufferevent_enable (backend->event, EV_READ); if ((backend->thread = (HANDLE) _beginthreadex (NULL, 0, &tr_watchdir_win32_thread, handle, 0, NULL)) == NULL) { log_error ("Failed to create thread"); goto fail; } /* Perform an initial scan on the directory */ if (event_base_once (tr_watchdir_get_event_base (handle), -1, EV_TIMEOUT, &tr_watchdir_win32_on_first_scan, handle, NULL) == -1) log_error ("Failed to perform initial scan: %s", tr_strerror (errno)); return BACKEND_DOWNCAST (backend); fail: tr_watchdir_win32_free (BACKEND_DOWNCAST (backend)); tr_free (wide_path); return NULL; }
static char const* parseFiles(tr_info* inf, tr_variant* files, tr_variant const* length) { int64_t len; inf->totalSize = 0; if (tr_variantIsList(files)) /* multi-file mode */ { struct evbuffer* buf; char const* result; if (path_component_is_suspicious(inf->name)) { return "path"; } buf = evbuffer_new(); result = NULL; inf->isFolder = true; inf->fileCount = tr_variantListSize(files); inf->files = tr_new0(tr_file, inf->fileCount); for (tr_file_index_t i = 0; i < inf->fileCount; i++) { tr_variant* file; tr_variant* path; file = tr_variantListChild(files, i); if (!tr_variantIsDict(file)) { result = "files"; break; } if (!tr_variantDictFindList(file, TR_KEY_path_utf_8, &path)) { if (!tr_variantDictFindList(file, TR_KEY_path, &path)) { result = "path"; break; } } if (!getfile(&inf->files[i].name, inf->name, path, buf)) { result = "path"; break; } if (!tr_variantDictFindInt(file, TR_KEY_length, &len)) { result = "length"; break; } inf->files[i].length = len; inf->totalSize += len; } evbuffer_free(buf); return result; } else if (tr_variantGetInt(length, &len)) /* single-file mode */ { if (path_component_is_suspicious(inf->name)) { return "path"; } inf->isFolder = false; inf->fileCount = 1; inf->files = tr_new0(tr_file, 1); inf->files[0].name = tr_strdup(inf->name); inf->files[0].length = len; inf->totalSize += len; } else { return "length"; } return NULL; }
tr_ratecontrol* tr_rcInit( void ) { return tr_new0( tr_ratecontrol, 1 ); }
tr_rc4_ctx_t tr_rc4_new (void) { return tr_new0 (Arc4, 1); }
static const char* getannounce( tr_info * inf, tr_benc * meta ) { const char * str; tr_tracker_info * trackers = NULL; int trackerCount = 0; tr_benc * tiers; /* Announce-list */ if( tr_bencDictFindList( meta, "announce-list", &tiers ) ) { int n; int i, j; const int numTiers = tr_bencListSize( tiers ); n = 0; for( i = 0; i < numTiers; ++i ) n += tr_bencListSize( tr_bencListChild( tiers, i ) ); trackers = tr_new0( tr_tracker_info, n ); trackerCount = 0; for( i = 0; i < numTiers; ++i ) { tr_benc * tier = tr_bencListChild( tiers, i ); const int tierSize = tr_bencListSize( tier ); for( j = 0; j < tierSize; ++j ) { if( tr_bencGetStr( tr_bencListChild( tier, j ), &str ) ) { char * url = tr_strstrip( tr_strdup( str ) ); if( tr_httpIsValidURL( url ) ) { tr_tracker_info * t = trackers + trackerCount++; t->tier = i; t->announce = tr_strdup( url ); t->scrape = announceToScrape( url ); } tr_free( url ); } } } /* did we use any of the tiers? */ if( !trackerCount ) { tr_free( trackers ); trackers = NULL; } } /* Regular announce value */ if( !trackerCount && tr_bencDictFindStr( meta, "announce", &str ) ) { char * url = tr_strstrip( tr_strdup( str ) ); if( tr_httpIsValidURL( url ) ) { trackers = tr_new0( tr_tracker_info, 1 ); trackers[trackerCount].tier = 0; trackers[trackerCount].announce = tr_strdup( url ); trackers[trackerCount++].scrape = announceToScrape( url ); /*fprintf( stderr, "single announce: [%s]\n", url );*/ } tr_free( url ); } inf->trackers = trackers; inf->trackerCount = trackerCount; return inf->trackerCount ? NULL : "announce"; }
void tr_msg( const char * file, int line, tr_msg_level level, const char * name, const char * fmt, ... ) { const int err = errno; /* message logging shouldn't affect errno */ char buf[1024]; va_list ap; tr_lockLock( getMessageLock( ) ); /* build the text message */ *buf = '\0'; va_start( ap, fmt ); evutil_vsnprintf( buf, sizeof( buf ), fmt, ap ); va_end( ap ); OutputDebugString( buf ); if( *buf ) { if( messageQueuing ) { tr_msg_list * newmsg; newmsg = tr_new0( tr_msg_list, 1 ); newmsg->level = level; newmsg->when = tr_time( ); newmsg->message = tr_strdup( buf ); newmsg->file = file; newmsg->line = line; newmsg->name = tr_strdup( name ); *messageQueueTail = newmsg; messageQueueTail = &newmsg->next; ++messageQueueCount; if( messageQueueCount > TR_MAX_MSG_LOG ) { tr_msg_list * old = messageQueue; messageQueue = old->next; old->next = NULL; tr_freeMessageList(old); --messageQueueCount; assert( messageQueueCount == TR_MAX_MSG_LOG ); } } else { char timestr[64]; FILE * fp; fp = tr_getLog( ); if( fp == NULL ) fp = stderr; tr_getLogTimeStr( timestr, sizeof( timestr ) ); if( name ) fprintf( fp, "[%s] %s: %s\n", timestr, name, buf ); else fprintf( fp, "[%s] %s\n", timestr, buf ); fflush( fp ); } } tr_lockUnlock( getMessageLock( ) ); errno = err; }
static tr_list* node_alloc( void ) { return tr_new0( tr_list, 1 ); }
static const char* getannounce( tr_info * inf, tr_benc * meta ) { const char * str; tr_tracker_info * trackers = NULL; int trackerCount = 0; tr_benc * tiers; /* Announce-list */ if( tr_bencDictFindList( meta, "announce-list", &tiers ) ) { int n; int i, j, validTiers; const int numTiers = tr_bencListSize( tiers ); n = 0; for( i = 0; i < numTiers; ++i ) n += tr_bencListSize( tr_bencListChild( tiers, i ) ); trackers = tr_new0( tr_tracker_info, n ); for( i = 0, validTiers = 0; i < numTiers; ++i ) { tr_benc * tier = tr_bencListChild( tiers, i ); const int tierSize = tr_bencListSize( tier ); tr_bool anyAdded = FALSE; for( j = 0; j < tierSize; ++j ) { if( tr_bencGetStr( tr_bencListChild( tier, j ), &str ) ) { char * url = tr_strstrip( tr_strdup( str ) ); if( tr_urlIsValidTracker( url ) ) { tr_tracker_info * t = trackers + trackerCount; t->tier = validTiers; t->announce = tr_strdup( url ); t->scrape = tr_convertAnnounceToScrape( url ); t->id = trackerCount; anyAdded = TRUE; ++trackerCount; } tr_free( url ); } } if( anyAdded ) ++validTiers; } /* did we use any of the tiers? */ if( !trackerCount ) { tr_free( trackers ); trackers = NULL; } } /* Regular announce value */ if( !trackerCount && tr_bencDictFindStr( meta, "announce", &str ) ) { char * url = tr_strstrip( tr_strdup( str ) ); if( tr_urlIsValidTracker( url ) ) { trackers = tr_new0( tr_tracker_info, 1 ); trackers[trackerCount].tier = 0; trackers[trackerCount].announce = tr_strdup( url ); trackers[trackerCount].scrape = tr_convertAnnounceToScrape( url ); trackers[trackerCount].id = 0; trackerCount++; /*fprintf( stderr, "single announce: [%s]\n", url );*/ } tr_free( url ); } inf->trackers = trackers; inf->trackerCount = trackerCount; return NULL; }