transmission 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +0 -0
- data/LICENSE +23 -0
- data/README +50 -0
- data/Rakefile +41 -0
- data/ext/bencode.c +335 -0
- data/ext/bencode.h +64 -0
- data/ext/choking.c +335 -0
- data/ext/choking.h +30 -0
- data/ext/clients.c +90 -0
- data/ext/clients.h +25 -0
- data/ext/completion.c +266 -0
- data/ext/completion.h +67 -0
- data/ext/extconf.rb +3 -0
- data/ext/fastresume.h +375 -0
- data/ext/fdlimit.c +297 -0
- data/ext/fdlimit.h +36 -0
- data/ext/inout.c +620 -0
- data/ext/inout.h +38 -0
- data/ext/internal.h +202 -0
- data/ext/metainfo.c +339 -0
- data/ext/metainfo.h +44 -0
- data/ext/net.c +405 -0
- data/ext/net.h +54 -0
- data/ext/peer.c +584 -0
- data/ext/peer.h +55 -0
- data/ext/peermessages.h +326 -0
- data/ext/peerparse.h +564 -0
- data/ext/peerutils.h +394 -0
- data/ext/platform.c +196 -0
- data/ext/platform.h +63 -0
- data/ext/r_transmission.c +1135 -0
- data/ext/ratecontrol.c +170 -0
- data/ext/ratecontrol.h +33 -0
- data/ext/sha1.c +235 -0
- data/ext/sha1.h +68 -0
- data/ext/tracker.c +767 -0
- data/ext/tracker.h +53 -0
- data/ext/transmission.c +776 -0
- data/ext/transmission.h +317 -0
- data/ext/utils.c +142 -0
- data/ext/utils.h +164 -0
- data/lib/transmission.rb +75 -0
- metadata +98 -0
data/ext/tracker.h
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: tracker.h 261 2006-05-29 21:27:31Z titer $
|
3
|
+
*
|
4
|
+
* Copyright (c) 2005-2006 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
|
+
#ifndef TR_TRACKER_H
|
26
|
+
#define TR_TRACKER_H 1
|
27
|
+
|
28
|
+
typedef struct tr_tracker_s tr_tracker_t;
|
29
|
+
|
30
|
+
tr_tracker_t * tr_trackerInit ( tr_torrent_t * );
|
31
|
+
void tr_trackerChangePort( tr_tracker_t *, int );
|
32
|
+
int tr_trackerPulse ( tr_tracker_t * );
|
33
|
+
void tr_trackerCompleted ( tr_tracker_t * );
|
34
|
+
void tr_trackerStopped ( tr_tracker_t * );
|
35
|
+
void tr_trackerClose ( tr_tracker_t * );
|
36
|
+
|
37
|
+
/***********************************************************************
|
38
|
+
* tr_trackerSeeders
|
39
|
+
***********************************************************************
|
40
|
+
* Looks for the seeders/leechers as returned by the tracker.
|
41
|
+
**********************************************************************/
|
42
|
+
int tr_trackerSeeders ( tr_tracker_t * );
|
43
|
+
|
44
|
+
/***********************************************************************
|
45
|
+
* tr_trackerLeechers
|
46
|
+
***********************************************************************
|
47
|
+
* Looks for the seeders/leechers as returned by the tracker.
|
48
|
+
**********************************************************************/
|
49
|
+
int tr_trackerLeechers ( tr_tracker_t * );
|
50
|
+
|
51
|
+
int tr_trackerScrape ( tr_torrent_t *, int *, int * );
|
52
|
+
|
53
|
+
#endif
|
data/ext/transmission.c
ADDED
@@ -0,0 +1,776 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: transmission.c 414 2006-06-20 14:29:12Z titer $
|
3
|
+
*
|
4
|
+
* Copyright (c) 2005-2006 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
|
+
|
27
|
+
/***********************************************************************
|
28
|
+
* Local prototypes
|
29
|
+
**********************************************************************/
|
30
|
+
static tr_torrent_t * torrentRealInit( tr_handle_t *, tr_torrent_t * tor,
|
31
|
+
int flags, int * error );
|
32
|
+
static void torrentReallyStop( tr_torrent_t * );
|
33
|
+
static void downloadLoop( void * );
|
34
|
+
static void acceptLoop( void * );
|
35
|
+
static void acceptStop( tr_handle_t * h );
|
36
|
+
|
37
|
+
/***********************************************************************
|
38
|
+
* tr_init
|
39
|
+
***********************************************************************
|
40
|
+
* Allocates a tr_handle_t structure and initializes a few things
|
41
|
+
**********************************************************************/
|
42
|
+
tr_handle_t * tr_init()
|
43
|
+
{
|
44
|
+
tr_handle_t * h;
|
45
|
+
int i, r;
|
46
|
+
|
47
|
+
tr_netResolveThreadInit();
|
48
|
+
|
49
|
+
h = calloc( sizeof( tr_handle_t ), 1 );
|
50
|
+
|
51
|
+
/* Generate a peer id : "-TRxxyy-" + 12 random alphanumeric
|
52
|
+
characters, where xx is the major version number and yy the
|
53
|
+
minor version number (Azureus-style) */
|
54
|
+
sprintf( h->id, "-TR%02d%02d-", VERSION_MAJOR, VERSION_MINOR );
|
55
|
+
for( i = 8; i < 20; i++ )
|
56
|
+
{
|
57
|
+
r = tr_rand( 36 );
|
58
|
+
h->id[i] = ( r < 26 ) ? ( 'a' + r ) : ( '0' + r - 26 ) ;
|
59
|
+
}
|
60
|
+
|
61
|
+
/* Random key */
|
62
|
+
for( i = 0; i < 20; i++ )
|
63
|
+
{
|
64
|
+
r = tr_rand( 36 );
|
65
|
+
h->key[i] = ( r < 26 ) ? ( 'a' + r ) : ( '0' + r - 26 ) ;
|
66
|
+
}
|
67
|
+
|
68
|
+
/* Don't exit when writing on a broken socket */
|
69
|
+
signal( SIGPIPE, SIG_IGN );
|
70
|
+
|
71
|
+
/* Initialize rate and file descripts controls */
|
72
|
+
h->upload = tr_rcInit();
|
73
|
+
h->download = tr_rcInit();
|
74
|
+
h->fdlimit = tr_fdInit();
|
75
|
+
h->choking = tr_chokingInit( h );
|
76
|
+
|
77
|
+
h->bindPort = -1;
|
78
|
+
h->bindSocket = -1;
|
79
|
+
|
80
|
+
h->acceptDie = 0;
|
81
|
+
tr_lockInit( &h->acceptLock );
|
82
|
+
tr_threadCreate( &h->acceptThread, acceptLoop, h );
|
83
|
+
|
84
|
+
return h;
|
85
|
+
}
|
86
|
+
|
87
|
+
/***********************************************************************
|
88
|
+
* tr_setBindPort
|
89
|
+
***********************************************************************
|
90
|
+
*
|
91
|
+
**********************************************************************/
|
92
|
+
void tr_setBindPort( tr_handle_t * h, int port )
|
93
|
+
{
|
94
|
+
int sock = -1;
|
95
|
+
tr_torrent_t * tor;
|
96
|
+
|
97
|
+
if( h->bindPort == port )
|
98
|
+
return;
|
99
|
+
|
100
|
+
#ifndef BEOS_NETSERVER
|
101
|
+
/* BeOS net_server seems to be unable to set incoming connections to
|
102
|
+
non-blocking. Too bad. */
|
103
|
+
if( !tr_fdSocketWillCreate( h->fdlimit, 0 ) )
|
104
|
+
{
|
105
|
+
/* XXX should handle failure here in a better way */
|
106
|
+
sock = tr_netBind( port );
|
107
|
+
}
|
108
|
+
#else
|
109
|
+
return;
|
110
|
+
#endif
|
111
|
+
|
112
|
+
tr_lockLock( &h->acceptLock );
|
113
|
+
|
114
|
+
h->bindPort = port;
|
115
|
+
|
116
|
+
for( tor = h->torrentList; tor; tor = tor->next )
|
117
|
+
{
|
118
|
+
tr_lockLock( &tor->lock );
|
119
|
+
if( NULL != tor->tracker )
|
120
|
+
{
|
121
|
+
tr_trackerChangePort( tor->tracker, port );
|
122
|
+
}
|
123
|
+
tr_lockUnlock( &tor->lock );
|
124
|
+
}
|
125
|
+
|
126
|
+
if( h->bindSocket > -1 )
|
127
|
+
{
|
128
|
+
tr_netClose( h->bindSocket );
|
129
|
+
tr_fdSocketClosed( h->fdlimit, 0 );
|
130
|
+
}
|
131
|
+
|
132
|
+
h->bindSocket = sock;
|
133
|
+
|
134
|
+
tr_lockUnlock( &h->acceptLock );
|
135
|
+
}
|
136
|
+
|
137
|
+
/***********************************************************************
|
138
|
+
* tr_setUploadLimit
|
139
|
+
***********************************************************************
|
140
|
+
*
|
141
|
+
**********************************************************************/
|
142
|
+
void tr_setUploadLimit( tr_handle_t * h, int limit )
|
143
|
+
{
|
144
|
+
tr_rcSetLimit( h->upload, limit );
|
145
|
+
tr_chokingSetLimit( h->choking, limit );
|
146
|
+
}
|
147
|
+
|
148
|
+
/***********************************************************************
|
149
|
+
* tr_setDownloadLimit
|
150
|
+
***********************************************************************
|
151
|
+
*
|
152
|
+
**********************************************************************/
|
153
|
+
void tr_setDownloadLimit( tr_handle_t * h, int limit )
|
154
|
+
{
|
155
|
+
tr_rcSetLimit( h->download, limit );
|
156
|
+
}
|
157
|
+
|
158
|
+
/***********************************************************************
|
159
|
+
* tr_torrentRates
|
160
|
+
***********************************************************************
|
161
|
+
*
|
162
|
+
**********************************************************************/
|
163
|
+
void tr_torrentRates( tr_handle_t * h, float * dl, float * ul )
|
164
|
+
{
|
165
|
+
tr_torrent_t * tor;
|
166
|
+
|
167
|
+
*dl = 0.0;
|
168
|
+
*ul = 0.0;
|
169
|
+
for( tor = h->torrentList; tor; tor = tor->next )
|
170
|
+
{
|
171
|
+
tr_lockLock( &tor->lock );
|
172
|
+
if( tor->status & TR_STATUS_DOWNLOAD )
|
173
|
+
*dl += tr_rcRate( tor->download );
|
174
|
+
*ul += tr_rcRate( tor->upload );
|
175
|
+
tr_lockUnlock( &tor->lock );
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
tr_torrent_t * tr_torrentInit( tr_handle_t * h, const char * path,
|
180
|
+
int flags, int * error )
|
181
|
+
{
|
182
|
+
int err_code;
|
183
|
+
tr_torrent_t * tor = calloc( sizeof( tr_torrent_t ), 1 );
|
184
|
+
int saveCopy = ( TR_FSAVEPRIVATE & flags );
|
185
|
+
|
186
|
+
/* Parse torrent file */
|
187
|
+
if( (err_code = tr_metainfoParse( &tor->info, path, NULL, saveCopy )) )
|
188
|
+
{
|
189
|
+
*error = err_code;
|
190
|
+
free( tor );
|
191
|
+
return NULL;
|
192
|
+
}
|
193
|
+
|
194
|
+
return torrentRealInit( h, tor, flags, error );
|
195
|
+
}
|
196
|
+
|
197
|
+
tr_torrent_t * tr_torrentInitSaved( tr_handle_t * h, const char * hashStr,
|
198
|
+
int flags, int * error )
|
199
|
+
{
|
200
|
+
tr_torrent_t * tor = calloc( sizeof( tr_torrent_t ), 1 );
|
201
|
+
|
202
|
+
/* Parse torrent file */
|
203
|
+
if( tr_metainfoParse( &tor->info, NULL, hashStr, 0 ) )
|
204
|
+
{
|
205
|
+
*error = TR_EINVALID;
|
206
|
+
free( tor );
|
207
|
+
return NULL;
|
208
|
+
}
|
209
|
+
|
210
|
+
return torrentRealInit( h, tor, ( TR_FSAVEPRIVATE | flags ), error );
|
211
|
+
}
|
212
|
+
|
213
|
+
/***********************************************************************
|
214
|
+
* tr_torrentInit
|
215
|
+
***********************************************************************
|
216
|
+
* Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
|
217
|
+
* to fill it.
|
218
|
+
**********************************************************************/
|
219
|
+
static tr_torrent_t * torrentRealInit( tr_handle_t * h, tr_torrent_t * tor,
|
220
|
+
int flags, int * error )
|
221
|
+
{
|
222
|
+
tr_torrent_t * tor_tmp;
|
223
|
+
tr_info_t * inf;
|
224
|
+
int i;
|
225
|
+
char * s1, * s2;
|
226
|
+
|
227
|
+
inf = &tor->info;
|
228
|
+
inf->flags = flags;
|
229
|
+
|
230
|
+
/* Make sure this torrent is not already open */
|
231
|
+
for( tor_tmp = h->torrentList; tor_tmp; tor_tmp = tor_tmp->next )
|
232
|
+
{
|
233
|
+
if( !memcmp( tor->info.hash, tor_tmp->info.hash,
|
234
|
+
SHA_DIGEST_LENGTH ) )
|
235
|
+
{
|
236
|
+
*error = TR_EDUPLICATE;
|
237
|
+
free( tor );
|
238
|
+
return NULL;
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
tor->status = TR_STATUS_PAUSE;
|
243
|
+
tor->id = h->id;
|
244
|
+
tor->key = h->key;
|
245
|
+
tor->bindPort = &h->bindPort;
|
246
|
+
tor->finished = 0;
|
247
|
+
|
248
|
+
|
249
|
+
/* Guess scrape URL */
|
250
|
+
s1 = strchr( inf->trackerAnnounce, '/' );
|
251
|
+
while( ( s2 = strchr( s1 + 1, '/' ) ) )
|
252
|
+
{
|
253
|
+
s1 = s2;
|
254
|
+
}
|
255
|
+
s1++;
|
256
|
+
if( !strncmp( s1, "announce", 8 ) )
|
257
|
+
{
|
258
|
+
int pre = (long) s1 - (long) inf->trackerAnnounce;
|
259
|
+
int post = strlen( inf->trackerAnnounce ) - pre - 8;
|
260
|
+
memcpy( tor->scrape, inf->trackerAnnounce, pre );
|
261
|
+
sprintf( &tor->scrape[pre], "scrape" );
|
262
|
+
memcpy( &tor->scrape[pre+6], &inf->trackerAnnounce[pre+8], post );
|
263
|
+
}
|
264
|
+
|
265
|
+
/* Escaped info hash for HTTP queries */
|
266
|
+
for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
|
267
|
+
{
|
268
|
+
sprintf( &tor->hashString[3*i], "%%%02x", inf->hash[i] );
|
269
|
+
}
|
270
|
+
|
271
|
+
/* Block size: usually 16 ko, or less if we have to */
|
272
|
+
tor->blockSize = MIN( inf->pieceSize, 1 << 14 );
|
273
|
+
tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
|
274
|
+
tor->blockSize;
|
275
|
+
tor->completion = tr_cpInit( tor );
|
276
|
+
|
277
|
+
tr_lockInit( &tor->lock );
|
278
|
+
|
279
|
+
tor->globalUpload = h->upload;
|
280
|
+
tor->globalDownload = h->download;
|
281
|
+
tor->fdlimit = h->fdlimit;
|
282
|
+
tor->upload = tr_rcInit();
|
283
|
+
tor->download = tr_rcInit();
|
284
|
+
|
285
|
+
/* We have a new torrent */
|
286
|
+
tr_lockLock( &h->acceptLock );
|
287
|
+
tor->prev = NULL;
|
288
|
+
tor->next = h->torrentList;
|
289
|
+
if( tor->next )
|
290
|
+
{
|
291
|
+
tor->next->prev = tor;
|
292
|
+
}
|
293
|
+
h->torrentList = tor;
|
294
|
+
(h->torrentCount)++;
|
295
|
+
tr_lockUnlock( &h->acceptLock );
|
296
|
+
|
297
|
+
if( 0 > h->bindPort )
|
298
|
+
{
|
299
|
+
tr_setBindPort( h, TR_DEFAULT_PORT );
|
300
|
+
}
|
301
|
+
|
302
|
+
return tor;
|
303
|
+
}
|
304
|
+
|
305
|
+
tr_info_t * tr_torrentInfo( tr_torrent_t * tor )
|
306
|
+
{
|
307
|
+
return &tor->info;
|
308
|
+
}
|
309
|
+
|
310
|
+
/***********************************************************************
|
311
|
+
* tr_torrentScrape
|
312
|
+
**********************************************************************/
|
313
|
+
int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l )
|
314
|
+
{
|
315
|
+
return tr_trackerScrape( tor, s, l );
|
316
|
+
}
|
317
|
+
|
318
|
+
void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
|
319
|
+
{
|
320
|
+
tor->destination = strdup( path );
|
321
|
+
tr_ioLoadResume( tor );
|
322
|
+
}
|
323
|
+
|
324
|
+
char * tr_torrentGetFolder( tr_torrent_t * tor )
|
325
|
+
{
|
326
|
+
return tor->destination;
|
327
|
+
}
|
328
|
+
|
329
|
+
void tr_torrentStart( tr_torrent_t * tor )
|
330
|
+
{
|
331
|
+
if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
|
332
|
+
{
|
333
|
+
/* Join the thread first */
|
334
|
+
torrentReallyStop( tor );
|
335
|
+
}
|
336
|
+
|
337
|
+
tor->status = TR_STATUS_CHECK;
|
338
|
+
tor->tracker = tr_trackerInit( tor );
|
339
|
+
|
340
|
+
tor->date = tr_date();
|
341
|
+
tor->die = 0;
|
342
|
+
tr_threadCreate( &tor->thread, downloadLoop, tor );
|
343
|
+
}
|
344
|
+
|
345
|
+
void tr_torrentStop( tr_torrent_t * tor )
|
346
|
+
{
|
347
|
+
tr_lockLock( &tor->lock );
|
348
|
+
tr_trackerStopped( tor->tracker );
|
349
|
+
tr_rcReset( tor->download );
|
350
|
+
tr_rcReset( tor->upload );
|
351
|
+
tor->status = TR_STATUS_STOPPING;
|
352
|
+
tor->stopDate = tr_date();
|
353
|
+
tr_lockUnlock( &tor->lock );
|
354
|
+
}
|
355
|
+
|
356
|
+
/***********************************************************************
|
357
|
+
* torrentReallyStop
|
358
|
+
***********************************************************************
|
359
|
+
* Joins the download thread and frees/closes everything related to it.
|
360
|
+
**********************************************************************/
|
361
|
+
static void torrentReallyStop( tr_torrent_t * tor )
|
362
|
+
{
|
363
|
+
tor->die = 1;
|
364
|
+
tr_threadJoin( &tor->thread );
|
365
|
+
tr_dbg( "Thread joined" );
|
366
|
+
|
367
|
+
tr_trackerClose( tor->tracker );
|
368
|
+
tor->tracker = NULL;
|
369
|
+
|
370
|
+
while( tor->peerCount > 0 )
|
371
|
+
{
|
372
|
+
tr_peerRem( tor, 0 );
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
376
|
+
/***********************************************************************
|
377
|
+
* tr_torrentCount
|
378
|
+
***********************************************************************
|
379
|
+
*
|
380
|
+
**********************************************************************/
|
381
|
+
int tr_torrentCount( tr_handle_t * h )
|
382
|
+
{
|
383
|
+
return h->torrentCount;
|
384
|
+
}
|
385
|
+
|
386
|
+
void tr_torrentIterate( tr_handle_t * h, tr_callback_t func, void * d )
|
387
|
+
{
|
388
|
+
tr_torrent_t * tor;
|
389
|
+
|
390
|
+
for( tor = h->torrentList; tor; tor = tor->next )
|
391
|
+
{
|
392
|
+
func( tor, d );
|
393
|
+
}
|
394
|
+
}
|
395
|
+
|
396
|
+
int tr_getFinished( tr_torrent_t * tor )
|
397
|
+
{
|
398
|
+
if( tor->finished )
|
399
|
+
{
|
400
|
+
tor->finished = 0;
|
401
|
+
return 1;
|
402
|
+
}
|
403
|
+
return 0;
|
404
|
+
}
|
405
|
+
|
406
|
+
tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
|
407
|
+
{
|
408
|
+
tr_stat_t * s;
|
409
|
+
tr_info_t * inf = &tor->info;
|
410
|
+
int i;
|
411
|
+
|
412
|
+
tor->statCur = ( tor->statCur + 1 ) % 2;
|
413
|
+
s = &tor->stats[tor->statCur];
|
414
|
+
|
415
|
+
if( ( tor->status & TR_STATUS_STOPPED ) ||
|
416
|
+
( ( tor->status & TR_STATUS_STOPPING ) &&
|
417
|
+
tr_date() > tor->stopDate + 60000 ) )
|
418
|
+
{
|
419
|
+
torrentReallyStop( tor );
|
420
|
+
tor->status = TR_STATUS_PAUSE;
|
421
|
+
}
|
422
|
+
|
423
|
+
tr_lockLock( &tor->lock );
|
424
|
+
|
425
|
+
s->status = tor->status;
|
426
|
+
s->error = tor->error;
|
427
|
+
memcpy( s->trackerError, tor->trackerError,
|
428
|
+
sizeof( s->trackerError ) );
|
429
|
+
|
430
|
+
s->peersTotal = 0;
|
431
|
+
s->peersUploading = 0;
|
432
|
+
s->peersDownloading = 0;
|
433
|
+
|
434
|
+
for( i = 0; i < tor->peerCount; i++ )
|
435
|
+
{
|
436
|
+
if( tr_peerIsConnected( tor->peers[i] ) )
|
437
|
+
{
|
438
|
+
(s->peersTotal)++;
|
439
|
+
if( tr_peerIsUploading( tor->peers[i] ) )
|
440
|
+
{
|
441
|
+
(s->peersUploading)++;
|
442
|
+
}
|
443
|
+
if( tr_peerIsDownloading( tor->peers[i] ) )
|
444
|
+
{
|
445
|
+
(s->peersDownloading)++;
|
446
|
+
}
|
447
|
+
}
|
448
|
+
}
|
449
|
+
|
450
|
+
s->progress = tr_cpCompletionAsFloat( tor->completion );
|
451
|
+
if( tor->status & TR_STATUS_DOWNLOAD )
|
452
|
+
s->rateDownload = tr_rcRate( tor->download );
|
453
|
+
else
|
454
|
+
/* tr_rcRate() doesn't make the difference between 'piece'
|
455
|
+
messages and other messages, which causes a non-zero
|
456
|
+
download rate even tough we are not downloading. So we
|
457
|
+
force it to zero not to confuse the user. */
|
458
|
+
s->rateDownload = 0.0;
|
459
|
+
s->rateUpload = tr_rcRate( tor->upload );
|
460
|
+
|
461
|
+
s->seeders = tr_trackerSeeders(tor->tracker);
|
462
|
+
s->leechers = tr_trackerLeechers(tor->tracker);
|
463
|
+
|
464
|
+
if( s->rateDownload < 0.1 )
|
465
|
+
{
|
466
|
+
s->eta = -1;
|
467
|
+
}
|
468
|
+
else
|
469
|
+
{
|
470
|
+
s->eta = (float) ( 1.0 - s->progress ) *
|
471
|
+
(float) inf->totalSize / s->rateDownload / 1024.0;
|
472
|
+
if( s->eta > 99 * 3600 + 59 * 60 + 59 )
|
473
|
+
{
|
474
|
+
s->eta = -1;
|
475
|
+
}
|
476
|
+
}
|
477
|
+
|
478
|
+
s->downloaded = tor->downloaded;
|
479
|
+
s->uploaded = tor->uploaded;
|
480
|
+
|
481
|
+
tr_lockUnlock( &tor->lock );
|
482
|
+
|
483
|
+
return s;
|
484
|
+
}
|
485
|
+
|
486
|
+
void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
|
487
|
+
{
|
488
|
+
int i, j, piece;
|
489
|
+
|
490
|
+
tr_lockLock( &tor->lock );
|
491
|
+
for( i = 0; i < size; i++ )
|
492
|
+
{
|
493
|
+
piece = i * tor->info.pieceCount / size;
|
494
|
+
|
495
|
+
if( tr_cpPieceIsComplete( tor->completion, piece ) )
|
496
|
+
{
|
497
|
+
tab[i] = -1;
|
498
|
+
continue;
|
499
|
+
}
|
500
|
+
|
501
|
+
tab[i] = 0;
|
502
|
+
for( j = 0; j < tor->peerCount; j++ )
|
503
|
+
{
|
504
|
+
if( tr_peerBitfield( tor->peers[j] ) &&
|
505
|
+
tr_bitfieldHas( tr_peerBitfield( tor->peers[j] ), piece ) )
|
506
|
+
{
|
507
|
+
(tab[i])++;
|
508
|
+
}
|
509
|
+
}
|
510
|
+
}
|
511
|
+
tr_lockUnlock( &tor->lock );
|
512
|
+
}
|
513
|
+
|
514
|
+
void tr_torrentRemoveSaved( tr_torrent_t * tor ) {
|
515
|
+
tr_metainfoRemoveSaved( tor->info.hashString );
|
516
|
+
}
|
517
|
+
|
518
|
+
/***********************************************************************
|
519
|
+
* tr_torrentClose
|
520
|
+
***********************************************************************
|
521
|
+
* Frees memory allocated by tr_torrentInit.
|
522
|
+
**********************************************************************/
|
523
|
+
void tr_torrentClose( tr_handle_t * h, tr_torrent_t * tor )
|
524
|
+
{
|
525
|
+
tr_info_t * inf = &tor->info;
|
526
|
+
|
527
|
+
if( tor->status & ( TR_STATUS_STOPPING | TR_STATUS_STOPPED ) )
|
528
|
+
{
|
529
|
+
/* Join the thread first */
|
530
|
+
torrentReallyStop( tor );
|
531
|
+
}
|
532
|
+
|
533
|
+
tr_lockLock( &h->acceptLock );
|
534
|
+
|
535
|
+
h->torrentCount--;
|
536
|
+
|
537
|
+
tr_lockClose( &tor->lock );
|
538
|
+
tr_cpClose( tor->completion );
|
539
|
+
|
540
|
+
tr_rcClose( tor->upload );
|
541
|
+
tr_rcClose( tor->download );
|
542
|
+
|
543
|
+
if( tor->destination )
|
544
|
+
{
|
545
|
+
free( tor->destination );
|
546
|
+
}
|
547
|
+
free( inf->pieces );
|
548
|
+
free( inf->files );
|
549
|
+
|
550
|
+
if( tor->prev )
|
551
|
+
{
|
552
|
+
tor->prev->next = tor->next;
|
553
|
+
}
|
554
|
+
else
|
555
|
+
{
|
556
|
+
h->torrentList = tor->next;
|
557
|
+
}
|
558
|
+
if( tor->next )
|
559
|
+
{
|
560
|
+
tor->next->prev = tor->prev;
|
561
|
+
}
|
562
|
+
free( tor );
|
563
|
+
|
564
|
+
tr_lockUnlock( &h->acceptLock );
|
565
|
+
}
|
566
|
+
|
567
|
+
void tr_close( tr_handle_t * h )
|
568
|
+
{
|
569
|
+
acceptStop( h );
|
570
|
+
tr_chokingClose( h->choking );
|
571
|
+
tr_fdClose( h->fdlimit );
|
572
|
+
tr_rcClose( h->upload );
|
573
|
+
tr_rcClose( h->download );
|
574
|
+
free( h );
|
575
|
+
|
576
|
+
tr_netResolveThreadClose();
|
577
|
+
}
|
578
|
+
|
579
|
+
/***********************************************************************
|
580
|
+
* downloadLoop
|
581
|
+
**********************************************************************/
|
582
|
+
static void downloadLoop( void * _tor )
|
583
|
+
{
|
584
|
+
tr_torrent_t * tor = _tor;
|
585
|
+
uint64_t date1, date2;
|
586
|
+
|
587
|
+
tr_dbg( "Thread started" );
|
588
|
+
|
589
|
+
#ifdef SYS_BEOS
|
590
|
+
/* This is required because on BeOS, SIGINT is sent to each thread,
|
591
|
+
which kills them not nicely */
|
592
|
+
signal( SIGINT, SIG_IGN );
|
593
|
+
#endif
|
594
|
+
|
595
|
+
tr_lockLock( &tor->lock );
|
596
|
+
|
597
|
+
tr_cpReset( tor->completion );
|
598
|
+
tor->io = tr_ioInit( tor );
|
599
|
+
tor->status = tr_cpIsSeeding( tor->completion ) ?
|
600
|
+
TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
|
601
|
+
|
602
|
+
while( !tor->die )
|
603
|
+
{
|
604
|
+
date1 = tr_date();
|
605
|
+
|
606
|
+
/* Are we finished ? */
|
607
|
+
if( ( tor->status & TR_STATUS_DOWNLOAD ) &&
|
608
|
+
tr_cpIsSeeding( tor->completion ) )
|
609
|
+
{
|
610
|
+
/* Done */
|
611
|
+
tor->status = TR_STATUS_SEED;
|
612
|
+
tor->finished = 1;
|
613
|
+
tr_trackerCompleted( tor->tracker );
|
614
|
+
tr_ioSaveResume( tor->io );
|
615
|
+
sync(); /* KLUDGE: all files should be closed and
|
616
|
+
re-opened in read-only mode instead */
|
617
|
+
}
|
618
|
+
|
619
|
+
/* Receive/send messages */
|
620
|
+
tr_peerPulse( tor );
|
621
|
+
|
622
|
+
/* Try to get new peers or to send a message to the tracker */
|
623
|
+
tr_trackerPulse( tor->tracker );
|
624
|
+
|
625
|
+
if( tor->status & TR_STATUS_STOPPED )
|
626
|
+
{
|
627
|
+
break;
|
628
|
+
}
|
629
|
+
|
630
|
+
/* Wait up to 20 ms */
|
631
|
+
date2 = tr_date();
|
632
|
+
if( date2 < date1 + 20 )
|
633
|
+
{
|
634
|
+
tr_lockUnlock( &tor->lock );
|
635
|
+
tr_wait( date1 + 20 - date2 );
|
636
|
+
tr_lockLock( &tor->lock );
|
637
|
+
}
|
638
|
+
}
|
639
|
+
|
640
|
+
tr_lockUnlock( &tor->lock );
|
641
|
+
|
642
|
+
tr_ioClose( tor->io );
|
643
|
+
|
644
|
+
tor->status = TR_STATUS_STOPPED;
|
645
|
+
|
646
|
+
tr_dbg( "Thread exited" );
|
647
|
+
}
|
648
|
+
|
649
|
+
/***********************************************************************
|
650
|
+
* acceptLoop
|
651
|
+
**********************************************************************/
|
652
|
+
static void acceptLoop( void * _h )
|
653
|
+
{
|
654
|
+
tr_handle_t * h = _h;
|
655
|
+
uint64_t date1, date2, lastchoke = 0;
|
656
|
+
int ii;
|
657
|
+
uint8_t * hash;
|
658
|
+
tr_torrent_t * tor;
|
659
|
+
|
660
|
+
tr_dbg( "Accept thread started" );
|
661
|
+
|
662
|
+
#ifdef SYS_BEOS
|
663
|
+
/* This is required because on BeOS, SIGINT is sent to each thread,
|
664
|
+
which kills them not nicely */
|
665
|
+
signal( SIGINT, SIG_IGN );
|
666
|
+
#endif
|
667
|
+
|
668
|
+
tr_lockLock( &h->acceptLock );
|
669
|
+
|
670
|
+
while( !h->acceptDie )
|
671
|
+
{
|
672
|
+
date1 = tr_date();
|
673
|
+
|
674
|
+
/* Check for incoming connections */
|
675
|
+
if( h->bindSocket > -1 &&
|
676
|
+
h->acceptPeerCount < TR_MAX_PEER_COUNT &&
|
677
|
+
!tr_fdSocketWillCreate( h->fdlimit, 0 ) )
|
678
|
+
{
|
679
|
+
int s;
|
680
|
+
struct in_addr addr;
|
681
|
+
in_port_t port;
|
682
|
+
s = tr_netAccept( h->bindSocket, &addr, &port );
|
683
|
+
if( s > -1 )
|
684
|
+
{
|
685
|
+
h->acceptPeers[h->acceptPeerCount++] = tr_peerInit( addr, port, s );
|
686
|
+
}
|
687
|
+
else
|
688
|
+
{
|
689
|
+
tr_fdSocketClosed( h->fdlimit, 0 );
|
690
|
+
}
|
691
|
+
}
|
692
|
+
|
693
|
+
for( ii = 0; ii < h->acceptPeerCount; )
|
694
|
+
{
|
695
|
+
if( tr_peerRead( NULL, h->acceptPeers[ii] ) )
|
696
|
+
{
|
697
|
+
tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
|
698
|
+
goto removePeer;
|
699
|
+
}
|
700
|
+
if( NULL != ( hash = tr_peerHash( h->acceptPeers[ii] ) ) )
|
701
|
+
{
|
702
|
+
for( tor = h->torrentList; tor; tor = tor->next )
|
703
|
+
{
|
704
|
+
tr_lockLock( &tor->lock );
|
705
|
+
if( 0 == memcmp( tor->info.hash, hash,
|
706
|
+
SHA_DIGEST_LENGTH ) )
|
707
|
+
{
|
708
|
+
tr_peerAttach( tor, h->acceptPeers[ii] );
|
709
|
+
tr_lockUnlock( &tor->lock );
|
710
|
+
goto removePeer;
|
711
|
+
}
|
712
|
+
tr_lockUnlock( &tor->lock );
|
713
|
+
}
|
714
|
+
tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
|
715
|
+
goto removePeer;
|
716
|
+
}
|
717
|
+
if( date1 > tr_peerDate( h->acceptPeers[ii] ) + 10000 )
|
718
|
+
{
|
719
|
+
/* Give them 10 seconds to send the handshake */
|
720
|
+
tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
|
721
|
+
goto removePeer;
|
722
|
+
}
|
723
|
+
ii++;
|
724
|
+
continue;
|
725
|
+
removePeer:
|
726
|
+
h->acceptPeerCount--;
|
727
|
+
memmove( &h->acceptPeers[ii], &h->acceptPeers[ii+1],
|
728
|
+
( h->acceptPeerCount - ii ) * sizeof( tr_peer_t * ) );
|
729
|
+
}
|
730
|
+
|
731
|
+
if( date1 > lastchoke + 2000 )
|
732
|
+
{
|
733
|
+
tr_chokingPulse( h->choking );
|
734
|
+
lastchoke = date1;
|
735
|
+
}
|
736
|
+
|
737
|
+
/* Wait up to 20 ms */
|
738
|
+
date2 = tr_date();
|
739
|
+
if( date2 < date1 + 20 )
|
740
|
+
{
|
741
|
+
tr_lockUnlock( &h->acceptLock );
|
742
|
+
tr_wait( date1 + 20 - date2 );
|
743
|
+
tr_lockLock( &h->acceptLock );
|
744
|
+
}
|
745
|
+
}
|
746
|
+
|
747
|
+
tr_lockUnlock( &h->acceptLock );
|
748
|
+
|
749
|
+
tr_dbg( "Accept thread exited" );
|
750
|
+
}
|
751
|
+
|
752
|
+
/***********************************************************************
|
753
|
+
* acceptStop
|
754
|
+
***********************************************************************
|
755
|
+
* Joins the accept thread and frees/closes everything related to it.
|
756
|
+
**********************************************************************/
|
757
|
+
static void acceptStop( tr_handle_t * h )
|
758
|
+
{
|
759
|
+
int ii;
|
760
|
+
|
761
|
+
h->acceptDie = 1;
|
762
|
+
tr_threadJoin( &h->acceptThread );
|
763
|
+
tr_lockClose( &h->acceptLock );
|
764
|
+
tr_dbg( "Accept thread joined" );
|
765
|
+
|
766
|
+
for( ii = 0; ii < h->acceptPeerCount; ii++ )
|
767
|
+
{
|
768
|
+
tr_peerDestroy( h->fdlimit, h->acceptPeers[ii] );
|
769
|
+
}
|
770
|
+
|
771
|
+
if( h->bindSocket > -1 )
|
772
|
+
{
|
773
|
+
tr_netClose( h->bindSocket );
|
774
|
+
tr_fdSocketClosed( h->fdlimit, 0 );
|
775
|
+
}
|
776
|
+
}
|