source: trunk/libtransmission/clients.c @ 7526

Last change on this file since 7526 was 7526, checked in by charles, 12 years ago

Add "SD" to the clients list as Xunlei as reported by malucas72 in the forums.

  • Property svn:keywords set to Date Rev Author Id
File size: 16.8 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@transmissionbt.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: clients.c 7526 2008-12-29 16:34:59Z charles $
11 */
12
13/* thanks amc1! */
14
15#include <ctype.h> /* isprint, tolower */
16#include <stdio.h>
17#include <stdlib.h> /* strtol */
18#include <string.h>
19
20#include <libevent/event.h> /* evbuffer */
21
22#include "transmission.h"
23#include "clients.h"
24#include "utils.h"
25
26static int
27charint( char ch )
28{
29    if( '0' <= ch && ch <= '9' ) return      ch - '0';
30    if( 'A' <= ch && ch <= 'Z' ) return 10 + ch - 'A';
31    if( 'a' <= ch && ch <= 'z' ) return 36 + ch - 'a';
32    return 0;
33}
34
35static int 
36getShadowInt( char ch, int * setme ) 
37{ 
38    const char * str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-"; 
39    const char * pch = strchr( str, ch ); 
40    if( !pch ) 
41    return 0; 
42        *setme = pch - str; 
43    return 1; 
44} 
45
46static int
47strint( const void * pch, int span )
48{
49    char tmp[64];
50    memcpy( tmp, pch, span );
51    tmp[span] = '\0';
52    return strtol( tmp, NULL, 0 );
53}
54
55static const char*
56getMnemonicEnd( char ch )
57{
58    switch( ch )
59    {
60        case 'b': case 'B': return " (Beta)";
61        case 'd': return " (Debug)";
62        case 'x': case 'X': case 'Z': return " (Dev)";
63        default: return "";
64    }
65}
66
67static void
68three_digits( char * buf, size_t buflen, const char * name, const uint8_t * digits )
69{
70    tr_snprintf( buf, buflen, "%s %d.%d.%d", name,
71                 charint( digits[0] ),
72                 charint( digits[1] ),
73                 charint( digits[2] ) );
74}
75static void
76four_digits( char * buf, size_t buflen, const char * name, const uint8_t * digits )
77{
78    tr_snprintf( buf, buflen, "%s %d.%d.%d.%d", name,
79                 charint( digits[0] ),
80                 charint( digits[1] ),
81                 charint( digits[2] ),
82                 charint( digits[3] ) );
83}
84static void
85two_major_two_minor( char * buf, size_t buflen, const char * name, const uint8_t * digits )
86{
87    tr_snprintf( buf, buflen, "%s %d.%02d", name,
88                 strint( digits, 2 ),
89                 strint( digits+2, 2 ) );
90}
91static void
92no_version( char * buf, size_t buflen, const char * name )
93{
94    tr_strlcpy( buf, name, buflen );
95}
96
97static void
98mainline_style( char * buf, size_t buflen, const char * name, const uint8_t * id )
99{
100    if( id[4] == '-' && id[6] == '-' )
101        tr_snprintf( buf, buflen, "%s %c.%c.%c", name, id[1], id[3], id[5] );
102    else if( id[5] == '-' )
103        tr_snprintf( buf, buflen, "%s %c.%c%c.%c", name, id[1], id[3], id[4], id[6] );
104}
105
106static int
107isMainlineStyle( const uint8_t * peer_id )
108{
109    /**
110     * One of the following styles will be used:
111     *   Mx-y-z--
112     *   Mx-yy-z-
113     */ 
114    return peer_id[2]=='-'
115        && peer_id[7]=='-'
116        && ( peer_id[4]=='-' || peer_id[5]=='-' );
117}
118
119static int
120decodeBitCometClient( char * buf, size_t buflen, const uint8_t * id )
121{
122    int is_bitlord;
123    int major, minor;
124    const char * name;
125    const char * mod = NULL;
126
127    if( !memcmp( id, "exbc", 4 ) ) mod = "";
128    else if( !memcmp( id, "FUTB", 4 )) mod = "(Solidox Mod) ";
129    else if( !memcmp( id, "xUTB", 4 )) mod = "(Mod 2) ";
130    else return FALSE;
131
132    is_bitlord = !memcmp( id+6, "LORD", 4 );
133    name = (is_bitlord) ? "BitLord " : "BitComet ";
134    major = id[4];
135    minor = id[5];
136
137    /**
138     * Bitcomet, and older versions of BitLord, are of the form x.yy.
139     * Bitcoment 1.0 and onwards are of the form x.y.
140     */
141    if( is_bitlord && major>0 )
142        tr_snprintf( buf, buflen, "%s%s%d.%d", name, mod, major, minor );
143    else
144        tr_snprintf( buf, buflen, "%s%s%d.%02d", name, mod, major, minor );
145
146    return TRUE;
147}
148
149static int
150decodeBitSpiritClient( char * buf, size_t buflen, const uint8_t * id )
151{
152    const int isBS = !memcmp( id+2, "BS", 2 );
153    if( isBS )
154    {
155        const int version = id[1] ? id[1] : 1;
156        tr_snprintf( buf, buflen, "BitSpirit v%d", version );
157    }
158    return isBS;
159}
160
161void
162tr_clientForId( char * buf, size_t buflen, const void * id_in )
163{
164    const uint8_t * id = id_in;
165
166    *buf = '\0';
167
168    if( !id )
169        return;
170
171    /* Azureus-style */
172    if( id[0] == '-' && id[7] == '-' )
173    {
174        if( !memcmp( id+1, "UT", 2 ) )
175        {
176            tr_snprintf( buf, buflen, "\xc2\xb5Torrent %d.%d.%d%s",
177                         strint(id+3,1), strint(id+4,1), strint(id+5,1), getMnemonicEnd(id[6]) );
178        }
179        if( !memcmp( id+1, "UM", 2 ) )
180        {
181            tr_snprintf( buf, buflen, "\xc2\xb5Torrent Mac %d.%d.%d%s",
182                         strint(id+3,1), strint(id+4,1), strint(id+5,1), getMnemonicEnd(id[6]) );
183        }
184
185        else if( !memcmp( id+1, "TR", 2 ) )
186        {
187            if( !memcmp( id+3, "000", 3 ) ) /* very old client style: -TR0006- is 0.6 */
188                tr_snprintf( buf, buflen, "Transmission 0.%c", id[6] );
189            else if( !memcmp( id+3, "00", 2) ) /* previous client style: -TR0072- is 0.72 */
190                tr_snprintf( buf, buflen, "Transmission 0.%02d", strint(id+5,2) );
191            else /* current client style: -TR111Z- is 1.11+ */
192                tr_snprintf( buf, buflen, "Transmission %d.%02d%s", strint(id+3,1), strint(id+4,2),
193                          id[6]=='Z' || id[6]=='X' ? "+" : "" );
194        }
195       
196        else if( !memcmp( id+1, "AZ", 2 ) )
197        {
198            if( id[3] > '3' || ( id[3] == '3' && id[4] >= '1' ) ) /* Vuze starts at version 3.1.0.0 */
199                four_digits( buf, buflen, "Vuze", id+3 );
200            else
201                four_digits( buf, buflen, "Azureus", id+3 );
202        }
203       
204        else if( !memcmp( id+1, "KT", 2 ) )
205        {
206            if( id[5] == 'D' )
207                tr_snprintf( buf, buflen, "KTorrent %d.%d Dev %d", charint(id[3]), charint(id[4]), charint(id[6]) );
208            else if( id[5] == 'R' )
209                tr_snprintf( buf, buflen, "KTorrent %d.%d RC %d", charint(id[3]), charint(id[4]), charint(id[6]) );
210            else
211                three_digits( buf, buflen, "KTorrent", id+3 );
212        }
213
214        else if( !memcmp( id+1, "AR", 2 ) ) four_digits( buf, buflen, "Ares", id+3 );
215        else if( !memcmp( id+1, "AT", 2 ) ) four_digits( buf, buflen, "Artemis", id+3 );
216        else if( !memcmp( id+1, "AV", 2 ) ) four_digits( buf, buflen, "Avicora", id+3 );
217        else if( !memcmp( id+1, "BG", 2 ) ) four_digits( buf, buflen, "BTGetit", id+3 );
218        else if( !memcmp( id+1, "BM", 2 ) ) four_digits( buf, buflen, "BitMagnet", id+3 );
219        else if( !memcmp( id+1, "BP", 2 ) ) four_digits( buf, buflen, "BitTorrent Pro (Azureus + Spyware)", id+3 );
220        else if( !memcmp( id+1, "BX", 2 ) ) four_digits( buf, buflen, "BittorrentX", id+3 );
221        else if( !memcmp( id+1, "bk", 2 ) ) four_digits( buf, buflen, "BitKitten (libtorrent)", id+3 );
222        else if( !memcmp( id+1, "BS", 2 ) ) four_digits( buf, buflen, "BTSlave", id+3 );
223        else if( !memcmp( id+1, "BW", 2 ) ) four_digits( buf, buflen, "BitWombat", id+3 );
224        else if( !memcmp( id+1, "BX", 2 ) ) four_digits( buf, buflen, "BittorrentX", id+3 );
225        else if( !memcmp( id+1, "EB", 2 ) ) four_digits( buf, buflen, "EBit", id+3 );
226        else if( !memcmp( id+1, "DE", 2 ) ) four_digits( buf, buflen, "Deluge", id+3 );
227        else if( !memcmp( id+1, "DP", 2 ) ) four_digits( buf, buflen, "Propogate Data Client", id+3 );
228        else if( !memcmp( id+1, "FC", 2 ) ) four_digits( buf, buflen, "FileCroc", id+3 );
229        else if( !memcmp( id+1, "FT", 2 ) ) four_digits( buf, buflen, "FoxTorrent/RedSwoosh", id+3 );
230        else if( !memcmp( id+1, "GR", 2 ) ) four_digits( buf, buflen, "GetRight", id+3 );
231        else if( !memcmp( id+1, "HN", 2 ) ) four_digits( buf, buflen, "Hydranode", id+3 );
232        else if( !memcmp( id+1, "LC", 2 ) ) four_digits( buf, buflen, "LeechCraft", id+3 );
233        else if( !memcmp( id+1, "LH", 2 ) ) four_digits( buf, buflen, "LH-ABC", id+3 );
234        else if( !memcmp( id+1, "NX", 2 ) ) four_digits( buf, buflen, "Net Transport", id+3 );
235        else if( !memcmp( id+1, "MO", 2 ) ) four_digits( buf, buflen, "MonoTorrent", id+3 );
236        else if( !memcmp( id+1, "MR", 2 ) ) four_digits( buf, buflen, "Miro", id+3 );
237        else if( !memcmp( id+1, "MT", 2 ) ) four_digits( buf, buflen, "Moonlight", id+3 );
238        else if( !memcmp( id+1, "OT", 2 ) ) four_digits( buf, buflen, "OmegaTorrent", id+3 );
239        else if( !memcmp( id+1, "PD", 2 ) ) four_digits( buf, buflen, "Pando", id+3 );
240        else if( !memcmp( id+1, "QD", 2 ) ) four_digits( buf, buflen, "QQDownload", id+3 );
241        else if( !memcmp( id+1, "RS", 2 ) ) four_digits( buf, buflen, "Rufus", id+3 );
242        else if( !memcmp( id+1, "RT", 2 ) ) four_digits( buf, buflen, "Retriever", id+3 );
243        else if( !memcmp( id+1, "SD", 2 ) ) four_digits( buf, buflen, "Xunlei", id+3 );
244        else if( !memcmp( id+1, "SS", 2 ) ) four_digits( buf, buflen, "SwarmScope", id+3 );
245        else if( !memcmp( id+1, "SZ", 2 ) ) four_digits( buf, buflen, "Shareaza", id+3 );
246        else if( !memcmp( id+1, "S~", 2 ) ) four_digits( buf, buflen, "Shareaza", id+3 );
247        else if( !memcmp( id+1, "st", 2 ) ) four_digits( buf, buflen, "SharkTorrent", id+3 );
248        else if( !memcmp( id+1, "TN", 2 ) ) four_digits( buf, buflen, "Torrent .NET", id+3 );
249        else if( !memcmp( id+1, "TS", 2 ) ) four_digits( buf, buflen, "TorrentStorm", id+3 );
250        else if( !memcmp( id+1, "UL", 2 ) ) four_digits( buf, buflen, "uLeecher!", id+3 );
251        else if( !memcmp( id+1, "VG", 2 ) ) four_digits( buf, buflen, "Vagaa", id+3 );
252        else if( !memcmp( id+1, "WT", 2 ) ) four_digits( buf, buflen, "BitLet", id+3 );
253        else if( !memcmp( id+1, "WY", 2 ) ) four_digits( buf, buflen, "Wyzo", id+3 );
254        else if( !memcmp( id+1, "XL", 2 ) ) four_digits( buf, buflen, "Xunlei", id+3 );
255        else if( !memcmp( id+1, "XT", 2 ) ) four_digits( buf, buflen, "XanTorrent", id+3 );
256        else if( !memcmp( id+1, "ZT", 2 ) ) four_digits( buf, buflen, "Zip Torrent", id+3 );
257
258        else if( !memcmp( id+1, "AG", 2 ) ) three_digits( buf, buflen, "Ares", id+3 );
259        else if( !memcmp( id+1, "A~", 2 ) ) three_digits( buf, buflen, "Ares", id+3 );
260        else if( !memcmp( id+1, "ES", 2 ) ) three_digits( buf, buflen, "Electric Sheep", id+3 );
261        else if( !memcmp( id+1, "HL", 2 ) ) three_digits( buf, buflen, "Halite", id+3 );
262        else if( !memcmp( id+1, "LT", 2 ) ) three_digits( buf, buflen, "libtorrent (Rasterbar)", id+3 );
263        else if( !memcmp( id+1, "lt", 2 ) ) three_digits( buf, buflen, "libTorrent (Rakshasa)", id+3 );
264        else if( !memcmp( id+1, "MP", 2 ) ) three_digits( buf, buflen, "MooPolice", id+3 );
265        else if( !memcmp( id+1, "TT", 2 ) ) three_digits( buf, buflen, "TuoTu", id+3 );
266        else if( !memcmp( id+1, "qB", 2 ) ) three_digits( buf, buflen, "qBittorrent", id+3 );
267
268        else if( !memcmp( id+1, "AX", 2 ) ) two_major_two_minor( buf, buflen, "BitPump", id+3 );
269        else if( !memcmp( id+1, "BC", 2 ) ) two_major_two_minor( buf, buflen, "BitComet", id+3 );
270        else if( !memcmp( id+1, "CD", 2 ) ) two_major_two_minor( buf, buflen, "Enhanced CTorrent", id+3 );
271        else if( !memcmp( id+1, "LP", 2 ) ) two_major_two_minor( buf, buflen, "Lphant", id+3 );
272
273        else if( !memcmp( id+1, "BF", 2 ) ) no_version( buf, buflen, "BitFlu" );
274        else if( !memcmp( id+1, "LW", 2 ) ) no_version( buf, buflen, "LimeWire" );
275
276        else if( !memcmp( id+1, "BB", 2 ) )
277        {
278            tr_snprintf( buf, buflen, "BitBuddy %c.%c%c%c", id[3], id[4], id[5], id[6] );
279        }
280        else if( !memcmp( id+1, "BR", 2 ) )
281        {
282            tr_snprintf( buf, buflen, "BitRocket %c.%c (%c%c)", id[3], id[4], id[5], id[6] );
283        }
284        else if( !memcmp( id+1, "CT", 2 ) )
285        {
286            tr_snprintf( buf, buflen, "CTorrent %d.%d.%02d", charint(id[3]), charint(id[4]), strint(id+5,2) );
287        }
288        else if( !memcmp( id+1, "XX", 2 ) )
289        {
290            tr_snprintf( buf, buflen, "Xtorrent %d.%d (%d)", charint(id[3]), charint(id[4]), strint(id+5,2) );
291        }
292        else if( !memcmp( id+1, "BOW", 3 ) )
293        {
294                 if( !memcmp( &id[4], "A0B", 3 ) ) tr_snprintf( buf, buflen, "Bits on Wheels 1.0.5" );
295            else if( !memcmp( &id[4], "A0C", 3 ) ) tr_snprintf( buf, buflen, "Bits on Wheels 1.0.6" );
296            else                                   tr_snprintf( buf, buflen, "Bits on Wheels %c.%c.%c", id[4], id[5], id[5] );
297        }
298
299        if( *buf )
300            return;
301    }
302
303    /* Mainline */
304    if( isMainlineStyle( id ) )
305    {
306        if( *id=='M' ) mainline_style( buf, buflen, "BitTorrent", id );
307        if( *id=='Q' ) mainline_style( buf, buflen, "Queen Bee", id );
308        if( *buf ) return;
309    }
310
311    if( decodeBitCometClient( buf, buflen, id ) )
312        return;
313    if( decodeBitSpiritClient( buf, buflen, id ) )
314        return;
315
316    /* Clients with no version */
317         if( !memcmp( id, "AZ2500BT", 8 ) )  no_version( buf, buflen, "BitTyrant (Azureus Mod)" );
318    else if( !memcmp( id, "LIME", 4 ) )      no_version( buf, buflen, "Limewire" );
319    else if( !memcmp( id, "martini", 7 ) )   no_version( buf, buflen, "Martini Man" );
320    else if( !memcmp( id, "Pando", 5 ) )     no_version( buf, buflen, "Pando" );
321    else if( !memcmp( id, "a00---0", 7 ) )   no_version( buf, buflen, "Swarmy" );
322    else if( !memcmp( id, "a02---0", 7 ) )   no_version( buf, buflen, "Swarmy" );
323    else if( !memcmp( id, "-G3", 3 ) )       no_version( buf, buflen, "G3 Torrent" );
324    else if( !memcmp( id, "10-------", 9 ) ) no_version( buf, buflen, "JVtorrent" );
325    else if( !memcmp( id, "346-", 4 ) )      no_version( buf, buflen, "TorrentTopia" );
326    else if( !memcmp( id, "eX", 2 ) )        no_version( buf, buflen, "eXeem" );
327    else if( !memcmp( id, "-FG", 3 ) )       two_major_two_minor( buf, buflen, "FlashGet", id+3 );
328   
329    /* Everything else */ 
330    else if( !memcmp( id, "S3", 2 ) && id[2] == '-' && id[4] == '-' && id[6] == '-' )
331    {
332        tr_snprintf( buf, buflen, "Amazon S3 %c.%c.%c", id[3], id[5], id[7] );
333    }
334    else if( !memcmp( id, "OP", 2 ) )
335    {
336        tr_snprintf( buf, buflen, "Opera (Build %c%c%c%c)", id[2], id[3], id[4], id[5] );
337    }
338    else if( !memcmp( id, "-ML", 3 ) )
339    {
340        tr_snprintf( buf, buflen, "MLDonkey %c%c%c%c%c", id[3], id[4], id[5], id[6], id[7] );
341    }
342    else if( !memcmp( id, "DNA", 3 ) )
343    {
344        tr_snprintf( buf, buflen, "BitTorrent DNA %d.%d.%d", strint(id+3,2),
345                                                             strint(id+5,2),
346                                                             strint(id+7,2) );
347    }
348    else if( !memcmp( id, "Plus", 4 ) )
349    {
350        tr_snprintf( buf, buflen, "Plus! v2 %c.%c%c", id[4], id[5], id[6] );
351    }
352    else if( !memcmp( id, "XBT", 3 ) )
353    {
354        tr_snprintf( buf, buflen, "XBT Client %c.%c.%c%s", id[3], id[4], id[5], getMnemonicEnd(id[6]) );
355    }
356    else if( !memcmp( id, "Mbrst", 5 ) )
357    {
358        tr_snprintf( buf, buflen, "burst! %c.%c.%c", id[5], id[7], id[9] );
359    }
360    else if( !memcmp( id, "btpd", 4 ) )
361    {
362        tr_snprintf( buf, buflen, "BT Protocol Daemon %c%c%c", id[5], id[6], id[7] );
363    }
364    else if( !memcmp( id, "BLZ", 3 ) )
365    {
366        tr_snprintf( buf, buflen, "Blizzard Downloader %d.%d", id[3]+1, id[4] );
367    }
368    else if( '\0' == id[0] && !memcmp( &id[1], "BS", 2 ) )
369    {
370        tr_snprintf( buf, buflen, "BitSpirit %u", ( id[1] == 0 ? 1 : id[1] ) );
371    }
372    else if( !memcmp( id, "QVOD", 4 ) )
373    {
374        four_digits( buf, buflen, "QVOD", id+4 );
375    }
376    else if( !memcmp( id, "-NE", 3 ) )
377    {
378        four_digits( buf, buflen, "BT Next Evolution", id+3 );
379    }
380
381    /* Shad0w-style */
382    {
383        int a, b, c;
384        if( strchr( "AOQRSTU", id[0] )
385            && getShadowInt( id[1], &a )
386            && getShadowInt( id[2], &b )
387            && getShadowInt( id[3], &c ) )
388        {
389            const char * name = NULL;
390
391            switch( id[0] )
392            {
393                case 'A': name = "ABC"; break;
394                case 'O': name = "Osprey"; break;
395                case 'Q': name = "BTQueue"; break;
396                case 'R': name = "Tribler"; break;
397                case 'S': name = "Shad0w"; break;
398                case 'T': name = "BitTornado"; break;
399                case 'U': name = "UPnP NAT Bit Torrent"; break;
400            }
401
402            if( name )
403            {
404                tr_snprintf( buf, buflen, "%s %d.%d.%d", name, a, b, c );
405                return;
406            }
407        }
408    }
409
410    /* No match */
411    if( !*buf )
412    {
413        struct evbuffer * out = evbuffer_new( );
414        const char *in, *in_end;
415        for( in=(const char*)id, in_end=in+8; in!=in_end; ++in ) {
416            if( isprint( *in ) )
417                evbuffer_add_printf( out, "%c", *in );
418            else
419                evbuffer_add_printf( out, "%%%02X", (unsigned int)*in );
420        }
421
422        tr_strlcpy( buf, EVBUFFER_DATA( out ), buflen );
423        evbuffer_free( out );
424    }
425}
Note: See TracBrowser for help on using the repository browser.