]> granicus.if.org Git - transmission/commitdiff
(libT) #1379: support "partial seeds" BEP 22
authorCharles Kerr <charles@transmissionbt.com>
Tue, 2 Dec 2008 19:46:51 +0000 (19:46 +0000)
committerCharles Kerr <charles@transmissionbt.com>
Tue, 2 Dec 2008 19:46:51 +0000 (19:46 +0000)
libtransmission/peer-common.h
libtransmission/peer-mgr.c
libtransmission/peer-msgs.c
libtransmission/rpcimpl.c
libtransmission/torrent.c
libtransmission/tracker.c
libtransmission/tracker.h
libtransmission/transmission.h
libtransmission/webseed.c

index b5f0c52d44ba97f243fa252aea07cee8f623c566..15f3c9f0a3752ae88046ff7370288ae61ce2c28b 100644 (file)
@@ -48,6 +48,7 @@ typedef enum
     TR_PEER_PEER_PROGRESS,
     TR_PEER_ERROR,
     TR_PEER_CANCEL,
+    TR_PEER_UPLOAD_ONLY,
     TR_PEER_NEED_REQ
 }
 PeerEventType;
@@ -61,6 +62,7 @@ typedef struct
     float            progress;     /* for PEER_PROGRESS */
     int              err;          /* errno for GOT_ERROR */
     int              wasPieceData; /* for GOT_DATA */
+    tr_bool          uploadOnly;   /* for UPLOAD_ONLY */
 }
 tr_peer_event;
 
index a452736d1bfe4e5531ab8aa99a3de56569548012..914ca7858aa2458efba93cde9be65ae98a14d0fb 100644 (file)
@@ -89,6 +89,13 @@ enum
 ***
 **/
 
+enum
+{
+    UPLOAD_ONLY_UKNOWN,
+    UPLOAD_ONLY_YES,
+    UPLOAD_ONLY_NO
+};
+
 /* We keep one of these for every peer we know about, whether
  * it's connected or not, so the struct must be small.
  * When our current connections underperform, we dip back
@@ -96,12 +103,14 @@ enum
 struct peer_atom
 {
     uint8_t     from;
-    uint8_t     flags; /* these match the added_f flags */
-    uint8_t     myflags; /* flags that aren't defined in added_f */
+    uint8_t     flags;       /* these match the added_f flags */
+    uint8_t     myflags;     /* flags that aren't defined in added_f */
+    uint8_t     uploadOnly;  /* UPLOAD_ONLY_ */
+    uint8_t     partialSeed;
     tr_port     port;
     uint16_t    numFails;
     tr_address  addr;
-    time_t      time; /* when the peer's connection status last changed */
+    time_t      time;        /* when the peer's connection status last changed */
     time_t      piece_data_time;
 };
 
@@ -980,6 +989,14 @@ peerCallbackFunc( void * vpeer,
 
     switch( e->eventType )
     {
+        case TR_PEER_UPLOAD_ONLY:
+            /* update our atom */
+            if( peer ) {
+                struct peer_atom * a = getExistingAtom( t, &peer->addr );
+                a->uploadOnly = e->uploadOnly ? UPLOAD_ONLY_YES : UPLOAD_ONLY_NO;
+            }
+            break;
+
         case TR_PEER_NEED_REQ:
             refillSoon( t );
             break;
@@ -1458,11 +1475,15 @@ tr_peerMgrGetPeers( tr_peerMgr *    manager,
         for( i = 0; i < peerCount; ++i, ++walk )
         {
             const tr_peer * peer = peers[i];
+            const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
+
             walk->addr = peer->addr;
             walk->port = peer->port;
             walk->flags = 0;
-            if( peerPrefersCrypto( peer ) ) walk->flags |= ADDED_F_ENCRYPTION_FLAG;
-            if( peer->progress >= 1.0 ) walk->flags |= ADDED_F_SEED_FLAG;
+            if( peerPrefersCrypto( peer ) )
+                walk->flags |= ADDED_F_ENCRYPTION_FLAG;
+            if( ( atom->uploadOnly == UPLOAD_ONLY_YES ) || ( peer->progress >= 1.0 ) )
+                walk->flags |= ADDED_F_SEED_FLAG;
         }
 
         assert( ( walk - pex ) == peerCount );
@@ -1884,11 +1905,22 @@ rechoke( Torrent * t )
     for( i = 0, size = 0; i < peerCount; ++i )
     {
         tr_peer * peer = peers[i];
+        struct peer_atom * atom = getExistingAtom( t, &peer->addr );
+
         if( peer->progress >= 1.0 ) /* choke all seeds */
+        {
             tr_peerMsgsSetChoke( peer->msgs, TRUE );
-        else if( chokeAll )
+        }
+        else if( atom->uploadOnly == UPLOAD_ONLY_YES ) /* choke partial seeds */
+        {
             tr_peerMsgsSetChoke( peer->msgs, TRUE );
-        else {
+        }
+        else if( chokeAll ) /* choke everyone if we're not uploading */
+        {
+            tr_peerMsgsSetChoke( peer->msgs, TRUE );
+        }
+        else
+        {
             struct ChokeData * n = &choke[size++];
             n->peer         = peer;
             n->isInterested = peer->peerIsInterested;
@@ -2175,7 +2207,8 @@ getPeerCandidates(                               Torrent * t,
             continue;
 
         /* no need to connect if we're both seeds... */
-        if( seed && ( atom->flags & ADDED_F_SEED_FLAG ) )
+        if( seed && ( ( atom->flags & ADDED_F_SEED_FLAG ) ||
+                      ( atom->uploadOnly == UPLOAD_ONLY_YES ) ) )
             continue;
 
         /* don't reconnect too often */
index 8bd462c26bee3e554a479ccb44ea815a1ac95109..7228b568b59c9722f329434d7ed261207a009ab6 100644 (file)
@@ -513,7 +513,7 @@ protocolSendHaveNone( tr_peermsgs * msgs )
 ***  EVENTS
 **/
 
-static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0 };
+static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0, 0 };
 
 static void
 publish( tr_peermsgs *   msgs,
@@ -537,10 +537,18 @@ fireError( tr_peermsgs * msgs,
 }
 
 static void
-fireNeedReq( tr_peermsgs * msgs )
+fireUploadOnly( tr_peermsgs * msgs, tr_bool uploadOnly )
 {
     tr_peer_event e = blankEvent;
+    e.eventType = TR_PEER_UPLOAD_ONLY;
+    e.uploadOnly = uploadOnly;
+    publish( msgs, &e );
+}
 
+static void
+fireNeedReq( tr_peermsgs * msgs )
+{
+    tr_peer_event e = blankEvent;
     e.eventType = TR_PEER_NEED_REQ;
     publish( msgs, &e );
 }
@@ -1086,9 +1094,9 @@ sendLtepHandshake( tr_peermsgs * msgs )
         pex = 1;
 
     tr_bencInitDict( &val, 4 );
-    tr_bencDictAddInt( &val, "e",
-                       msgs->session->encryptionMode != TR_CLEAR_PREFERRED );
+    tr_bencDictAddInt( &val, "e", msgs->session->encryptionMode != TR_CLEAR_PREFERRED );
     tr_bencDictAddInt( &val, "p", tr_sessionGetPeerPort( msgs->session ) );
+    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 )
@@ -1145,6 +1153,10 @@ parseLtepHandshake( tr_peermsgs *     msgs,
         }
     }
 
+    /* look for upload_only (BEP 21) */
+    if( tr_bencDictFindInt( &val, "upload_only", &i ) )
+        fireUploadOnly( msgs, i!=0 );
+
     /* get peer's listening port */
     if( tr_bencDictFindInt( &val, "p", &i ) )
     {
index 05f783c16d10e4298a43f3b0284a6e0bcf505e0b..0b8d011d9fee287f287098ddd1279ce32f08d0ad 100644 (file)
@@ -291,6 +291,8 @@ addField( const tr_torrent * tor,
         tr_bencDictAddInt( d, key, st->doneDate );
     else if( !strcmp( key, "downloadedEver" ) )
         tr_bencDictAddInt( d, key, st->downloadedEver );
+    else if( !strcmp( key, "downloaders" ) )
+        tr_bencDictAddInt( d, key, st->downloaders );
     else if( !strcmp( key, "downloadLimitMode" ) )
         tr_bencDictAddInt( d, key, tr_torrentGetSpeedMode( tor, TR_DOWN ) );
     else if( !strcmp( key, "downloadLimit" ) )
index 35dc07330d0cbdc5c5ac5c1ce3710e971754603a..525e177a785e41f682e1df5da31e1c290002c7b3 100644 (file)
@@ -783,9 +783,12 @@ tr_torrentStat( tr_torrent * tor )
     s->announceURL = ti ? ti->announce : NULL;
     s->scrapeURL   = ti ? ti->scrape   : NULL;
     tr_trackerStat( tc, s );
+
     tr_trackerGetCounts( tc, &s->timesCompleted,
-                         &s->leechers,
-                         &s->seeders );
+                             &s->leechers,
+                             &s->seeders,
+                             &s->downloaders );
+
     tr_peerMgrTorrentStats( tor->session->peerMgr,
                             tor->info.hash,
                             &s->peersKnown,
index e6c6184d6b0645c70383114d25d7bec867c43d94..b93edcaa3968ae0417717e24766eaea514ea66ac 100644 (file)
@@ -108,6 +108,7 @@ struct tr_tracker
     /* these are set from the latest tracker response... -1 is 'unknown' */
     int       timesDownloaded;
     int       seederCount;
+    int       downloaderCount;
     int       leecherCount;
     char *    trackerID;
 
@@ -538,7 +539,7 @@ onScrapeResponse( tr_session * session,
                   long         responseCode,
                   const void * response,
                   size_t       responseLen,
-                  void *       torrent_hash )
+                  void       * torrent_hash )
 {
     int          success = FALSE;
     int          retry;
@@ -583,9 +584,11 @@ onScrapeResponse( tr_session * session,
                 if( ( tr_bencDictFindInt( tordict, "downloaded", &itmp ) ) )
                     t->timesDownloaded = itmp;
 
+                if( ( tr_bencDictFindInt( tordict, "downloaders", &itmp ) ) )
+                    t->downloaderCount = itmp;
+
                 if( tr_bencDictFindDict( tordict, "flags", &flags ) )
-                    if( ( tr_bencDictFindInt( flags, "min_request_interval",
-                                              &itmp ) ) )
+                    if( ( tr_bencDictFindInt( flags, "min_request_interval", &itmp ) ) )
                         t->scrapeIntervalSec = i;
 
                 /* as per ticket #1045, safeguard against trackers returning
@@ -651,9 +654,9 @@ enum
     TR_REQ_STARTED,
     TR_REQ_COMPLETED,
     TR_REQ_STOPPED,
+    TR_REQ_PAUSED,     /* BEP 21 */
     TR_REQ_REANNOUNCE,
-    TR_REQ_SCRAPE,
-    TR_REQ_COUNT
+    TR_REQ_SCRAPE
 };
 
 struct tr_tracker_request
@@ -712,19 +715,23 @@ buildTrackerRequestURI( tr_tracker *       t,
 }
 
 static struct tr_tracker_request*
-createRequest(                 tr_session * session,
-                               tr_tracker * tracker,
-                           int reqtype )
+createRequest( tr_session * session,
+               tr_tracker * tracker,
+               int          reqtype )
 {
-    static const char*          strings[] =
-    { "started", "completed", "stopped", "", "err" };
-    const tr_torrent *          torrent = tr_torrentFindFromHash(
-        session, tracker->hash );
-    const tr_tracker_info *     address = getCurrentAddressFromTorrent(
-        tracker, torrent );
-    const int                   isStopping = reqtype == TR_REQ_STOPPED;
+    static const char* strings[] = { "started", "completed", "stopped", "paused", "", "err" };
+    const tr_torrent * torrent = tr_torrentFindFromHash( session, tracker->hash );
+    const tr_tracker_info * address = getCurrentAddressFromTorrent( tracker, torrent );
+    int isStopping;
     struct tr_tracker_request * req;
-    struct evbuffer *           url;
+    struct evbuffer * url;
+
+    /* BEP 21: In order to tell the tracker that a peer is a partial seed, it MUST send
+     * an event=paused parameter in every announce while it is a partial seed. */
+    if( tr_cpGetStatus( torrent->completion ) == TR_PARTIAL_SEED )
+        reqtype = TR_REQ_PAUSED;
+
+    isStopping = reqtype == TR_REQ_STOPPED;
 
     url = evbuffer_new( );
     evbuffer_add_printf( url, "%s", address->announce );
@@ -980,6 +987,7 @@ tr_trackerNew( const tr_torrent * torrent )
     t->announceMinIntervalSec   = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
     t->timesDownloaded          = -1;
     t->seederCount              = -1;
+    t->downloaderCount          = -1;
     t->leecherCount             = -1;
     t->lastAnnounceResponse     = -1;
     t->lastScrapeResponse       = -1;
@@ -1060,9 +1068,10 @@ tr_trackerCanManualAnnounce( const tr_tracker * t )
 
 void
 tr_trackerGetCounts( const tr_tracker * t,
-                     int *              setme_completedCount,
-                     int *              setme_leecherCount,
-                     int *              setme_seederCount )
+                     int              * setme_completedCount,
+                     int              * setme_leecherCount,
+                     int              * setme_seederCount,
+                     int              * setme_downloaderCount )
 {
     if( setme_completedCount )
         *setme_completedCount = t->timesDownloaded;
@@ -1072,6 +1081,9 @@ tr_trackerGetCounts( const tr_tracker * t,
 
     if( setme_seederCount )
         *setme_seederCount = t->seederCount;
+
+    if( setme_downloaderCount )
+        *setme_downloaderCount = t->downloaderCount;
 }
 
 void
index 6e238f795b6a224abe2fb4bda081df561836307c..93bccfdcd7da2ef9dc9e1d7da228e78129c9844a 100644 (file)
@@ -97,6 +97,7 @@ time_t                  tr_trackerGetManualAnnounceTime( const struct tr_tracker
 void                    tr_trackerGetCounts( const struct tr_tracker *,
                                              int * setme_completedCount,
                                              int * setme_leecherCount,
-                                             int * setme_seederCount );
+                                             int * setme_seederCount,
+                                             int * setme_downloaderCount );
 
 #endif
index cc912ad302365c46e4ba45f80b5390177079b605..ba96ba506afbe6eae4904dd2b87becab33f047b6 100644 (file)
@@ -1323,6 +1323,11 @@ typedef struct tr_stat
     /** Number of leechers that the tracker says this torrent has */
     int    leechers;
 
+    /** Number of downloaders that the tracker says this torrent has.
+        This is a new key introduced in BEP 21 and may not be supported by some trackers.
+        If the tracker doesn't support this key, the value here will be -1. */
+    int    downloaders;
+
     /** Number of finished downloads that the tracker says torrent has */
     int    timesCompleted;
 
index 9c238478d5f8bf1a74a3cd5dfc80b22422d9e430..54314c230771ae73aa55e67f0fce75c994c824d3 100644 (file)
@@ -51,7 +51,7 @@ struct tr_webseed
 ****
 ***/
 
-static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0 };
+static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0, 0 };
 
 static void
 publish( tr_webseed *    w,