source: trunk/libtransmission/webseed.c @ 6971

Last change on this file since 6971 was 6971, checked in by charles, 8 years ago

janitorial.
(1) re-enable `deflate' in transmission-remote iff libz is installed
(2) change c++-style comments to c-style comments
(3) add positional arguments to a couple of libT strings marked for translation

  • Property svn:keywords set to Date Rev Author Id
File size: 8.7 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: webseed.c 6971 2008-10-27 18:09:15Z charles $
11 */
12
13#include <assert.h>
14#include <string.h> /* strlen */
15
16#include <event.h>
17
18#include "transmission.h"
19#include "inout.h"
20#include "list.h"
21#include "ratecontrol.h"
22#include "torrent.h"
23#include "utils.h"
24#include "web.h"
25#include "webseed.h"
26
27struct tr_webseed
28{
29    unsigned int        busy : 1;
30    unsigned int        dead : 1;
31
32    tr_torrent *        torrent;
33    char *              url;
34
35    tr_delivery_func *  callback;
36    void *              callback_userdata;
37
38    tr_piece_index_t    pieceIndex;
39    uint32_t            pieceOffset;
40    uint32_t            byteCount;
41
42    tr_ratecontrol *    rateDown;
43
44    struct evbuffer *   content;
45};
46
47/***
48****
49***/
50
51static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0 };
52
53static void
54publish( tr_webseed *    w,
55         tr_peer_event * e )
56{
57    if( w->callback )
58        w->callback( NULL, e, w->callback_userdata );
59}
60
61static void
62fireNeedReq( tr_webseed * w )
63{
64    tr_peer_event e = blankEvent;
65
66    e.eventType = TR_PEER_NEED_REQ;
67    publish( w, &e );
68}
69
70static void
71fireClientGotBlock( tr_webseed * w,
72                    uint32_t     pieceIndex,
73                    uint32_t     offset,
74                    uint32_t     length )
75{
76    tr_peer_event e = blankEvent;
77
78    e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
79    e.pieceIndex = pieceIndex;
80    e.offset = offset;
81    e.length = length;
82    publish( w, &e );
83}
84
85static void
86fireClientGotData( tr_webseed * w,
87                   uint32_t     length )
88{
89    tr_peer_event e = blankEvent;
90
91    e.eventType = TR_PEER_CLIENT_GOT_DATA;
92    e.length = length;
93    publish( w, &e );
94}
95
96/***
97****
98***/
99
100static char*
101makeURL( tr_webseed *    w,
102         const tr_file * file )
103{
104    char *            ret;
105    struct evbuffer * out = evbuffer_new( );
106    const char *      url = w->url;
107    const size_t      url_len = strlen( url );
108
109    evbuffer_add( out, url, url_len );
110
111    /* if url ends with a '/', add the torrent name */
112    if( url[url_len - 1] == '/' )
113    {
114        const char * str = file->name;
115
116        /* this is like curl_escape() but doesn't munge the
117         * '/' directory separators in the path */
118        while( str && *str )
119        {
120            switch( *str )
121            {
122                case ',':
123                case '-':
124                case '.':
125                case '/':
126                case '0':
127                case '1':
128                case '2':
129                case '3':
130                case '4':
131                case '5':
132                case '6':
133                case '7':
134                case '8':
135                case '9':
136                case 'a':
137                case 'b':
138                case 'c':
139                case 'd':
140                case 'e':
141                case 'f':
142                case 'g':
143                case 'h':
144                case 'i':
145                case 'j':
146                case 'k':
147                case 'l':
148                case 'm':
149                case 'n':
150                case 'o':
151                case 'p':
152                case 'q':
153                case 'r':
154                case 's':
155                case 't':
156                case 'u':
157                case 'v':
158                case 'w':
159                case 'x':
160                case 'y':
161                case 'z':
162                case 'A':
163                case 'B':
164                case 'C':
165                case 'D':
166                case 'E':
167                case 'F':
168                case 'G':
169                case 'H':
170                case 'I':
171                case 'J':
172                case 'K':
173                case 'L':
174                case 'M':
175                case 'N':
176                case 'O':
177                case 'P':
178                case 'Q':
179                case 'R':
180                case 'S':
181                case 'T':
182                case 'U':
183                case 'V':
184                case 'W':
185                case 'X':
186                case 'Y':
187                case 'Z':
188                    evbuffer_add( out, str, 1 );
189                    break;
190
191                default:
192                    evbuffer_add_printf( out, "%%%02X", *str );
193                    break;
194            }
195            str++;
196        }
197    }
198
199    ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) );
200    evbuffer_free( out );
201    return ret;
202}
203
204static void requestNextChunk( tr_webseed * w );
205
206static void
207webResponseFunc( tr_handle   * session UNUSED,
208                 long                  response_code,
209                 const void *          response,
210                 size_t                response_byte_count,
211                 void *                vw )
212{
213    tr_webseed * w = vw;
214    const int    success = ( response_code == 206 );
215
216/*fprintf( stderr, "server responded with code %ld and %lu bytes\n",
217  response_code, (unsigned long)response_byte_count );*/
218    if( !success )
219    {
220        /* FIXME */
221    }
222    else
223    {
224        evbuffer_add( w->content, response, response_byte_count );
225        if( !w->dead )
226        {
227            fireClientGotData( w, response_byte_count );
228            tr_rcTransferred( w->rateDown, response_byte_count );
229        }
230
231        if( EVBUFFER_LENGTH( w->content ) < w->byteCount )
232            requestNextChunk( w );
233        else {
234            tr_ioWrite( w->torrent, w->pieceIndex, w->pieceOffset, w->byteCount, EVBUFFER_DATA(w->content) );
235            evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
236            w->busy = 0;
237            if( w->dead )
238                tr_webseedFree( w );
239            else  {
240                fireClientGotBlock( w, w->pieceIndex, w->pieceOffset, w->byteCount );
241                fireNeedReq( w );
242            }
243        }
244    }
245}
246
247static void
248requestNextChunk( tr_webseed * w )
249{
250    const tr_info * inf = tr_torrentInfo( w->torrent );
251    const uint32_t have = EVBUFFER_LENGTH( w->content );
252    const uint32_t left = w->byteCount - have;
253    const uint32_t pieceOffset = w->pieceOffset + have;
254    tr_file_index_t fileIndex;
255    uint64_t fileOffset;
256    uint32_t thisPass;
257    char * url;
258    char * range;
259
260    tr_ioFindFileLocation( w->torrent, w->pieceIndex, pieceOffset,
261                           &fileIndex, &fileOffset );
262    thisPass = MIN( left, inf->files[fileIndex].length - fileOffset );
263
264    url = makeURL( w, &inf->files[fileIndex] );
265/*fprintf( stderr, "url is [%s]\n", url );*/
266    range = tr_strdup_printf( "%"PRIu64"-%"PRIu64, fileOffset, fileOffset + thisPass - 1 );
267/*fprintf( stderr, "range is [%s] ... we want %lu total, we have %lu, so %lu are left, and we're asking for %lu this time\n", range, (unsigned long)w->byteCount, (unsigned long)have, (unsigned long)left, (unsigned long)thisPass );*/
268    tr_webRun( w->torrent->session, url, range, webResponseFunc, w );
269    tr_free( range );
270    tr_free( url );
271}
272
273tr_addreq_t
274tr_webseedAddRequest( tr_webseed  * w,
275                      uint32_t      pieceIndex,
276                      uint32_t      pieceOffset,
277                      uint32_t      byteCount )
278{
279    int ret;
280
281    if( w->busy || w->dead )
282    {
283        ret = TR_ADDREQ_FULL;
284    }
285    else
286    {
287        w->busy = 1;
288        w->pieceIndex = pieceIndex;
289        w->pieceOffset = pieceOffset;
290        w->byteCount = byteCount;
291        evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
292        requestNextChunk( w );
293        ret = TR_ADDREQ_OK;
294    }
295
296    return ret;
297}
298
299int
300tr_webseedIsActive( const tr_webseed * w )
301{
302    return w->busy != 0;
303}
304
305int
306tr_webseedGetSpeed( const tr_webseed * w,
307                    float *            setme_KiBs )
308{
309    const int isActive = tr_webseedIsActive( w );
310
311    *setme_KiBs = isActive ? tr_rcRate( w->rateDown ) : 0.0f;
312    return isActive;
313}
314
315/***
316****
317***/
318
319tr_webseed*
320tr_webseedNew( struct tr_torrent * torrent,
321               const char *        url,
322               tr_delivery_func    callback,
323               void *              callback_userdata )
324{
325    tr_webseed * w = tr_new0( tr_webseed, 1 );
326
327    w->content = evbuffer_new( );
328    w->rateDown = tr_rcInit( );
329    w->torrent = torrent;
330    w->url = tr_strdup( url );
331    w->callback = callback;
332    w->callback_userdata = callback_userdata;
333/*fprintf( stderr, "w->callback_userdata is %p\n", w->callback_userdata );*/
334    return w;
335}
336
337void
338tr_webseedFree( tr_webseed * w )
339{
340    if( w )
341    {
342        if( w->busy )
343        {
344            w->dead = 1;
345        }
346        else
347        {
348            evbuffer_free( w->content );
349            tr_rcClose( w->rateDown );
350            tr_free( w->url );
351            tr_free( w );
352        }
353    }
354}
Note: See TracBrowser for help on using the repository browser.