TR_PEER_PEER_PROGRESS,
TR_PEER_ERROR,
TR_PEER_CANCEL,
+ TR_PEER_UPLOAD_ONLY,
TR_PEER_NEED_REQ
}
PeerEventType;
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;
***
**/
+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
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;
};
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;
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 );
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;
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 */
*** 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,
}
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 );
}
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 )
}
}
+ /* 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 ) )
{
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" ) )
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,
/* these are set from the latest tracker response... -1 is 'unknown' */
int timesDownloaded;
int seederCount;
+ int downloaderCount;
int leecherCount;
char * trackerID;
long responseCode,
const void * response,
size_t responseLen,
- void * torrent_hash )
+ void * torrent_hash )
{
int success = FALSE;
int retry;
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
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
}
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 );
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;
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;
if( setme_seederCount )
*setme_seederCount = t->seederCount;
+
+ if( setme_downloaderCount )
+ *setme_downloaderCount = t->downloaderCount;
}
void
void tr_trackerGetCounts( const struct tr_tracker *,
int * setme_completedCount,
int * setme_leecherCount,
- int * setme_seederCount );
+ int * setme_seederCount,
+ int * setme_downloaderCount );
#endif
/** 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;
****
***/
-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,