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