1 /*
2  * This file Copyright (C) 2008-2014 Mnemosyne LLC
3  *
4  * It may be used under the GNU GPL versions 2 or 3
5  * or any future license endorsed by Mnemosyne LLC.
6  *
7  */
8 
9 /* thanks amc1! */
10 
11 #include <ctype.h> /* isprint() */
12 #include <stdlib.h> /* strtol() */
13 #include <string.h>
14 
15 #include "transmission.h"
16 #include "clients.h"
17 #include "utils.h" /* tr_snprintf(), tr_strlcpy() */
18 
charint(uint8_t ch)19 static int charint(uint8_t ch)
20 {
21     if ('0' <= ch && ch <= '9')
22     {
23         return ch - '0';
24     }
25 
26     if ('A' <= ch && ch <= 'Z')
27     {
28         return 10 + ch - 'A';
29     }
30 
31     if ('a' <= ch && ch <= 'z')
32     {
33         return 36 + ch - 'a';
34     }
35 
36     return 0;
37 }
38 
getShadowInt(uint8_t ch,int * setme)39 static bool getShadowInt(uint8_t ch, int* setme)
40 {
41     char const* str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-";
42     char const* pch = strchr(str, ch);
43 
44     if (pch == NULL)
45     {
46         return false;
47     }
48 
49     *setme = pch - str;
50     return true;
51 }
52 
getFDMInt(uint8_t ch,int * setme)53 static bool getFDMInt(uint8_t ch, int* setme)
54 {
55     char const* str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*()";
56     char const* pch = strchr(str, ch);
57 
58     if (pch == NULL)
59     {
60         return false;
61     }
62 
63     *setme = pch - str;
64     return true;
65 }
66 
strint(void const * pch,int span)67 static int strint(void const* pch, int span)
68 {
69     char tmp[64];
70     memcpy(tmp, pch, span);
71     tmp[span] = '\0';
72     return strtol(tmp, NULL, 0);
73 }
74 
getMnemonicEnd(uint8_t ch)75 static char const* getMnemonicEnd(uint8_t ch)
76 {
77     switch (ch)
78     {
79     case 'b':
80     case 'B':
81         return " (Beta)";
82 
83     case 'd':
84         return " (Debug)";
85 
86     case 'x':
87     case 'X':
88     case 'Z':
89         return " (Dev)";
90 
91     default:
92         return "";
93     }
94 }
95 
three_digits(char * buf,size_t buflen,char const * name,uint8_t const * digits)96 static void three_digits(char* buf, size_t buflen, char const* name, uint8_t const* digits)
97 {
98     tr_snprintf(buf, buflen, "%s %d.%d.%d", name, charint(digits[0]), charint(digits[1]), charint(digits[2]));
99 }
100 
four_digits(char * buf,size_t buflen,char const * name,uint8_t const * digits)101 static void four_digits(char* buf, size_t buflen, char const* name, uint8_t const* digits)
102 {
103     tr_snprintf(buf, buflen, "%s %d.%d.%d.%d", name, charint(digits[0]), charint(digits[1]), charint(digits[2]),
104         charint(digits[3]));
105 }
106 
two_major_two_minor(char * buf,size_t buflen,char const * name,uint8_t const * digits)107 static void two_major_two_minor(char* buf, size_t buflen, char const* name, uint8_t const* digits)
108 {
109     tr_snprintf(buf, buflen, "%s %d.%02d", name, strint(digits, 2), strint(digits + 2, 2));
110 }
111 
no_version(char * buf,size_t buflen,char const * name)112 static void no_version(char* buf, size_t buflen, char const* name)
113 {
114     tr_strlcpy(buf, name, buflen);
115 }
116 
mainline_style(char * buf,size_t buflen,char const * name,uint8_t const * id)117 static void mainline_style(char* buf, size_t buflen, char const* name, uint8_t const* id)
118 {
119     if (id[4] == '-' && id[6] == '-')
120     {
121         tr_snprintf(buf, buflen, "%s %c.%c.%c", name, id[1], id[3], id[5]);
122     }
123     else if (id[5] == '-')
124     {
125         tr_snprintf(buf, buflen, "%s %c.%c%c.%c", name, id[1], id[3], id[4], id[6]);
126     }
127 }
128 
isMainlineStyle(uint8_t const * peer_id)129 static bool isMainlineStyle(uint8_t const* peer_id)
130 {
131     /**
132      * One of the following styles will be used:
133      *   Mx-y-z--
134      *   Mx-yy-z-
135      */
136     return peer_id[2] == '-' && peer_id[7] == '-' && (peer_id[4] == '-' || peer_id[5] == '-');
137 }
138 
decodeBitCometClient(char * buf,size_t buflen,uint8_t const * id)139 static bool decodeBitCometClient(char* buf, size_t buflen, uint8_t const* id)
140 {
141     char const* chid = (char*)id;
142     bool is_bitlord;
143     int major;
144     int minor;
145     char const* name;
146     char const* mod = NULL;
147 
148     if (strncmp(chid, "exbc", 4) == 0)
149     {
150         mod = "";
151     }
152     else if (strncmp(chid, "FUTB", 4) == 0)
153     {
154         mod = " (Solidox Mod) ";
155     }
156     else if (strncmp(chid, "xUTB", 4) == 0)
157     {
158         mod = " (Mod 2) ";
159     }
160     else
161     {
162         return false;
163     }
164 
165     is_bitlord = strncmp(chid + 6, "LORD", 4) == 0;
166     name = (is_bitlord) ? "BitLord " : "BitComet ";
167     major = id[4];
168     minor = id[5];
169 
170     /**
171      * Bitcomet, and older versions of BitLord, are of the form x.yy.
172      * Bitcoment 1.0 and onwards are of the form x.y.
173      */
174     if (is_bitlord && major > 0)
175     {
176         tr_snprintf(buf, buflen, "%s%s%d.%d", name, mod, major, minor);
177     }
178     else
179     {
180         tr_snprintf(buf, buflen, "%s%s%d.%02d", name, mod, major, minor);
181     }
182 
183     return true;
184 }
185 
tr_clientForId(char * buf,size_t buflen,void const * id_in)186 char* tr_clientForId(char* buf, size_t buflen, void const* id_in)
187 {
188     uint8_t const* id = id_in;
189     char const* chid = (char*)id;
190 
191     *buf = '\0';
192 
193     if (id == NULL)
194     {
195         return buf;
196     }
197 
198     /* Azureus-style */
199     if (id[0] == '-' && id[7] == '-')
200     {
201         if (strncmp(chid + 1, "TR", 2) == 0)
202         {
203             if (strncmp(chid + 3, "000", 3) == 0) /* very old client style: -TR0006- is 0.6 */
204             {
205                 tr_snprintf(buf, buflen, "Transmission 0.%c", id[6]);
206             }
207             else if (strncmp(chid + 3, "00", 2) == 0) /* previous client style: -TR0072- is 0.72 */
208             {
209                 tr_snprintf(buf, buflen, "Transmission 0.%02d", strint(id + 5, 2));
210             }
211             else /* current client style: -TR111Z- is 1.11+ */
212             {
213                 tr_snprintf(buf, buflen, "Transmission %d.%02d%s", strint(id + 3, 1), strint(id + 4, 2),
214                     (id[6] == 'Z' || id[6] == 'X') ? "+" : "");
215             }
216         }
217         else if (strncmp(chid + 1, "UT", 2) == 0)
218         {
219             tr_snprintf(buf, buflen, "\xc2\xb5Torrent %d.%d.%d%s", strint(id + 3, 1), strint(id + 4, 1), strint(id + 5, 1),
220                 getMnemonicEnd(id[6]));
221         }
222         else if (strncmp(chid + 1, "BT", 2) == 0)
223         {
224             tr_snprintf(buf, buflen, "BitTorrent %d.%d.%d%s", strint(id + 3, 1), strint(id + 4, 1), strint(id + 5, 1),
225                 getMnemonicEnd(id[6]));
226         }
227         else if (strncmp(chid + 1, "UM", 2) == 0)
228         {
229             tr_snprintf(buf, buflen, "\xc2\xb5Torrent Mac %d.%d.%d%s", strint(id + 3, 1), strint(id + 4, 1), strint(id + 5, 1),
230                 getMnemonicEnd(id[6]));
231         }
232         else if (strncmp(chid + 1, "UE", 2) == 0)
233         {
234             tr_snprintf(buf, buflen, "\xc2\xb5Torrent Embedded %d.%d.%d%s", strint(id + 3, 1), strint(id + 4, 1),
235                 strint(id + 5, 1), getMnemonicEnd(id[6]));
236         }
237         /* */
238         else if (strncmp(chid + 1, "AZ", 2) == 0)
239         {
240             if (id[3] > '3' || (id[3] == '3' && id[4] >= '1')) /* Vuze starts at version 3.1.0.0 */
241             {
242                 four_digits(buf, buflen, "Vuze", id + 3);
243             }
244             else
245             {
246                 four_digits(buf, buflen, "Azureus", id + 3);
247             }
248         }
249         /* */
250         else if (strncmp(chid + 1, "KT", 2) == 0)
251         {
252             if (id[5] == 'D')
253             {
254                 tr_snprintf(buf, buflen, "KTorrent %d.%d Dev %d", charint(id[3]), charint(id[4]), charint(id[6]));
255             }
256             else if (id[5] == 'R')
257             {
258                 tr_snprintf(buf, buflen, "KTorrent %d.%d RC %d", charint(id[3]), charint(id[4]), charint(id[6]));
259             }
260             else
261             {
262                 three_digits(buf, buflen, "KTorrent", id + 3);
263             }
264         }
265         /* */
266         else if (strncmp(chid + 1, "AG", 2) == 0)
267         {
268             four_digits(buf, buflen, "Ares", id + 3);
269         }
270         else if (strncmp(chid + 1, "AR", 2) == 0)
271         {
272             four_digits(buf, buflen, "Arctic", id + 3);
273         }
274         else if (strncmp(chid + 1, "AT", 2) == 0)
275         {
276             four_digits(buf, buflen, "Artemis", id + 3);
277         }
278         else if (strncmp(chid + 1, "AV", 2) == 0)
279         {
280             four_digits(buf, buflen, "Avicora", id + 3);
281         }
282         else if (strncmp(chid + 1, "BE", 2) == 0)
283         {
284             four_digits(buf, buflen, "BitTorrent SDK", id + 3);
285         }
286         else if (strncmp(chid + 1, "BG", 2) == 0)
287         {
288             four_digits(buf, buflen, "BTGetit", id + 3);
289         }
290         else if (strncmp(chid + 1, "BH", 2) == 0)
291         {
292             four_digits(buf, buflen, "BitZilla", id + 3);
293         }
294         else if (strncmp(chid + 1, "BM", 2) == 0)
295         {
296             four_digits(buf, buflen, "BitMagnet", id + 3);
297         }
298         else if (strncmp(chid + 1, "BP", 2) == 0)
299         {
300             four_digits(buf, buflen, "BitTorrent Pro (Azureus + Spyware)", id + 3);
301         }
302         else if (strncmp(chid + 1, "BX", 2) == 0)
303         {
304             four_digits(buf, buflen, "BittorrentX", id + 3);
305         }
306         else if (strncmp(chid + 1, "bk", 2) == 0)
307         {
308             four_digits(buf, buflen, "BitKitten (libtorrent)", id + 3);
309         }
310         else if (strncmp(chid + 1, "BS", 2) == 0)
311         {
312             four_digits(buf, buflen, "BTSlave", id + 3);
313         }
314         else if (strncmp(chid + 1, "BW", 2) == 0)
315         {
316             four_digits(buf, buflen, "BitWombat", id + 3);
317         }
318         else if (strncmp(chid + 1, "EB", 2) == 0)
319         {
320             four_digits(buf, buflen, "EBit", id + 3);
321         }
322         else if (strncmp(chid + 1, "DE", 2) == 0)
323         {
324             four_digits(buf, buflen, "Deluge", id + 3);
325         }
326         else if (strncmp(chid + 1, "DP", 2) == 0)
327         {
328             four_digits(buf, buflen, "Propagate Data Client", id + 3);
329         }
330         else if (strncmp(chid + 1, "FC", 2) == 0)
331         {
332             four_digits(buf, buflen, "FileCroc", id + 3);
333         }
334         else if (strncmp(chid + 1, "FT", 2) == 0)
335         {
336             four_digits(buf, buflen, "FoxTorrent/RedSwoosh", id + 3);
337         }
338         else if (strncmp(chid + 1, "GR", 2) == 0)
339         {
340             four_digits(buf, buflen, "GetRight", id + 3);
341         }
342         else if (strncmp(chid + 1, "GS", 2) == 0)
343         {
344             four_digits(buf, buflen, "GSTorrent", id + 3);
345         }
346         else if (strncmp(chid + 1, "HK", 2) == 0)
347         {
348             four_digits(buf, buflen, "Hekate", id + 3);
349         }
350         else if (strncmp(chid + 1, "HN", 2) == 0)
351         {
352             four_digits(buf, buflen, "Hydranode", id + 3);
353         }
354         else if (strncmp(chid + 1, "KG", 2) == 0)
355         {
356             four_digits(buf, buflen, "KGet", id + 3);
357         }
358         else if (strncmp(chid + 1, "LC", 2) == 0)
359         {
360             four_digits(buf, buflen, "LeechCraft", id + 3);
361         }
362         else if (strncmp(chid + 1, "LH", 2) == 0)
363         {
364             four_digits(buf, buflen, "LH-ABC", id + 3);
365         }
366         else if (strncmp(chid + 1, "NX", 2) == 0)
367         {
368             four_digits(buf, buflen, "Net Transport", id + 3);
369         }
370         else if (strncmp(chid + 1, "MK", 2) == 0)
371         {
372             four_digits(buf, buflen, "Meerkat", id + 3);
373         }
374         else if (strncmp(chid + 1, "MO", 2) == 0)
375         {
376             four_digits(buf, buflen, "MonoTorrent", id + 3);
377         }
378         else if (strncmp(chid + 1, "MR", 2) == 0)
379         {
380             four_digits(buf, buflen, "Miro", id + 3);
381         }
382         else if (strncmp(chid + 1, "MT", 2) == 0)
383         {
384             four_digits(buf, buflen, "Moonlight", id + 3);
385         }
386         else if (strncmp(chid + 1, "OS", 2) == 0)
387         {
388             four_digits(buf, buflen, "OneSwarm", id + 3);
389         }
390         else if (strncmp(chid + 1, "OT", 2) == 0)
391         {
392             four_digits(buf, buflen, "OmegaTorrent", id + 3);
393         }
394         else if (strncmp(chid + 1, "PD", 2) == 0)
395         {
396             four_digits(buf, buflen, "Pando", id + 3);
397         }
398         else if (strncmp(chid + 1, "QD", 2) == 0)
399         {
400             four_digits(buf, buflen, "QQDownload", id + 3);
401         }
402         else if (strncmp(chid + 1, "RS", 2) == 0)
403         {
404             four_digits(buf, buflen, "Rufus", id + 3);
405         }
406         else if (strncmp(chid + 1, "RT", 2) == 0)
407         {
408             four_digits(buf, buflen, "Retriever", id + 3);
409         }
410         else if (strncmp(chid + 1, "RZ", 2) == 0)
411         {
412             four_digits(buf, buflen, "RezTorrent", id + 3);
413         }
414         else if (strncmp(chid + 1, "SD", 2) == 0)
415         {
416             four_digits(buf, buflen, "Thunder", id + 3);
417         }
418         else if (strncmp(chid + 1, "SM", 2) == 0)
419         {
420             four_digits(buf, buflen, "SoMud", id + 3);
421         }
422         else if (strncmp(chid + 1, "SS", 2) == 0)
423         {
424             four_digits(buf, buflen, "SwarmScope", id + 3);
425         }
426         else if (strncmp(chid + 1, "ST", 2) == 0)
427         {
428             four_digits(buf, buflen, "SymTorrent", id + 3);
429         }
430         else if (strncmp(chid + 1, "SZ", 2) == 0)
431         {
432             four_digits(buf, buflen, "Shareaza", id + 3);
433         }
434         else if (strncmp(chid + 1, "S~", 2) == 0)
435         {
436             four_digits(buf, buflen, "Shareaza", id + 3);
437         }
438         else if (strncmp(chid + 1, "st", 2) == 0)
439         {
440             four_digits(buf, buflen, "SharkTorrent", id + 3);
441         }
442         else if (strncmp(chid + 1, "TN", 2) == 0)
443         {
444             four_digits(buf, buflen, "Torrent .NET", id + 3);
445         }
446         else if (strncmp(chid + 1, "TS", 2) == 0)
447         {
448             four_digits(buf, buflen, "TorrentStorm", id + 3);
449         }
450         else if (strncmp(chid + 1, "TT", 2) == 0)
451         {
452             four_digits(buf, buflen, "TuoTu", id + 3);
453         }
454         else if (strncmp(chid + 1, "UL", 2) == 0)
455         {
456             four_digits(buf, buflen, "uLeecher!", id + 3);
457         }
458         else if (strncmp(chid + 1, "VG", 2) == 0)
459         {
460             four_digits(buf, buflen, "Vagaa", id + 3);
461         }
462         else if (strncmp(chid + 1, "WT", 2) == 0)
463         {
464             four_digits(buf, buflen, "BitLet", id + 3);
465         }
466         else if (strncmp(chid + 1, "WY", 2) == 0)
467         {
468             four_digits(buf, buflen, "FireTorrent", id + 3);
469         }
470         else if (strncmp(chid + 1, "XL", 2) == 0)
471         {
472             four_digits(buf, buflen, "Xunlei", id + 3);
473         }
474         else if (strncmp(chid + 1, "XS", 2) == 0)
475         {
476             four_digits(buf, buflen, "XSwifter", id + 3);
477         }
478         else if (strncmp(chid + 1, "XT", 2) == 0)
479         {
480             four_digits(buf, buflen, "XanTorrent", id + 3);
481         }
482         else if (strncmp(chid + 1, "XX", 2) == 0)
483         {
484             four_digits(buf, buflen, "Xtorrent", id + 3);
485         }
486         else if (strncmp(chid + 1, "ZT", 2) == 0)
487         {
488             four_digits(buf, buflen, "Zip Torrent", id + 3);
489         }
490         else if (strncmp(chid + 1, "ZO", 2) == 0)
491         {
492             four_digits(buf, buflen, "Zona", id + 3);
493         }
494         /* */
495         else if (strncmp(chid + 1, "A~", 2) == 0)
496         {
497             three_digits(buf, buflen, "Ares", id + 3);
498         }
499         else if (strncmp(chid + 1, "ES", 2) == 0)
500         {
501             three_digits(buf, buflen, "Electric Sheep", id + 3);
502         }
503         else if (strncmp(chid + 1, "HL", 2) == 0)
504         {
505             three_digits(buf, buflen, "Halite", id + 3);
506         }
507         else if (strncmp(chid + 1, "LT", 2) == 0)
508         {
509             three_digits(buf, buflen, "libtorrent (Rasterbar)", id + 3);
510         }
511         else if (strncmp(chid + 1, "lt", 2) == 0)
512         {
513             three_digits(buf, buflen, "libTorrent (Rakshasa)", id + 3);
514         }
515         else if (strncmp(chid + 1, "MP", 2) == 0)
516         {
517             three_digits(buf, buflen, "MooPolice", id + 3);
518         }
519         else if (strncmp(chid + 1, "pb", 2) == 0)
520         {
521             three_digits(buf, buflen, "pbTorrent", id + 3);
522         }
523         else if (strncmp(chid + 1, "qB", 2) == 0)
524         {
525             three_digits(buf, buflen, "qBittorrent", id + 3);
526         }
527         /* */
528         else if (strncmp(chid + 1, "AX", 2) == 0)
529         {
530             two_major_two_minor(buf, buflen, "BitPump", id + 3);
531         }
532         else if (strncmp(chid + 1, "BC", 2) == 0)
533         {
534             two_major_two_minor(buf, buflen, "BitComet", id + 3);
535         }
536         else if (strncmp(chid + 1, "CD", 2) == 0)
537         {
538             two_major_two_minor(buf, buflen, "Enhanced CTorrent", id + 3);
539         }
540         else if (strncmp(chid + 1, "LP", 2) == 0)
541         {
542             two_major_two_minor(buf, buflen, "Lphant", id + 3);
543         }
544         /* */
545         else if (strncmp(chid + 1, "BF", 2) == 0)
546         {
547             no_version(buf, buflen, "BitFlu");
548         }
549         else if (strncmp(chid + 1, "LW", 2) == 0)
550         {
551             no_version(buf, buflen, "LimeWire");
552         }
553         /* */
554         else if (strncmp(chid + 1, "BB", 2) == 0)
555         {
556             tr_snprintf(buf, buflen, "BitBuddy %c.%c%c%c", id[3], id[4], id[5], id[6]);
557         }
558         else if (strncmp(chid + 1, "BR", 2) == 0)
559         {
560             tr_snprintf(buf, buflen, "BitRocket %c.%c (%c%c)", id[3], id[4], id[5], id[6]);
561         }
562         else if (strncmp(chid + 1, "CT", 2) == 0)
563         {
564             tr_snprintf(buf, buflen, "CTorrent %d.%d.%02d", charint(id[3]), charint(id[4]), strint(id + 5, 2));
565         }
566         else if (strncmp(chid + 1, "XC", 2) == 0 || strncmp(chid + 1, "XX", 2) == 0)
567         {
568             tr_snprintf(buf, buflen, "Xtorrent %d.%d (%d)", charint(id[3]), charint(id[4]), strint(id + 5, 2));
569         }
570         else if (strncmp(chid + 1, "BOW", 3) == 0)
571         {
572             if (strncmp(&chid[4], "A0B", 3) == 0)
573             {
574                 tr_snprintf(buf, buflen, "Bits on Wheels 1.0.5");
575             }
576             else if (strncmp(&chid[4], "A0C", 3) == 0)
577             {
578                 tr_snprintf(buf, buflen, "Bits on Wheels 1.0.6");
579             }
580             else
581             {
582                 tr_snprintf(buf, buflen, "Bits on Wheels %c.%c.%c", id[4], id[5], id[5]);
583             }
584         }
585         else if (strncmp(chid + 1, "MG", 2) == 0)
586         {
587             tr_snprintf(buf, buflen, "MediaGet %d.%02d", charint(id[3]), charint(id[4]));
588         }
589         else if (strncmp(chid + 1, "XF", 2) == 0)
590         {
591             if (chid[6] == '0')
592             {
593                 three_digits(buf, buflen, "Xfplay", id + 3);
594             }
595             else
596             {
597                 tr_snprintf(buf, buflen, "Xfplay %d.%d.%d", strint(id + 3, 1), strint(id + 4, 1), strint(id + 5, 2));
598             }
599         }
600         else if (strncmp(chid + 1, "PI", 2) == 0)
601         {
602             tr_snprintf(buf, buflen, "PicoTorrent %d.%d%d.%d", charint(id[3]), charint(id[4]), charint(id[5]), charint(id[6]));
603         }
604         else if (strncmp(chid + 1, "FD", 2) == 0)
605         {
606             int c;
607 
608             if (getFDMInt(id[5], &c))
609             {
610                 tr_snprintf(buf, buflen, "Free Download Manager %d.%d.%d", charint(id[3]), charint(id[4]), c);
611             }
612             else
613             {
614                 tr_snprintf(buf, buflen, "Free Download Manager %d.%d.x", charint(id[3]), charint(id[4]));
615             }
616         }
617         else if (strncmp(chid + 1, "FL", 2) == 0)
618         {
619             tr_snprintf(buf, buflen, "Folx %d.x", charint(id[3]));
620         }
621         else if (strncmp(chid + 1, "BN", 2) == 0)
622         {
623             tr_snprintf(buf, buflen, "Baidu Netdisk");
624         }
625 
626         if (!tr_str_is_empty(buf))
627         {
628             return buf;
629         }
630     }
631 
632     /* uTorrent will replace the trailing dash with an extra digit for longer version numbers */
633     if (id[0] == '-')
634     {
635         if (strncmp(chid + 1, "UT", 2) == 0)
636         {
637             tr_snprintf(buf, buflen, "\xc2\xb5Torrent %d.%d.%d%s", strint(id + 3, 1), strint(id + 4, 1), strint(id + 5, 2),
638                 getMnemonicEnd(id[7]));
639         }
640         else if (strncmp(chid + 1, "UM", 2) == 0)
641         {
642             tr_snprintf(buf, buflen, "\xc2\xb5Torrent Mac %d.%d.%d%s", strint(id + 3, 1), strint(id + 4, 1), strint(id + 5, 2),
643                 getMnemonicEnd(id[7]));
644         }
645         else if (strncmp(chid + 1, "UE", 2) == 0)
646         {
647             tr_snprintf(buf, buflen, "\xc2\xb5Torrent Embedded %d.%d.%d%s", strint(id + 3, 1), strint(id + 4, 1),
648                 strint(id + 5, 2), getMnemonicEnd(id[7]));
649         }
650 
651         if (!tr_str_is_empty(buf))
652         {
653             return buf;
654         }
655     }
656 
657     /* Mainline */
658     if (isMainlineStyle(id))
659     {
660         if (*id == 'M')
661         {
662             mainline_style(buf, buflen, "BitTorrent", id);
663         }
664 
665         if (*id == 'Q')
666         {
667             mainline_style(buf, buflen, "Queen Bee", id);
668         }
669 
670         if (!tr_str_is_empty(buf))
671         {
672             return buf;
673         }
674     }
675 
676     if (decodeBitCometClient(buf, buflen, id))
677     {
678         return buf;
679     }
680 
681     /* Clients with no version */
682     if (strncmp(chid, "AZ2500BT", 8) == 0)
683     {
684         no_version(buf, buflen, "BitTyrant (Azureus Mod)");
685     }
686     else if (strncmp(chid, "LIME", 4) == 0)
687     {
688         no_version(buf, buflen, "Limewire");
689     }
690     else if (strncmp(chid, "martini", 7) == 0)
691     {
692         no_version(buf, buflen, "Martini Man");
693     }
694     else if (strncmp(chid, "Pando", 5) == 0)
695     {
696         no_version(buf, buflen, "Pando");
697     }
698     else if (strncmp(chid, "a00---0", 7) == 0)
699     {
700         no_version(buf, buflen, "Swarmy");
701     }
702     else if (strncmp(chid, "a02---0", 7) == 0)
703     {
704         no_version(buf, buflen, "Swarmy");
705     }
706     else if (strncmp(chid, "-G3", 3) == 0)
707     {
708         no_version(buf, buflen, "G3 Torrent");
709     }
710     else if (strncmp(chid, "10-------", 9) == 0)
711     {
712         no_version(buf, buflen, "JVtorrent");
713     }
714     else if (strncmp(chid, "346-", 4) == 0)
715     {
716         no_version(buf, buflen, "TorrentTopia");
717     }
718     else if (strncmp(chid, "eX", 2) == 0)
719     {
720         no_version(buf, buflen, "eXeem");
721     }
722     else if (strncmp(chid, "aria2-", 6) == 0)
723     {
724         no_version(buf, buflen, "aria2");
725     }
726     else if (strncmp(chid, "-WT-", 4) == 0)
727     {
728         no_version(buf, buflen, "BitLet");
729     }
730     else if (strncmp(chid, "-FG", 3) == 0)
731     {
732         two_major_two_minor(buf, buflen, "FlashGet", id + 3);
733     }
734     /* Everything else */
735     else if (strncmp(chid, "S3", 2) == 0 && id[2] == '-' && id[4] == '-' && id[6] == '-')
736     {
737         tr_snprintf(buf, buflen, "Amazon S3 %c.%c.%c", id[3], id[5], id[7]);
738     }
739     else if (strncmp(chid, "OP", 2) == 0)
740     {
741         tr_snprintf(buf, buflen, "Opera (Build %c%c%c%c)", id[2], id[3], id[4], id[5]);
742     }
743     else if (strncmp(chid, "-ML", 3) == 0)
744     {
745         tr_snprintf(buf, buflen, "MLDonkey %c%c%c%c%c", id[3], id[4], id[5], id[6], id[7]);
746     }
747     else if (strncmp(chid, "DNA", 3) == 0)
748     {
749         tr_snprintf(buf, buflen, "BitTorrent DNA %d.%d.%d", strint(id + 3, 2), strint(id + 5, 2), strint(id + 7, 2));
750     }
751     else if (strncmp(chid, "Plus", 4) == 0)
752     {
753         tr_snprintf(buf, buflen, "Plus! v2 %c.%c%c", id[4], id[5], id[6]);
754     }
755     else if (strncmp(chid, "XBT", 3) == 0)
756     {
757         tr_snprintf(buf, buflen, "XBT Client %c.%c.%c%s", id[3], id[4], id[5], getMnemonicEnd(id[6]));
758     }
759     else if (strncmp(chid, "Mbrst", 5) == 0)
760     {
761         tr_snprintf(buf, buflen, "burst! %c.%c.%c", id[5], id[7], id[9]);
762     }
763     else if (strncmp(chid, "btpd", 4) == 0)
764     {
765         tr_snprintf(buf, buflen, "BT Protocol Daemon %c%c%c", id[5], id[6], id[7]);
766     }
767     else if (strncmp(chid, "BLZ", 3) == 0)
768     {
769         tr_snprintf(buf, buflen, "Blizzard Downloader %d.%d", id[3] + 1, id[4]);
770     }
771     else if (strncmp(chid, "-SP", 3) == 0)
772     {
773         three_digits(buf, buflen, "BitSpirit", id + 3);
774     }
775     else if ('\0' == id[0] && strncmp(chid + 2, "BS", 2) == 0)
776     {
777         tr_snprintf(buf, buflen, "BitSpirit %u", (id[1] == 0 ? 1 : id[1]));
778     }
779     else if (strncmp(chid, "QVOD", 4) == 0)
780     {
781         four_digits(buf, buflen, "QVOD", id + 4);
782     }
783     else if (strncmp(chid, "-NE", 3) == 0)
784     {
785         four_digits(buf, buflen, "BT Next Evolution", id + 3);
786     }
787     else if (strncmp(chid, "TIX", 3) == 0)
788     {
789         two_major_two_minor(buf, buflen, "Tixati", id + 3);
790     }
791 
792     /* Shad0w-style */
793     if (tr_str_is_empty(buf))
794     {
795         int a;
796         int b;
797         int c;
798 
799         if (strchr("AOQRSTU", id[0]) != NULL && getShadowInt(id[1], &a) && getShadowInt(id[2], &b) && getShadowInt(id[3], &c))
800         {
801             char const* name = NULL;
802 
803             switch (id[0])
804             {
805             case 'A':
806                 name = "ABC";
807                 break;
808 
809             case 'O':
810                 name = "Osprey";
811                 break;
812 
813             case 'Q':
814                 name = "BTQueue";
815                 break;
816 
817             case 'R':
818                 name = "Tribler";
819                 break;
820 
821             case 'S':
822                 name = "Shad0w";
823                 break;
824 
825             case 'T':
826                 name = "BitTornado";
827                 break;
828 
829             case 'U':
830                 name = "UPnP NAT Bit Torrent";
831                 break;
832             }
833 
834             if (name != NULL)
835             {
836                 tr_snprintf(buf, buflen, "%s %d.%d.%d", name, a, b, c);
837                 return buf;
838             }
839         }
840     }
841 
842     /* No match */
843     if (tr_str_is_empty(buf))
844     {
845         char out[32];
846         char* walk = out;
847 
848         for (size_t i = 0; i < 8; ++i)
849         {
850             char const c = chid[i];
851 
852             if (isprint((unsigned char)c))
853             {
854                 *walk++ = c;
855             }
856             else
857             {
858                 tr_snprintf(walk, out + sizeof(out) - walk, "%%%02X", (unsigned int)c);
859                 walk += 3;
860             }
861         }
862 
863         *walk = '\0';
864         tr_strlcpy(buf, out, buflen);
865     }
866 
867     return buf;
868 }
869