source: trunk/libtransmission/torrent.c @ 1603

Last change on this file since 1603 was 1603, checked in by joshe, 15 years ago

Show peers found and used when adding new peers.
Accept --without-foo in addition to --disable-foo in the configure script.

  • Property svn:keywords set to Date Rev Author Id
File size: 21.0 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 1603 2007-03-29 00:19:09Z joshe $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "transmission.h"
26#include "shared.h"
27
28/***********************************************************************
29 * Local prototypes
30 **********************************************************************/
31static tr_torrent_t * torrentRealInit( tr_handle_t *, tr_torrent_t * tor,
32                                       uint8_t *, int flags, int * error );
33static void torrentReallyStop( tr_torrent_t * );
34static void downloadLoop( void * );
35
36void tr_setUseCustomUpload( tr_torrent_t * tor, int limit )
37{
38    tor->customUploadLimit = limit;
39}
40
41void tr_setUseCustomDownload( tr_torrent_t * tor, int limit )
42{
43    tor->customDownloadLimit = limit;
44}
45
46void tr_setUploadLimit( tr_torrent_t * tor, int limit )
47{
48    tr_rcSetLimit( tor->upload, limit );
49}
50
51void tr_setDownloadLimit( tr_torrent_t * tor, int limit )
52{
53    tr_rcSetLimit( tor->download, limit );
54}
55
56tr_torrent_t * tr_torrentInit( tr_handle_t * h, const char * path,
57                               uint8_t * hash, int flags, int * error )
58{
59    tr_torrent_t  * tor = calloc( sizeof( tr_torrent_t ), 1 );
60    int             saveCopy = ( TR_FLAG_SAVE & flags );
61
62    /* Parse torrent file */
63    if( tr_metainfoParse( &tor->info, h->tag, path, NULL, saveCopy ) )
64    {
65        *error = TR_EINVALID;
66        free( tor );
67        return NULL;
68    }
69
70    return torrentRealInit( h, tor, hash, flags, error );
71}
72
73tr_torrent_t * tr_torrentInitSaved( tr_handle_t * h, const char * hashStr,
74                                    int flags, int * error )
75{
76    tr_torrent_t  * tor = calloc( sizeof( tr_torrent_t ), 1 );
77
78    /* Parse torrent file */
79    if( tr_metainfoParse( &tor->info, h->tag, NULL, hashStr, 0 ) )
80    {
81        *error = TR_EINVALID;
82        free( tor );
83        return NULL;
84    }
85
86    return torrentRealInit( h, tor, NULL, ( TR_FLAG_SAVE | flags ), error );
87}
88
89/***********************************************************************
90 * tr_torrentInit
91 ***********************************************************************
92 * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
93 * to fill it.
94 **********************************************************************/
95static tr_torrent_t * torrentRealInit( tr_handle_t * h, tr_torrent_t * tor,
96                                       uint8_t * hash, int flags, int * error )
97{
98    tr_torrent_t  * tor_tmp;
99    tr_info_t     * inf;
100    int             i;
101   
102    inf         = &tor->info;
103    inf->flags |= flags;
104
105    tr_sharedLock( h->shared );
106
107    /* Make sure this torrent is not already open */
108    for( tor_tmp = h->torrentList; tor_tmp; tor_tmp = tor_tmp->next )
109    {
110        if( !memcmp( tor->info.hash, tor_tmp->info.hash,
111                     SHA_DIGEST_LENGTH ) )
112        {
113            if( NULL != hash )
114            {
115                memcpy( hash, tor->info.hash, SHA_DIGEST_LENGTH );
116            }
117            *error = TR_EDUPLICATE;
118            tr_metainfoFree( &tor->info );
119            free( tor );
120            tr_sharedUnlock( h->shared );
121            return NULL;
122        }
123    }
124
125    tor->handle   = h;
126    tor->status   = TR_STATUS_PAUSE;
127    tor->id       = h->id;
128    tor->key      = h->key;
129    tor->azId     = h->azId;
130    tor->finished = 0;
131
132    /* Escaped info hash for HTTP queries */
133    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
134    {
135        snprintf( &tor->escapedHashString[3*i],
136                  sizeof( tor->escapedHashString ) - 3 * i,
137                  "%%%02x", inf->hash[i] );
138    }
139
140    /* Block size: usually 16 ko, or less if we have to */
141    tor->blockSize  = MIN( inf->pieceSize, 1 << 14 );
142    tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
143                        tor->blockSize;
144    tor->completion = tr_cpInit( tor );
145
146    tr_lockInit( &tor->lock );
147    tr_condInit( &tor->cond );
148
149    tor->upload         = tr_rcInit();
150    tor->download       = tr_rcInit();
151    tor->swarmspeed     = tr_rcInit();
152 
153    /* We have a new torrent */
154    tor->publicPort = tr_sharedGetPublicPort( h->shared );
155    tor->prev       = NULL;
156    tor->next       = h->torrentList;
157    if( tor->next )
158    {
159        tor->next->prev = tor;
160    }
161    h->torrentList = tor;
162    (h->torrentCount)++;
163
164    tr_sharedUnlock( h->shared );
165
166    if( !h->isPortSet )
167    {
168        tr_setBindPort( h, TR_DEFAULT_PORT );
169    }
170
171    return tor;
172}
173
174tr_info_t * tr_torrentInfo( tr_torrent_t * tor )
175{
176    return &tor->info;
177}
178
179/***********************************************************************
180 * tr_torrentScrape     
181 **********************************************************************/
182int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
183{
184    return tr_trackerScrape( tor, s, l, d );
185}
186
187void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
188{
189    tor->destination = strdup( path );
190    tr_ioLoadResume( tor );
191}
192
193char * tr_torrentGetFolder( tr_torrent_t * tor )
194{
195    return tor->destination;
196}
197
198void tr_torrentStart( tr_torrent_t * tor )
199{
200    char name[32];
201
202    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
203    {
204        /* Join the thread first */
205        torrentReallyStop( tor );
206    }
207
208    tr_lockLock( &tor->lock );
209
210    tor->downloadedPrev += tor->downloadedCur;
211    tor->downloadedCur   = 0;
212    tor->uploadedPrev   += tor->uploadedCur;
213    tor->uploadedCur     = 0;
214
215    tor->status  = TR_STATUS_CHECK;
216    tor->error   = TR_OK;
217    tor->tracker = tr_trackerInit( tor );
218
219    tor->date = tr_date();
220    tor->die = 0;
221    snprintf( name, sizeof( name ), "torrent %p", tor );
222
223    tr_lockUnlock( &tor->lock );
224
225    tr_threadCreate( &tor->thread, downloadLoop, tor, name );
226}
227
228static void torrentStop( tr_torrent_t * tor )
229{
230    tr_trackerStopped( tor->tracker );
231    tr_rcReset( tor->download );
232    tr_rcReset( tor->upload );
233    tr_rcReset( tor->swarmspeed );
234    tor->status = TR_STATUS_STOPPING;
235    tor->stopDate = tr_date();
236}
237
238void tr_torrentStop( tr_torrent_t * tor )
239{
240    tr_lockLock( &tor->lock );
241    torrentStop( tor );
242
243    /* Don't return until the files are closed, so the UI can trash
244     * them if requested */
245    tr_condWait( &tor->cond, &tor->lock );
246    tr_lockUnlock( &tor->lock );
247}
248
249/***********************************************************************
250 * torrentReallyStop
251 ***********************************************************************
252 * Joins the download thread and frees/closes everything related to it.
253 **********************************************************************/
254static void torrentReallyStop( tr_torrent_t * tor )
255{
256    int i;
257
258    tor->die = 1;
259    tr_threadJoin( &tor->thread );
260
261    tr_trackerClose( tor->tracker );
262    tor->tracker = NULL;
263
264    tr_lockLock( &tor->lock );
265    for( i = 0; i < tor->peerCount; i++ )
266    {
267        tr_peerDestroy( tor->peers[i] );
268    }
269    tor->peerCount = 0;
270    tr_lockUnlock( &tor->lock );
271}
272
273int tr_getFinished( tr_torrent_t * tor )
274{
275    if( tor->finished )
276    {
277        tor->finished = 0;
278        return 1;
279    }
280    return 0;
281}
282
283void tr_manualUpdate( tr_torrent_t * tor )
284{
285    int peerCount, new;
286    uint8_t * peerCompact;
287
288    if( !( tor->status & TR_STATUS_ACTIVE ) )
289        return;
290   
291    tr_lockLock( &tor->lock );
292    tr_trackerAnnouncePulse( tor->tracker, &peerCount, &peerCompact, 1 );
293    new = 0;
294    if( peerCount > 0 )
295    {
296        new = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
297                              peerCompact, peerCount );
298        free( peerCompact );
299    }
300    tr_dbg( "got %i peers from manual announce, used %i", peerCount, new );
301    tr_lockUnlock( &tor->lock );
302}
303
304tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
305{
306    tr_stat_t * s;
307    tr_peer_t * peer;
308    tr_info_t * inf = &tor->info;
309    tr_tracker_t * tc;
310    int i;
311
312    tor->statCur = ( tor->statCur + 1 ) % 2;
313    s = &tor->stats[tor->statCur];
314
315    if( ( tor->status & TR_STATUS_STOPPED ) ||
316        ( ( tor->status & TR_STATUS_STOPPING ) &&
317          tr_date() > tor->stopDate + 60000 ) )
318    {
319        torrentReallyStop( tor );
320        tor->status = TR_STATUS_PAUSE;
321    }
322
323    tr_lockLock( &tor->lock );
324
325    s->status = tor->status;
326    s->error  = tor->error;
327    memcpy( s->errorString, tor->errorString,
328            sizeof( s->errorString ) );
329
330    tc = tor->tracker;
331    s->cannotConnect = tr_trackerCannotConnect( tc );
332    s->tracker = ( tc ? tr_trackerGet( tc ) : &inf->trackerList[0].list[0] );
333
334    s->peersTotal       = 0;
335    memset( s->peersFrom, 0, sizeof( s->peersFrom ) );
336    s->peersUploading   = 0;
337    s->peersDownloading = 0;
338   
339    for( i = 0; i < tor->peerCount; i++ )
340    {
341        peer = tor->peers[i];
342   
343        if( tr_peerIsConnected( peer ) )
344        {
345            (s->peersTotal)++;
346            (s->peersFrom[ tr_peerIsFrom( peer ) ])++;
347            if( tr_peerAmInterested( peer ) && !tr_peerIsChoking( peer ) )
348            {
349                (s->peersUploading)++;
350            }
351            if( !tr_peerAmChoking( peer ) )
352            {
353                (s->peersDownloading)++;
354            }
355        }
356    }
357
358    s->progress = tr_cpCompletionAsFloat( tor->completion );
359    if( tor->status & TR_STATUS_DOWNLOAD )
360    {
361        s->rateDownload = tr_rcRate( tor->download );
362    }
363    else
364    {
365        /* tr_rcRate() doesn't make the difference between 'piece'
366           messages and other messages, which causes a non-zero
367           download rate even tough we are not downloading. So we
368           force it to zero not to confuse the user. */
369        s->rateDownload = 0.0;
370    }
371    s->rateUpload = tr_rcRate( tor->upload );
372   
373    s->seeders  = tr_trackerSeeders( tc );
374    s->leechers = tr_trackerLeechers( tc );
375    s->completedFromTracker = tr_trackerDownloaded( tc );
376
377    s->swarmspeed = tr_rcRate( tor->swarmspeed );
378
379    if( s->rateDownload < 0.1 )
380    {
381        s->eta = -1;
382    }
383    else
384    {
385        s->eta = ( 1.0 - s->progress ) *
386            (float) inf->totalSize / s->rateDownload / 1024.0;
387    }
388
389    s->downloaded = tor->downloadedCur + tor->downloadedPrev;
390    s->uploaded   = tor->uploadedCur   + tor->uploadedPrev;
391   
392    if( s->downloaded == 0 )
393    {
394        s->ratio = s->uploaded == 0 ? TR_RATIO_NA : TR_RATIO_INF;
395    }
396    else
397    {
398        s->ratio = (float)s->uploaded / (float)s->downloaded;
399    }
400   
401    tr_lockUnlock( &tor->lock );
402
403    return s;
404}
405
406tr_peer_stat_t * tr_torrentPeers( tr_torrent_t * tor, int * peerCount )
407{
408    tr_peer_stat_t * peers;
409
410    tr_lockLock( &tor->lock );
411
412    *peerCount = tor->peerCount;
413   
414    peers = (tr_peer_stat_t *) calloc( tor->peerCount, sizeof( tr_peer_stat_t ) );
415    if (peers != NULL)
416    {
417        tr_peer_t * peer;
418        struct in_addr * addr;
419        int i;
420        for( i = 0; i < tor->peerCount; i++ )
421        {
422            peer = tor->peers[i];
423           
424            addr = tr_peerAddress( peer );
425            if( NULL != addr )
426            {
427                tr_netNtop( addr, peers[i].addr,
428                           sizeof( peers[i].addr ) );
429            }
430           
431            peers[i].client = tr_clientForId(tr_peerId(peer));
432           
433            peers[i].isConnected   = tr_peerIsConnected( peer );
434            peers[i].from          = tr_peerIsFrom( peer );
435            peers[i].progress      = tr_peerProgress( peer );
436            peers[i].port          = tr_peerPort( peer );
437           
438            if( ( peers[i].isDownloading = !tr_peerAmChoking( peer ) ) )
439            {
440                peers[i].uploadToRate = tr_peerUploadRate( peer );
441            }
442            if( ( peers[i].isUploading = ( tr_peerAmInterested( peer ) &&
443                                           !tr_peerIsChoking( peer ) ) ) )
444            {
445                peers[i].downloadFromRate = tr_peerDownloadRate( peer );
446            }
447        }
448    }
449   
450    tr_lockUnlock( &tor->lock );
451   
452    return peers;
453}
454
455void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount )
456{
457    int i;
458
459    if (peers == NULL)
460        return;
461
462    for (i = 0; i < peerCount; i++)
463        free( peers[i].client );
464
465    free( peers );
466}
467
468void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
469{
470    int i, j, piece;
471    float interval;
472
473    tr_lockLock( &tor->lock );
474    interval = (float)tor->info.pieceCount / (float)size;
475    for( i = 0; i < size; i++ )
476    {
477        piece = i * interval;
478
479        if( tr_cpPieceIsComplete( tor->completion, piece ) )
480        {
481            tab[i] = -1;
482            continue;
483        }
484
485        tab[i] = 0;
486        for( j = 0; j < tor->peerCount; j++ )
487        {
488            if( tr_peerBitfield( tor->peers[j] ) &&
489                tr_bitfieldHas( tr_peerBitfield( tor->peers[j] ), piece ) )
490            {
491                (tab[i])++;
492            }
493        }
494    }
495    tr_lockUnlock( &tor->lock );
496}
497
498float * tr_torrentCompletion( tr_torrent_t * tor )
499{
500    tr_info_t * inf = &tor->info;
501    int         piece, file;
502    float     * ret, prog, weight;
503    uint64_t    piecemax, piecesize;
504    uint64_t    filestart, fileoff, filelen, blockend, blockused;
505
506    tr_lockLock( &tor->lock );
507
508    ret       = calloc( inf->fileCount, sizeof( float ) );
509    file      = 0;
510    piecemax  = inf->pieceSize;
511    filestart = 0;
512    fileoff   = 0;
513    piece     = 0;
514    while( inf->pieceCount > piece )
515    {
516        assert( file < inf->fileCount );
517        assert( filestart + fileoff < inf->totalSize );
518        filelen    = inf->files[file].length;
519        piecesize  = tr_pieceSize( piece );
520        blockend   = MIN( filestart + filelen, piecemax * piece + piecesize );
521        blockused  = blockend - ( filestart + fileoff );
522        weight     = ( filelen ? ( float )blockused / ( float )filelen : 1.0 );
523        prog       = tr_cpPercentBlocksInPiece( tor->completion, piece );
524        ret[file] += prog * weight;
525        fileoff   += blockused;
526        assert( -0.1 < prog   && 1.1 > prog );
527        assert( -0.1 < weight && 1.1 > weight );
528        if( fileoff == filelen )
529        {
530            ret[file] = MIN( 1.0, ret[file] );
531            ret[file] = MAX( 0.0, ret[file] );
532            filestart += fileoff;
533            fileoff    = 0;
534            file++;
535        }
536        if( filestart + fileoff >= piecemax * piece + piecesize )
537        {
538            piece++;
539        }
540    }
541
542    tr_lockUnlock( &tor->lock );
543
544    return ret;
545}
546
547void tr_torrentAmountFinished( tr_torrent_t * tor, float * tab, int size )
548{
549    int i, piece;
550    float interval;
551
552    tr_lockLock( &tor->lock );
553    interval = (float)tor->info.pieceCount / (float)size;
554    for( i = 0; i < size; i++ )
555    {
556        piece = i * interval;
557        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
558    }
559    tr_lockUnlock( &tor->lock );
560}
561
562void tr_torrentRemoveSaved( tr_torrent_t * tor )
563{
564    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
565}
566
567/***********************************************************************
568 * tr_torrentClose
569 ***********************************************************************
570 * Frees memory allocated by tr_torrentInit.
571 **********************************************************************/
572void tr_torrentClose( tr_handle_t * h, tr_torrent_t * tor )
573{
574    tr_info_t * inf = &tor->info;
575
576    if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
577    {
578        /* Join the thread first */
579        torrentReallyStop( tor );
580    }
581
582    tr_sharedLock( h->shared );
583
584    h->torrentCount--;
585
586    tr_lockClose( &tor->lock );
587    tr_condClose( &tor->cond );
588    tr_cpClose( tor->completion );
589
590    tr_rcClose( tor->upload );
591    tr_rcClose( tor->download );
592    tr_rcClose( tor->swarmspeed );
593
594    if( tor->destination )
595    {
596        free( tor->destination );
597    }
598
599    tr_metainfoFree( inf );
600
601    if( tor->prev )
602    {
603        tor->prev->next = tor->next;
604    }
605    else
606    {
607        h->torrentList = tor->next;
608    }
609    if( tor->next )
610    {
611        tor->next->prev = tor->prev;
612    }
613    free( tor );
614
615    tr_sharedUnlock( h->shared );
616}
617
618int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
619{
620    int i;
621    tr_peer_t * otherPeer;
622
623    if( tor->peerCount >= TR_MAX_PEER_COUNT )
624    {
625        tr_peerDestroy(  peer );
626        return 0;
627    }
628
629    /* Don't accept two connections from the same IP */
630    for( i = 0; i < tor->peerCount; i++ )
631    {
632        otherPeer = tor->peers[i];
633        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
634        {
635            tr_peerDestroy(  peer );
636            return 0;
637        }
638    }
639
640    tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE );
641    tr_peerSetTorrent( peer, tor );
642    tor->peers[tor->peerCount++] = peer;
643
644    return 1;
645}
646
647int tr_torrentAddCompact( tr_torrent_t * tor, int from,
648                           uint8_t * buf, int count )
649{
650    struct in_addr addr;
651    in_port_t port;
652    int i, added;
653    tr_peer_t * peer;
654
655    added = 0;
656    for( i = 0; i < count; i++ )
657    {
658        memcpy( &addr, buf, 4 ); buf += 4;
659        memcpy( &port, buf, 2 ); buf += 2;
660
661        peer = tr_peerInit( addr, port, -1, from );
662        added += tr_torrentAttachPeer( tor, peer );
663    }
664
665    return added;
666}
667
668/***********************************************************************
669 * downloadLoop
670 **********************************************************************/
671static void downloadLoop( void * _tor )
672{
673    tr_torrent_t * tor = _tor;
674    int            i, ret;
675    int            peerCount, used;
676    uint8_t      * peerCompact;
677    tr_peer_t    * peer;
678
679    tr_lockLock( &tor->lock );
680
681    tr_cpReset( tor->completion );
682    tor->io     = tr_ioInit( tor );
683    tor->status = tr_cpIsSeeding( tor->completion ) ?
684                      TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
685
686    while( !tor->die )
687    {
688        tr_lockUnlock( &tor->lock );
689        tr_wait( 20 );
690        tr_lockLock( &tor->lock );
691
692        /* Are we finished ? */
693        if( ( tor->status & TR_STATUS_DOWNLOAD ) &&
694            tr_cpIsSeeding( tor->completion ) )
695        {
696            /* Done */
697            tor->status = TR_STATUS_SEED;
698                        tor->finished = 1;
699            tr_trackerCompleted( tor->tracker );
700            tr_ioSync( tor->io );
701        }
702
703        /* Try to get new peers or to send a message to the tracker */
704        tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
705        used = 0;
706        if( peerCount > 0 )
707        {
708            used = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
709                                         peerCompact, peerCount );
710            free( peerCompact );
711        }
712        tr_dbg( "got %i peers from announce, used %i", peerCount, used );
713        if( tor->status & TR_STATUS_STOPPED )
714        {
715            break;
716        }
717
718        /* Stopping: make sure all files are closed and stop talking
719           to peers */
720        if( tor->status & TR_STATUS_STOPPING )
721        {
722            if( tor->io )
723            {
724                tr_ioClose( tor->io ); tor->io = NULL;
725                tr_condSignal( &tor->cond );
726            }
727            continue;
728        }
729
730        /* Shuffle peers */
731        if( tor->peerCount > 1 )
732        {
733            peer = tor->peers[0];
734            memmove( &tor->peers[0], &tor->peers[1],
735                    ( tor->peerCount - 1 ) * sizeof( void * ) );
736            tor->peers[tor->peerCount - 1] = peer;
737        }
738
739        /* Receive/send messages */
740        for( i = 0; i < tor->peerCount; )
741        {
742            peer = tor->peers[i];
743
744            ret = tr_peerPulse( peer );
745            if( ret & TR_ERROR_IO_MASK )
746            {
747                tr_err( "Fatal error, stopping download (%d)", ret );
748                torrentStop( tor );
749                tor->error = ret;
750                snprintf( tor->errorString, sizeof( tor->errorString ),
751                          "%s", tr_errorString( ret ) );
752                break;
753            }
754            if( ret )
755            {
756                tr_peerDestroy( peer );
757                tor->peerCount--;
758                memmove( &tor->peers[i], &tor->peers[i+1],
759                         ( tor->peerCount - i ) * sizeof( void * ) );
760                continue;
761            }
762            i++;
763        }
764    }
765
766    tr_lockUnlock( &tor->lock );
767
768    if( tor->io )
769    {
770        tr_ioClose( tor->io ); tor->io = NULL;
771        tr_condSignal( &tor->cond );
772    }
773
774    tor->status = TR_STATUS_STOPPED;
775}
776
Note: See TracBrowser for help on using the repository browser.