replaced std::isprint with one that is not dependent on locale. Fixes #384
[libtorrent.git] / src / identify_client.cpp
blobab0965d3f36947d475e589c37a9676a6ccd01990
1 /*
3 Copyright (c) 2003, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/pch.hpp"
35 #include <cctype>
36 #include <algorithm>
38 #ifdef _MSC_VER
39 #pragma warning(push, 1)
40 #endif
42 #include <boost/optional.hpp>
44 #ifdef _MSC_VER
45 #pragma warning(pop)
46 #endif
48 #include "libtorrent/identify_client.hpp"
49 #include "libtorrent/fingerprint.hpp"
51 namespace
54 using namespace libtorrent;
56 int decode_digit(char c)
58 if (std::isdigit(c)) return c - '0';
59 return unsigned(c) - 'A' + 10;
62 bool isprint(char c)
64 return c >= 32 && c < 127;
67 // takes a peer id and returns a valid boost::optional
68 // object if the peer id matched the azureus style encoding
69 // the returned fingerprint contains information about the
70 // client's id
71 boost::optional<fingerprint> parse_az_style(const peer_id& id)
73 fingerprint ret("..", 0, 0, 0, 0);
75 if (id[0] != '-' || !isprint(id[1]) || (id[2] < '0')
76 || (id[3] < '0') || (id[4] < '0')
77 || (id[5] < '0') || (id[6] < '0')
78 || id[7] != '-')
79 return boost::optional<fingerprint>();
81 ret.name[0] = id[1];
82 ret.name[1] = id[2];
83 ret.major_version = decode_digit(id[3]);
84 ret.minor_version = decode_digit(id[4]);
85 ret.revision_version = decode_digit(id[5]);
86 ret.tag_version = decode_digit(id[6]);
88 return boost::optional<fingerprint>(ret);
91 // checks if a peer id can possibly contain a shadow-style
92 // identification
93 boost::optional<fingerprint> parse_shadow_style(const peer_id& id)
95 fingerprint ret("..", 0, 0, 0, 0);
97 if (!std::isalnum(id[0]))
98 return boost::optional<fingerprint>();
100 if (std::equal(id.begin()+4, id.begin()+6, "--"))
102 if ((id[1] < '0') || (id[2] < '0')
103 || (id[3] < '0'))
104 return boost::optional<fingerprint>();
105 ret.major_version = decode_digit(id[1]);
106 ret.minor_version = decode_digit(id[2]);
107 ret.revision_version = decode_digit(id[3]);
109 else
111 if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127)
112 return boost::optional<fingerprint>();
113 ret.major_version = id[1];
114 ret.minor_version = id[2];
115 ret.revision_version = id[3];
118 ret.name[0] = id[0];
119 ret.name[1] = 0;
121 ret.tag_version = 0;
122 return boost::optional<fingerprint>(ret);
125 // checks if a peer id can possibly contain a mainline-style
126 // identification
127 boost::optional<fingerprint> parse_mainline_style(const peer_id& id)
129 char ids[21];
130 std::copy(id.begin(), id.end(), ids);
131 ids[20] = 0;
132 fingerprint ret("..", 0, 0, 0, 0);
133 ret.name[1] = 0;
134 ret.tag_version = 0;
135 if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version
136 , &ret.revision_version) != 4
137 || !isprint(ret.name[0]))
138 return boost::optional<fingerprint>();
140 return boost::optional<fingerprint>(ret);
143 struct map_entry
145 char const* id;
146 char const* name;
149 // only support BitTorrentSpecification
150 // must be ordered alphabetically
151 map_entry name_map[] =
153 {"A", "ABC"}
154 , {"AG", "Ares"}
155 , {"AR", "Arctic Torrent"}
156 , {"AV", "Avicora"}
157 , {"AX", "BitPump"}
158 , {"AZ", "Azureus"}
159 , {"A~", "Ares"}
160 , {"BB", "BitBuddy"}
161 , {"BC", "BitComet"}
162 , {"BF", "Bitflu"}
163 , {"BG", "BTG"}
164 , {"BR", "BitRocket"}
165 , {"BS", "BTSlave"}
166 , {"BX", "BittorrentX"}
167 , {"CD", "Enhanced CTorrent"}
168 , {"CT", "CTorrent"}
169 , {"DE", "Deluge Torrent"}
170 , {"EB", "EBit"}
171 , {"ES", "electric sheep"}
172 , {"HL", "Halite"}
173 , {"HN", "Hydranode"}
174 , {"KT", "KTorrent"}
175 , {"LC", "LeechCraft"}
176 , {"LK", "Linkage"}
177 , {"LP", "lphant"}
178 , {"LT", "libtorrent"}
179 , {"M", "Mainline"}
180 , {"ML", "MLDonkey"}
181 , {"MO", "Mono Torrent"}
182 , {"MP", "MooPolice"}
183 , {"MR", "Miro"}
184 , {"MT", "Moonlight Torrent"}
185 , {"O", "Osprey Permaseed"}
186 , {"PD", "Pando"}
187 , {"Q", "BTQueue"}
188 , {"QT", "Qt 4"}
189 , {"R", "Tribler"}
190 , {"S", "Shadow"}
191 , {"SB", "Swiftbit"}
192 , {"SN", "ShareNet"}
193 , {"SS", "SwarmScope"}
194 , {"ST", "SymTorrent"}
195 , {"SZ", "Shareaza"}
196 , {"S~", "Shareaza (beta)"}
197 , {"T", "BitTornado"}
198 , {"TN", "Torrent.NET"}
199 , {"TR", "Transmission"}
200 , {"TS", "TorrentStorm"}
201 , {"TT", "TuoTu"}
202 , {"U", "UPnP"}
203 , {"UL", "uLeecher"}
204 , {"UT", "uTorrent"}
205 , {"XL", "Xunlei"}
206 , {"XT", "XanTorrent"}
207 , {"XX", "Xtorrent"}
208 , {"ZT", "ZipTorrent"}
209 , {"lt", "rTorrent"}
210 , {"pX", "pHoeniX"}
211 , {"qB", "qBittorrent"}
212 , {"st", "SharkTorrent"}
215 struct generic_map_entry
217 int offset;
218 char const* id;
219 char const* name;
221 // non-standard names
222 generic_map_entry generic_mappings[] =
224 {0, "Deadman Walking-", "Deadman"}
225 , {5, "Azureus", "Azureus 2.0.3.2"}
226 , {0, "DansClient", "XanTorrent"}
227 , {4, "btfans", "SimpleBT"}
228 , {0, "PRC.P---", "Bittorrent Plus! II"}
229 , {0, "P87.P---", "Bittorrent Plus!"}
230 , {0, "S587Plus", "Bittorrent Plus!"}
231 , {0, "martini", "Martini Man"}
232 , {0, "Plus---", "Bittorrent Plus"}
233 , {0, "turbobt", "TurboBT"}
234 , {0, "a00---0", "Swarmy"}
235 , {0, "a02---0", "Swarmy"}
236 , {0, "T00---0", "Teeweety"}
237 , {0, "BTDWV-", "Deadman Walking"}
238 , {2, "BS", "BitSpirit"}
239 , {0, "Pando-", "Pando"}
240 , {0, "LIME", "LimeWire"}
241 , {0, "btuga", "BTugaXP"}
242 , {0, "oernu", "BTugaXP"}
243 , {0, "Mbrst", "Burst!"}
244 , {0, "PEERAPP", "PeerApp"}
245 , {0, "Plus", "Plus!"}
246 , {0, "-Qt-", "Qt"}
247 , {0, "exbc", "BitComet"}
248 , {0, "DNA", "BitTorrent DNA"}
249 , {0, "-G3", "G3 Torrent"}
250 , {0, "-FG", "FlashGet"}
251 , {0, "-ML", "MLdonkey"}
252 , {0, "XBT", "XBT"}
253 , {0, "OP", "Opera"}
254 , {2, "RS", "Rufus"}
255 , {0, "AZ2500BT", "BitTyrant"}
258 bool compare_id(map_entry const& lhs, map_entry const& rhs)
260 return lhs.id[0] < rhs.id[0]
261 || ((lhs.id[0] == rhs.id[0]) && (lhs.id[1] < rhs.id[1]));
264 std::string lookup(fingerprint const& f)
266 std::stringstream identity;
268 const int size = sizeof(name_map)/sizeof(name_map[0]);
269 map_entry tmp = {f.name, ""};
270 map_entry* i =
271 std::lower_bound(name_map, name_map + size
272 , tmp, &compare_id);
274 #ifndef NDEBUG
275 for (int i = 1; i < size; ++i)
277 TORRENT_ASSERT(compare_id(name_map[i-1]
278 , name_map[i]));
280 #endif
282 if (i < name_map + size && std::equal(f.name, f.name + 2, i->id))
283 identity << i->name;
284 else
286 identity << f.name[0];
287 if (f.name[1] != 0) identity << f.name[1];
290 identity << " " << (int)f.major_version
291 << "." << (int)f.minor_version
292 << "." << (int)f.revision_version;
294 if (f.name[1] != 0)
295 identity << "." << (int)f.tag_version;
297 return identity.str();
300 bool find_string(unsigned char const* id, char const* search)
302 return std::equal(search, search + std::strlen(search), id);
306 namespace libtorrent
309 boost::optional<fingerprint> client_fingerprint(peer_id const& p)
311 // look for azureus style id
312 boost::optional<fingerprint> f;
313 f = parse_az_style(p);
314 if (f) return f;
316 // look for shadow style id
317 f = parse_shadow_style(p);
318 if (f) return f;
320 // look for mainline style id
321 f = parse_mainline_style(p);
322 if (f) return f;
323 return f;
326 std::string identify_client(peer_id const& p)
328 peer_id::const_iterator PID = p.begin();
329 boost::optional<fingerprint> f;
331 if (p.is_all_zeros()) return "Unknown";
333 // ----------------------
334 // non standard encodings
335 // ----------------------
337 int num_generic_mappings = sizeof(generic_mappings) / sizeof(generic_mappings[0]);
339 for (int i = 0; i < num_generic_mappings; ++i)
341 generic_map_entry const& e = generic_mappings[i];
342 if (find_string(PID + e.offset, e.id)) return e.name;
345 if (find_string(PID, "-BOW") && PID[7] == '-')
346 return "Bits on Wheels " + std::string(PID + 4, PID + 7);
349 if (find_string(PID, "eX"))
351 std::string user(PID + 2, PID + 14);
352 return std::string("eXeem ('") + user.c_str() + "')";
355 if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
356 return "Experimental 3.2.1b2";
358 if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
359 return "Experimental 3.1";
362 // look for azureus style id
363 f = parse_az_style(p);
364 if (f) return lookup(*f);
366 // look for shadow style id
367 f = parse_shadow_style(p);
368 if (f) return lookup(*f);
370 // look for mainline style id
371 f = parse_mainline_style(p);
372 if (f) return lookup(*f);
375 if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
376 return "Generic";
378 std::string unknown("Unknown [");
379 for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i)
381 unknown += isprint(*i)?*i:'.';
383 unknown += "]";
384 return unknown;