1 /*
2 * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include "stdinc.h"
20
21 #include "Util.h"
22 #include "File.h"
23
24 #include "StringTokenizer.h"
25 #include "ClientManager.h"
26 #include "SettingsManager.h"
27 #include "LogManager.h"
28 #include "version.h"
29 #include "File.h"
30 #include "SimpleXML.h"
31
32 #ifdef _WIN32
33 #include "w.h"
34 #include "shlobj.h"
35 #include "lmcons.h"
36 #else
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <netdb.h>
41 #include <sys/utsname.h>
42 #include <cctype>
43 #endif
44 #ifdef HAVE_IFADDRS_H
45 #include <ifaddrs.h>
46 #include <net/if.h>
47 #endif
48 #include <locale.h>
49
50 #include "CID.h"
51
52 #include "FastAlloc.h"
53
54 #ifdef USE_IDNA
55 #include <idna.h>
56 #endif
57
58 namespace dcpp {
59
60 #ifndef _DEBUG
61 FastCriticalSection FastAllocBase::cs;
62 #endif
63 time_t Util::startTime = time(NULL);
64 string Util::emptyString;
65 wstring Util::emptyStringW;
66 tstring Util::emptyStringT;
67
68 bool Util::away = false;
69 bool Util::manualAway = false;
70 string Util::awayMsg;
71 time_t Util::awayTime;
72
73 Util::CountryList Util::countries;
74
75 string Util::paths[Util::PATH_LAST];
76
77 bool Util::localMode = true;
78
79 static void sgenrand(unsigned long seed);
80
bz_internal_error(int errcode)81 extern "C" void bz_internal_error(int errcode) {
82 dcdebug("bzip2 internal error: %d\n", errcode);
83 }
84
85 #ifdef _WIN32
86
87 typedef HRESULT (WINAPI* _SHGetKnownFolderPath)(GUID& rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
88
getDownloadsPath(const string & def)89 static string getDownloadsPath(const string& def) {
90 // Try Vista downloads path
91 static _SHGetKnownFolderPath getKnownFolderPath = 0;
92 static HINSTANCE shell32 = NULL;
93
94 if(!shell32) {
95 shell32 = ::LoadLibrary(_T("Shell32.dll"));
96 if(shell32) {
97 getKnownFolderPath = (_SHGetKnownFolderPath)::GetProcAddress(shell32, "SHGetKnownFolderPath");
98
99 if(getKnownFolderPath) {
100 PWSTR path = NULL;
101 // Defined in KnownFolders.h.
102 static GUID downloads = {0x374de290, 0x123f, 0x4565, {0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b}};
103 if(getKnownFolderPath(downloads, 0, NULL, &path) == S_OK) {
104 string ret = Text::fromT((const tstring&)path) + "\\";
105 ::CoTaskMemFree(path);
106 return ret;
107 }
108 }
109 }
110 }
111
112 return def + "Downloads\\";
113 }
114
115 #endif
116
initialize(PathsMap pathOverrides)117 void Util::initialize(PathsMap pathOverrides) {
118 static bool initDone = false;
119 if (initDone)
120 return;
121
122 Text::initialize();
123
124 sgenrand((unsigned long)time(NULL));
125
126 // Override core generated paths
127 for (PathsMap::const_iterator it = pathOverrides.begin(); it != pathOverrides.end(); ++it)
128 {
129 if (!it->second.empty())
130 paths[it->first] = it->second;
131 }
132
133 #ifdef _WIN32
134 TCHAR buf[MAX_PATH+1] = { 0 };
135 ::GetModuleFileName(NULL, buf, MAX_PATH);
136
137 string exePath = Util::getFilePath(Text::fromT(buf));
138
139 // Global config path is DC++ executable path...
140 if (Util::getPath(Util::PATH_GLOBAL_CONFIG).empty())
141 paths[PATH_GLOBAL_CONFIG] = exePath;
142 if (Util::getPath(Util::PATH_USER_CONFIG).empty()) {
143 paths[PATH_USER_CONFIG] = paths[PATH_GLOBAL_CONFIG];
144
145 loadBootConfig();
146
147 if(!File::isAbsolute(paths[PATH_USER_CONFIG])) {
148 paths[PATH_USER_CONFIG] = paths[PATH_GLOBAL_CONFIG] + paths[PATH_USER_CONFIG];
149 }
150
151 paths[PATH_USER_CONFIG] = validateFileName(paths[PATH_USER_CONFIG]);
152 }
153 if (Util::getPath(Util::PATH_USER_LOCAL).empty()) {
154 if(localMode) {
155 paths[PATH_USER_LOCAL] = paths[PATH_USER_CONFIG];
156 } else {
157 if(::SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, buf) == S_OK) {
158 paths[PATH_USER_CONFIG] = Text::fromT(buf) + "\\EiskaltDC++\\";
159 }
160
161 paths[PATH_USER_LOCAL] = ::SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, buf) == S_OK ? Text::fromT(buf) + "\\EiskaltDC++\\" : paths[PATH_USER_CONFIG];
162 }
163 }
164 if (Util::getPath(Util::PATH_RESOURCES).empty())
165 paths[PATH_RESOURCES] = exePath;
166
167 // libintl doesn't support wide path names so we use the short (8.3) format.
168 // https://sourceforge.net/forum/message.php?msg_id=4882703
169 tstring localePath_ = Text::toT(exePath) + _T("locale\\");
170 memset(buf, 0, sizeof(buf));
171 ::GetShortPathName(localePath_.c_str(), buf, sizeof(buf)/sizeof(TCHAR));
172 if (Util::getPath(Util::PATH_LOCALE).empty())
173 paths[PATH_LOCALE] = Text::fromT(buf);
174 if (Util::getPath(Util::PATH_DOWNLOADS).empty())
175 paths[PATH_DOWNLOADS] = getDownloadsPath(paths[PATH_USER_CONFIG]);
176
177 #else
178 if (Util::getPath(Util::PATH_GLOBAL_CONFIG).empty())
179 paths[PATH_GLOBAL_CONFIG] = "/etc/";
180 const char* home_ = getenv("HOME");
181 string home = home_ ? Text::toUtf8(home_) : "/tmp/";
182
183 if (Util::getPath(Util::PATH_USER_CONFIG).empty()) {
184 #ifdef FORCE_XDG
185 const char *xdg_config_home_ = getenv("XDG_CONFIG_HOME");
186 string xdg_config_home = xdg_config_home_? Text::toUtf8(xdg_config_home_) : (home+"/.config");
187 paths[PATH_USER_CONFIG] = xdg_config_home + "/eiskaltdc++/";
188 #elif defined __HAIKU__
189 paths[PATH_USER_CONFIG] = home + "/config/settings/eiskaltdc++/";
190 #else
191 paths[PATH_USER_CONFIG] = home + "/.eiskaltdc++/";
192 #endif
193
194 loadBootConfig();
195
196 if(!File::isAbsolute(paths[PATH_USER_CONFIG])) {
197 paths[PATH_USER_CONFIG] = paths[PATH_GLOBAL_CONFIG] + paths[PATH_USER_CONFIG];
198 }
199
200 paths[PATH_USER_CONFIG] = validateFileName(paths[PATH_USER_CONFIG]);
201 }
202
203 if(localMode) {
204 // @todo implement...
205 }
206
207 if (Util::getPath(Util::PATH_USER_LOCAL).empty()) {
208 #ifdef FORCE_XDG
209 const char *xdg_data_home_ = getenv("XDG_DATA_HOME");
210 string xdg_data_home = xdg_data_home_? Text::toUtf8(xdg_data_home_) : (home+"/.local/share");
211 paths[PATH_USER_LOCAL] = xdg_data_home + "/eiskaltdc++/";
212 #elif defined __HAIKU__
213 paths[PATH_USER_LOCAL] = home + "/config/data/eiskaltdc++/";
214 #else
215 paths[PATH_USER_LOCAL] = paths[PATH_USER_CONFIG];
216 #endif
217 }
218
219 if (Util::getPath(Util::PATH_RESOURCES).empty())
220 paths[PATH_RESOURCES] = paths[PATH_USER_CONFIG];
221 if (Util::getPath(Util::PATH_LOCALE).empty())
222 paths[PATH_LOCALE] = LOCALEDIR;
223 if (Util::getPath(Util::PATH_DOWNLOADS).empty()) {
224 #ifdef FORCE_XDG
225 const char *xdg_config_down_ = getenv("XDG_DOWNLOAD_DIR");
226 string xdg_config_down = xdg_config_down_? (Text::toUtf8(xdg_config_down_)+"/") : (home+"/Downloads/");
227 paths[PATH_DOWNLOADS] = xdg_config_down;
228 #else
229 paths[PATH_DOWNLOADS] = home + "/Downloads/";
230 #endif
231 }
232 #endif
233 if (Util::getPath(Util::PATH_FILE_LISTS).empty())
234 paths[PATH_FILE_LISTS] = paths[PATH_USER_LOCAL] + "FileLists" PATH_SEPARATOR_STR;
235 if (Util::getPath(Util::PATH_HUB_LISTS).empty())
236 paths[PATH_HUB_LISTS] = paths[PATH_USER_LOCAL] + "HubLists" PATH_SEPARATOR_STR;
237 if (Util::getPath(Util::PATH_NOTEPAD).empty())
238 paths[PATH_NOTEPAD] = paths[PATH_USER_CONFIG] + "Notepad.txt";
239
240 File::ensureDirectory(paths[PATH_USER_CONFIG]);
241 File::ensureDirectory(paths[PATH_USER_LOCAL]);
242
243 try {
244 // This product includes GeoIP data created by MaxMind, available from http://maxmind.com/
245 // Updates at http://www.maxmind.com/app/geoip_country
246 #ifdef _WIN32
247 string file = getPath(PATH_RESOURCES) + "GeoIPCountryWhois.csv";
248 #else //_WIN32
249 string file_usr = getPath(PATH_RESOURCES) + "GeoIPCountryWhois.csv";
250 string file_sys = string(_DATADIR) + PATH_SEPARATOR + "GeoIPCountryWhois.csv";
251 string file = "";
252
253 struct stat stFileInfo;
254 if (stat(file_usr.c_str(),&stFileInfo) == 0)
255 file = file_usr;
256 else
257 file = file_sys;
258 #endif //_WIN32
259 string data = File(file, File::READ, File::OPEN).read();
260
261 const char* start = data.c_str();
262 string::size_type linestart = 0;
263 string::size_type comma1 = 0;
264 string::size_type comma2 = 0;
265 string::size_type comma3 = 0;
266 string::size_type comma4 = 0;
267 string::size_type lineend = 0;
268 CountryIter last = countries.end();
269 uint32_t startIP = 0;
270 uint32_t endIP = 0, endIPprev = 0;
271
272 for(;;) {
273 comma1 = data.find(',', linestart);
274 if(comma1 == string::npos) break;
275 comma2 = data.find(',', comma1 + 1);
276 if(comma2 == string::npos) break;
277 comma3 = data.find(',', comma2 + 1);
278 if(comma3 == string::npos) break;
279 comma4 = data.find(',', comma3 + 1);
280 if(comma4 == string::npos) break;
281 lineend = data.find('\n', comma4);
282 if(lineend == string::npos) break;
283
284 startIP = Util::toUInt32(start + comma2 + 2);
285 endIP = Util::toUInt32(start + comma3 + 2);
286 uint16_t* country = (uint16_t*)(start + comma4 + 2);
287 if((startIP-1) != endIPprev)
288 last = countries.insert(last, make_pair((startIP-1), (uint16_t)16191));
289 last = countries.insert(last, make_pair(endIP, *country));
290
291 endIPprev = endIP;
292 linestart = lineend + 1;
293 }
294 } catch(const FileException&) {
295 }
296 initDone = true;
297 }
298
migrate(const string & file)299 void Util::migrate(const string& file) {
300 if(localMode) {
301 return;
302 }
303
304 if(File::getSize(file) != -1) {
305 return;
306 }
307
308 string fname = getFileName(file);
309 string old = paths[PATH_GLOBAL_CONFIG] + fname;
310 if(File::getSize(old) == -1) {
311 return;
312 }
313
314 File::renameFile(old, file);
315 }
316
getLoginName()317 string Util::getLoginName() {
318 string loginName = "unknown";
319
320 #ifdef _WIN32
321 char winUserName[UNLEN + 1]; // UNLEN is defined in LMCONS.H
322 DWORD winUserNameSize = sizeof(winUserName);
323 if(GetUserNameA(winUserName, &winUserNameSize))
324 loginName = Text::toUtf8(winUserName);
325 #else // not _WIN32
326 const char *envUserName = getenv("LOGNAME");
327 loginName = envUserName? Text::toUtf8(envUserName) : loginName;
328 #endif // _WIN32
329
330 return loginName;
331 }
332
loadBootConfig()333 void Util::loadBootConfig() {
334 // Load boot settings
335 try {
336 SimpleXML boot;
337 boot.fromXML(File(getPath(PATH_GLOBAL_CONFIG) + "dcppboot.xml", File::READ, File::OPEN).read());
338 boot.stepIn();
339
340 if(boot.findChild("LocalMode")) {
341 localMode = boot.getChildData() != "0";
342 }
343
344 if(boot.findChild("ConfigPath")) {
345 StringMap params;
346 #ifdef _WIN32
347 // @todo load environment variables instead? would make it more useful on *nix
348 TCHAR path[MAX_PATH];
349
350 params["APPDATA"] = Text::fromT((::SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path), path));
351 params["PERSONAL"] = Text::fromT((::SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path), path));
352 #endif
353 paths[PATH_USER_CONFIG] = Util::formatParams(boot.getChildData(), params, false);
354 }
355 } catch(const Exception& ) {
356 // Unable to load boot settings...
357 }
358 }
359
360 #ifdef _WIN32
361 static const char badChars[] = {
362 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
363 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
364 31, '<', '>', '/', '"', '|', '?', '*', 0
365 };
366 #else
367
368 static const char badChars[] = {
369 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
370 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
371 31, '<', '>', '\\', '"', '|', '?', '*', 0
372 };
373 #endif
374
375 /**
376 * Replaces all strange characters in a file with '_'
377 * @todo Check for invalid names such as nul and aux...
378 */
validateFileName(string tmp,const string & badCharsExtra)379 string Util::validateFileName(string tmp, const string& badCharsExtra) {
380 string::size_type i = 0;
381
382 // First, eliminate forbidden chars
383 while( (i = tmp.find_first_of(badChars, i)) != string::npos) {
384 tmp[i] = '_';
385 i++;
386 }
387
388 i = 0;
389 if(!badCharsExtra.empty()) {
390 while( (i = tmp.find_first_of(badCharsExtra.c_str(), i)) != string::npos) {
391 tmp[i] = '_';
392 i++;
393 }
394 }
395
396 // Then, eliminate all ':' that are not the second letter ("c:\...")
397 i = 0;
398 while( (i = tmp.find(':', i)) != string::npos) {
399 if(i == 1) {
400 i++;
401 continue;
402 }
403 tmp[i] = '_';
404 i++;
405 }
406
407 // Remove the .\ that doesn't serve any purpose
408 i = 0;
409 while( (i = tmp.find("\\.\\", i)) != string::npos) {
410 tmp.erase(i+1, 2);
411 }
412 i = 0;
413 while( (i = tmp.find("/./", i)) != string::npos) {
414 tmp.erase(i+1, 2);
415 }
416
417 // Remove any double \\ that are not at the beginning of the path...
418 i = 1;
419 while( (i = tmp.find("\\\\", i)) != string::npos) {
420 tmp.erase(i+1, 1);
421 }
422 i = 1;
423 while( (i = tmp.find("//", i)) != string::npos) {
424 tmp.erase(i+1, 1);
425 }
426
427 // And last, but not least, the infamous ..\! ...
428 i = 0;
429 while( ((i = tmp.find("\\..\\", i)) != string::npos) ) {
430 tmp[i + 1] = '_';
431 tmp[i + 2] = '_';
432 tmp[i + 3] = '_';
433 i += 2;
434 }
435 i = 0;
436 while( ((i = tmp.find("/../", i)) != string::npos) ) {
437 tmp[i + 1] = '_';
438 tmp[i + 2] = '_';
439 tmp[i + 3] = '_';
440 i += 2;
441 }
442
443 // Dots at the end of path names aren't popular
444 i = 0;
445 while( ((i = tmp.find(".\\", i)) != string::npos) ) {
446 tmp[i] = '_';
447 i += 1;
448 }
449 i = 0;
450 while( ((i = tmp.find("./", i)) != string::npos) ) {
451 tmp[i] = '_';
452 i += 1;
453 }
454
455
456 return tmp;
457 }
458
checkExtension(const string & tmp)459 bool Util::checkExtension(const string& tmp) {
460 for(unsigned int i = 0; i < tmp.length(); i++) {
461 if (tmp[i] < 0 || tmp[i] == 32 || tmp[i] == ':') {
462 return false;
463 }
464 }
465 if(tmp.find_first_of(badChars, 0) != string::npos) {
466 return false;
467 }
468 return true;
469 }
470
cleanPathChars(string aNick)471 string Util::cleanPathChars(string aNick) {
472 string::size_type i = 0;
473
474 while( (i = aNick.find_first_of("/.\\", i)) != string::npos) {
475 aNick[i] = '_';
476 }
477 return aNick;
478 }
479
addBrackets(const string & s)480 string Util::addBrackets(const string& s) {
481 return '<' + s + '>';
482 }
483
getShortTimeString(time_t t)484 string Util::getShortTimeString(time_t t) {
485 char buf[255];
486 tm* _tm = localtime(&t);
487 if(_tm == NULL) {
488 strcpy(buf, "xx:xx");
489 } else {
490 strftime(buf, 254, SETTING(TIME_STAMPS_FORMAT).c_str(), _tm);
491 }
492 return Text::toUtf8(buf);
493 }
494
495 /**
496 * Decodes a URL the best it can...
497 * Default ports:
498 * http:// -> port 80
499 * dchub:// -> port 411
500 */
decodeUrl(const string & url,string & protocol,string & host,uint16_t & port,string & path,string & query,string & fragment)501 void Util::decodeUrl(const string& url, string& protocol, string& host, uint16_t& port, string& path, string& query, string& fragment) {
502 size_t fragmentEnd = url.size();
503 size_t fragmentStart = url.rfind('#');
504
505 size_t queryEnd;
506 if(fragmentStart == string::npos) {
507 queryEnd = fragmentStart = fragmentEnd;
508 } else {
509 dcdebug("f");
510 queryEnd = fragmentStart;
511 fragmentStart++;
512 }
513
514 //size_t queryStart = url.rfind('?', queryEnd);
515 //size_t fileEnd;
516 //if(queryStart == string::npos) {
517 //fileEnd = queryStart = queryEnd;
518 //} else {
519 //dcdebug("q");
520 //fileEnd = queryStart;
521 //queryStart++;
522 //}
523 size_t queryStart = queryEnd;
524 size_t fileEnd = queryStart;
525
526 size_t protoStart = 0;
527 size_t protoEnd = url.find("://", protoStart);
528
529 size_t authorityStart = protoEnd == string::npos ? protoStart : protoEnd + 3;
530 size_t authorityEnd = url.find_first_of("/#?", authorityStart);
531
532 size_t fileStart;
533 if(authorityEnd == string::npos) {
534 authorityEnd = fileStart = fileEnd;
535 } else {
536 dcdebug("a");
537 fileStart = authorityEnd;
538 }
539
540 protocol = url.substr(protoStart, protoEnd - protoStart);
541
542 if(authorityEnd > authorityStart) {
543 dcdebug("x");
544 size_t portStart = string::npos;
545 if(url[authorityStart] == '[') {
546 // IPv6?
547 size_t hostEnd = url.find(']');
548 if(hostEnd == string::npos) {
549 return;
550 }
551
552 host = url.substr(authorityStart, hostEnd - authorityStart);
553 if(hostEnd + 1 < url.size() && url[hostEnd + 1] == ':') {
554 portStart = hostEnd + 1;
555 }
556 } else {
557 size_t hostEnd;
558 portStart = url.find(':', authorityStart);
559 if(portStart != string::npos && portStart > authorityEnd) {
560 portStart = string::npos;
561 }
562
563 if(portStart == string::npos) {
564 hostEnd = authorityEnd;
565 } else {
566 hostEnd = portStart;
567 portStart++;
568 }
569
570 dcdebug("h");
571 host = url.substr(authorityStart, hostEnd - authorityStart);
572 }
573
574 if(portStart == string::npos) {
575 if(protocol == "http") {
576 port = 80;
577 } else if(protocol == "https") {
578 port = 443;
579 } else if(protocol == "dchub") {
580 port = 411;
581 } else {
582 port = 411;
583 }
584 } else {
585 dcdebug("p");
586 port = static_cast<uint16_t>(Util::toInt(url.substr(portStart, authorityEnd - portStart)));
587 }
588 }
589
590 dcdebug("\n");
591 path = url.substr(fileStart, fileEnd - fileStart);
592 query = url.substr(queryStart, queryEnd - queryStart);
593 fragment = url.substr(fragmentStart, fragmentEnd - fragmentStart);
594
595 #ifdef USE_IDNA
596 //printf("%s\n",host.c_str());
597 char *p;
598 if (idna_to_ascii_8z(host.c_str(), &p, 0) == IDNA_SUCCESS) {
599 host = string(p);
600 }
601 free(p);
602 //printf ("ACE label (length %d): '%s'\n", strlen (p), p);
603 //printf ("%s\n", host.c_str());
604 #endif
605 //printf("protocol:%s\n host:%s\n port:%d\n path:%s\n query:%s\n fragment:%s\n", protocol.c_str(), host.c_str(), port, path.c_str(), query.c_str(), fragment.c_str());
606 }
607
parseIpPort(const string & aIpPort,string & ip,uint16_t & port)608 void Util::parseIpPort(const string& aIpPort, string& ip, uint16_t& port) {
609 string::size_type i = aIpPort.rfind(':');
610 if (i == string::npos) {
611 ip = aIpPort;
612 } else {
613 ip = aIpPort.substr(0, i);
614 port = Util::toInt(aIpPort.substr(i + 1));
615 }
616 }
617
decodeQuery(const string & query)618 map<string, string> Util::decodeQuery(const string& query) {
619 map<string, string> ret;
620 size_t start = 0;
621 while(start < query.size()) {
622 size_t eq = query.find('=', start);
623 if(eq == string::npos) {
624 break;
625 }
626
627 size_t param = eq + 1;
628 size_t end = query.find('&', param);
629
630 if(end == string::npos) {
631 end = query.size();
632 }
633
634 if(eq > start && end > param) {
635 ret[query.substr(start, eq-start)] = query.substr(param, end - param);
636 }
637
638 start = end + 1;
639 }
640
641 return ret;
642 }
643
getAwayMessage()644 string Util::getAwayMessage() {
645 return (formatTime(awayMsg.empty() ? SETTING(DEFAULT_AWAY_MESSAGE) : awayMsg, awayTime)) + " <" APPNAME " v" VERSIONSTRING ">";
646 }
647
formatBytes(int64_t aBytes,uint8_t base)648 string Util::formatBytes(int64_t aBytes, uint8_t base) {
649 uint16_t a = (base < 2? 1024 : 1000);
650 float b = a*1.0;
651
652 char buf[128];
653 if(aBytes < a) {
654 snprintf(buf, sizeof(buf), _("%d B"), (int)(aBytes&0xffffffff));
655 } else if(aBytes < a*a) {
656 snprintf(buf, sizeof(buf), (base == 0 ? _("%.02f KiB") : _("%.02f KB")), (double)aBytes/(b));
657 } else if(aBytes < a*a*a) {
658 snprintf(buf, sizeof(buf), (base == 0 ? _("%.02f MiB") : _("%.02f MB")), (double)aBytes/(b*b));
659 } else if(aBytes < (int64_t)a*a*a*a) {
660 snprintf(buf, sizeof(buf), (base == 0 ? _("%.02f GiB") : _("%.02f GB")), (double)aBytes/(b*b*b));
661 } else if(aBytes < (int64_t)a*a*a*a*a) {
662 snprintf(buf, sizeof(buf), (base == 0 ? _("%.02f TiB") : _("%.02f TB")), (double)aBytes/(b*b*b*b));
663 } else {
664 snprintf(buf, sizeof(buf), (base == 0 ? _("%.02f PiB") : _("%.02f PB")), (double)aBytes/(b*b*b*b*b));
665 }
666
667 return buf;
668 }
669
formatBytes(int64_t aBytes)670 string Util::formatBytes(int64_t aBytes) {
671 return formatBytes(aBytes, SETTING(APP_UNIT_BASE));
672 }
673
formatExactSize(int64_t aBytes)674 string Util::formatExactSize(int64_t aBytes) {
675 #ifdef _WIN32
676 TCHAR tbuf[128];
677 TCHAR number[64];
678 NUMBERFMT nf;
679 _sntprintf(number, 64, _T("%I64d"), aBytes);
680 TCHAR Dummy[16];
681 TCHAR sep[2] = _T(",");
682
683 /*No need to read these values from the system because they are not
684 used to format the exact size*/
685 nf.NumDigits = 0;
686 nf.LeadingZero = 0;
687 nf.NegativeOrder = 0;
688 nf.lpDecimalSep = sep;
689
690 GetLocaleInfo( LOCALE_SYSTEM_DEFAULT, LOCALE_SGROUPING, Dummy, 16 );
691 nf.Grouping = Util::toInt(Text::fromT(Dummy));
692 GetLocaleInfo( LOCALE_SYSTEM_DEFAULT, LOCALE_STHOUSAND, Dummy, 16 );
693 nf.lpThousandSep = Dummy;
694
695 GetNumberFormat(LOCALE_USER_DEFAULT, 0, number, &nf, tbuf, sizeof(tbuf)/sizeof(tbuf[0]));
696
697 char buf[128];
698 _snprintf(buf, sizeof(buf), _("%s B"), Text::fromT(tbuf).c_str());
699 return buf;
700 #else
701 char buf[128];
702 snprintf(buf, sizeof(buf), _("%'lld B"), (long long int)aBytes);
703 return string(buf);
704 #endif
705 }
706
getLocalIPs(unsigned short sa_family)707 vector<string> Util::getLocalIPs(unsigned short sa_family) {
708 vector<string> addresses;
709
710 #ifdef HAVE_IFADDRS_H
711 struct ifaddrs *ifap;
712
713 if (getifaddrs(&ifap) == 0)
714 {
715 bool ipv4 = (sa_family == AF_UNSPEC) || (sa_family == AF_INET);
716 bool ipv6 = (sa_family == AF_UNSPEC) || (sa_family == AF_INET6);
717
718 for (struct ifaddrs *i = ifap; i != NULL; i = i->ifa_next)
719 {
720 struct sockaddr *sa = i->ifa_addr;
721
722 // If the interface is up, is not a loopback and it has an address
723 if ((i->ifa_flags & IFF_UP) && !(i->ifa_flags & IFF_LOOPBACK) && sa != NULL)
724 {
725 void* src = NULL;
726 socklen_t len;
727
728 // IPv4 address
729 if (ipv4 && (sa->sa_family == AF_INET))
730 {
731 struct sockaddr_in* sai = (struct sockaddr_in*)sa;
732 src = (void*) &(sai->sin_addr);
733 len = INET_ADDRSTRLEN;
734 }
735 // IPv6 address
736 else if (ipv6 && (sa->sa_family == AF_INET6))
737 {
738 struct sockaddr_in6* sai6 = (struct sockaddr_in6*)sa;
739 src = (void*) &(sai6->sin6_addr);
740 len = INET6_ADDRSTRLEN;
741 }
742
743 // Convert the binary address to a string and add it to the output list
744 if (src != NULL)
745 {
746 char address[len];
747 inet_ntop(sa->sa_family, src, address, len);
748 addresses.push_back(address);
749 }
750 }
751 }
752 freeifaddrs(ifap);
753 }
754 #endif
755
756 return addresses;
757 }
getLocalIp(unsigned short as_family)758 string Util::getLocalIp(unsigned short as_family) {
759 #ifdef HAVE_IFADDRS_H
760 vector<string> addresses = getLocalIPs(as_family);
761 if (addresses.empty())
762 return (((as_family == AF_UNSPEC) || (as_family == AF_INET)) ? "0.0.0.0" : "::");
763
764 return addresses[0];
765 #else
766 string tmp;
767
768 char buf[256];
769 gethostname(buf, 255);
770 hostent* he = gethostbyname(buf);
771 if(he == NULL || he->h_addr_list[0] == 0)
772 return Util::emptyString;
773 sockaddr_in dest;
774 int i = 0;
775
776 // We take the first ip as default, but if we can find a better one, use it instead...
777 memcpy(&(dest.sin_addr), he->h_addr_list[i++], he->h_length);
778 tmp = inet_ntoa(dest.sin_addr);
779 if(Util::isPrivateIp(tmp) || ::strncmp(tmp.c_str(), "169", 3) == 0) {
780 while(he->h_addr_list[i]) {
781 memcpy(&(dest.sin_addr), he->h_addr_list[i], he->h_length);
782 string tmp2 = inet_ntoa(dest.sin_addr);
783 if(!Util::isPrivateIp(tmp2) && ::strncmp(tmp2.c_str(), "169", 3) != 0) {
784 tmp = tmp2;
785 }
786 i++;
787 }
788 }
789 return tmp;
790 #endif
791 }
792
isPrivateIp(string const & ip)793 bool Util::isPrivateIp(string const& ip) {
794 struct in_addr addr;
795
796 addr.s_addr = inet_addr(ip.c_str());
797
798 if (addr.s_addr != INADDR_NONE) {
799 unsigned long haddr = ntohl(addr.s_addr);
800 return ((haddr & 0xff000000) == 0x0a000000 || // 10.0.0.0/8
801 (haddr & 0xff000000) == 0x7f000000 || // 127.0.0.0/8
802 (haddr & 0xfff00000) == 0xac100000 || // 172.16.0.0/12
803 (haddr & 0xffff0000) == 0xc0a80000); // 192.168.0.0/16
804 }
805 return false;
806 }
807
808 typedef const uint8_t* ccp;
utf8ToLC(ccp & str)809 static wchar_t utf8ToLC(ccp& str) {
810 wchar_t c = 0;
811 if(str[0] & 0x80) {
812 if(str[0] & 0x40) {
813 if(str[0] & 0x20) {
814 if(str[1] == 0 || str[2] == 0 ||
815 !((((unsigned char)str[1]) & ~0x3f) == 0x80) ||
816 !((((unsigned char)str[2]) & ~0x3f) == 0x80))
817 {
818 str++;
819 return 0;
820 }
821 c = ((wchar_t)(unsigned char)str[0] & 0xf) << 12 |
822 ((wchar_t)(unsigned char)str[1] & 0x3f) << 6 |
823 ((wchar_t)(unsigned char)str[2] & 0x3f);
824 str += 3;
825 } else {
826 if(str[1] == 0 ||
827 !((((unsigned char)str[1]) & ~0x3f) == 0x80))
828 {
829 str++;
830 return 0;
831 }
832 c = ((wchar_t)(unsigned char)str[0] & 0x1f) << 6 |
833 ((wchar_t)(unsigned char)str[1] & 0x3f);
834 str += 2;
835 }
836 } else {
837 str++;
838 return 0;
839 }
840 } else {
841 wchar_t c = Text::asciiToLower((char)str[0]);
842 str++;
843 return c;
844 }
845
846 return Text::toLower(c);
847 }
848
toString(const string & sep,const StringList & lst)849 string Util::toString(const string& sep, const StringList& lst) {
850 string ret;
851 for(StringList::const_iterator i = lst.begin(), iend = lst.end(); i != iend; ++i) {
852 ret += *i;
853 if(i + 1 != iend)
854 ret += sep;
855 }
856 return ret;
857 }
858
toString(const StringList & lst)859 string Util::toString(const StringList& lst) {
860 if(lst.empty())
861 return emptyString;
862 if(lst.size() == 1)
863 return lst[0];
864 return '[' + toString(",", lst) + ']';
865 }
866
findSubString(const string & aString,const string & aSubString,string::size_type start)867 string::size_type Util::findSubString(const string& aString, const string& aSubString, string::size_type start) noexcept {
868 if(aString.length() < start)
869 return (string::size_type)string::npos;
870
871 if(aString.length() - start < aSubString.length())
872 return (string::size_type)string::npos;
873
874 if(aSubString.empty())
875 return 0;
876
877 // Hm, should start measure in characters or in bytes? bytes for now...
878 const uint8_t* tx = (const uint8_t*)aString.c_str() + start;
879 const uint8_t* px = (const uint8_t*)aSubString.c_str();
880
881 const uint8_t* end = tx + aString.length() - start - aSubString.length() + 1;
882
883 wchar_t wp = utf8ToLC(px);
884
885 while(tx < end) {
886 const uint8_t* otx = tx;
887 if(wp == utf8ToLC(tx)) {
888 const uint8_t* px2 = px;
889 const uint8_t* tx2 = tx;
890
891 for(;;) {
892 if(*px2 == 0)
893 return otx - (uint8_t*)aString.c_str();
894
895 if(utf8ToLC(px2) != utf8ToLC(tx2))
896 break;
897 }
898 }
899 }
900 return (string::size_type)string::npos;
901 }
902
findSubString(const wstring & aString,const wstring & aSubString,wstring::size_type pos)903 wstring::size_type Util::findSubString(const wstring& aString, const wstring& aSubString, wstring::size_type pos) noexcept {
904 if(aString.length() < pos)
905 return static_cast<wstring::size_type>(wstring::npos);
906
907 if(aString.length() - pos < aSubString.length())
908 return static_cast<wstring::size_type>(wstring::npos);
909
910 if(aSubString.empty())
911 return 0;
912
913 wstring::size_type j = 0;
914 wstring::size_type end = aString.length() - aSubString.length() + 1;
915
916 for(; pos < end; ++pos) {
917 if(Text::toLower(aString[pos]) == Text::toLower(aSubString[j])) {
918 wstring::size_type tmp = pos+1;
919 bool found = true;
920 for(++j; j < aSubString.length(); ++j, ++tmp) {
921 if(Text::toLower(aString[tmp]) != Text::toLower(aSubString[j])) {
922 j = 0;
923 found = false;
924 break;
925 }
926 }
927
928 if(found)
929 return pos;
930 }
931 }
932 return static_cast<wstring::size_type>(wstring::npos);
933 }
934
stricmp(const char * a,const char * b)935 int Util::stricmp(const char* a, const char* b) {
936 while(*a) {
937 wchar_t ca = 0, cb = 0;
938 int na = Text::utf8ToWc(a, ca);
939 int nb = Text::utf8ToWc(b, cb);
940 ca = Text::toLower(ca);
941 cb = Text::toLower(cb);
942 if(ca != cb) {
943 return (int)ca - (int)cb;
944 }
945 a += abs(na);
946 b += abs(nb);
947 }
948 wchar_t ca = 0, cb = 0;
949 Text::utf8ToWc(a, ca);
950 Text::utf8ToWc(b, cb);
951
952 return (int)Text::toLower(ca) - (int)Text::toLower(cb);
953 }
954
strnicmp(const char * a,const char * b,size_t n)955 int Util::strnicmp(const char* a, const char* b, size_t n) {
956 const char* end = a + n;
957 while(*a && a < end) {
958 wchar_t ca = 0, cb = 0;
959 int na = Text::utf8ToWc(a, ca);
960 int nb = Text::utf8ToWc(b, cb);
961 ca = Text::toLower(ca);
962 cb = Text::toLower(cb);
963 if(ca != cb) {
964 return (int)ca - (int)cb;
965 }
966 a += abs(na);
967 b += abs(nb);
968 }
969 wchar_t ca = 0, cb = 0;
970 Text::utf8ToWc(a, ca);
971 Text::utf8ToWc(b, cb);
972 return (a >= end) ? 0 : ((int)Text::toLower(ca) - (int)Text::toLower(cb));
973 }
974
encodeURI(const string & aString,bool reverse)975 string Util::encodeURI(const string& aString, bool reverse) {
976 // reference: rfc2396
977 string tmp = aString;
978 if(reverse) {
979 string::size_type idx;
980 for(idx = 0; idx < tmp.length(); ++idx) {
981 if(tmp.length() > idx + 2 && tmp[idx] == '%' && isxdigit(tmp[idx+1]) && isxdigit(tmp[idx+2])) {
982 tmp[idx] = fromHexEscape(tmp.substr(idx+1,2));
983 tmp.erase(idx+1, 2);
984 } else { // reference: rfc1630, magnet-uri draft
985 if(tmp[idx] == '+')
986 tmp[idx] = ' ';
987 }
988 }
989 } else {
990 const string disallowed = ";/?:@&=+$," // reserved
991 "<>#%\" " // delimiters
992 "{}|\\^[]`"; // unwise
993 string::size_type idx, loc;
994 for(idx = 0; idx < tmp.length(); ++idx) {
995 if(tmp[idx] == ' ') {
996 tmp[idx] = '+';
997 } else {
998 if(tmp[idx] <= 0x1F || tmp[idx] >= 0x7f || (loc = disallowed.find_first_of(tmp[idx])) != string::npos) {
999 tmp.replace(idx, 1, toHexEscape(tmp[idx]));
1000 idx+=2;
1001 }
1002 }
1003 }
1004 }
1005 return tmp;
1006 }
1007
1008 /**
1009 * This function takes a string and a set of parameters and transforms them according to
1010 * a simple formatting rule, similar to strftime. In the message, every parameter should be
1011 * represented by %[name]. It will then be replaced by the corresponding item in
1012 * the params stringmap. After that, the string is passed through strftime with the current
1013 * date/time and then finally written to the log file. If the parameter is not present at all,
1014 * it is removed from the string completely...
1015 */
formatParams(const string & msg,const StringMap & params,bool filter)1016 string Util::formatParams(const string& msg, const StringMap& params, bool filter) {
1017 string result = msg;
1018
1019 string::size_type i, j, k;
1020 i = 0;
1021 while (( j = result.find("%[", i)) != string::npos) {
1022 if( (result.size() < j + 2) || ((k = result.find(']', j + 2)) == string::npos) ) {
1023 break;
1024 }
1025 string name = result.substr(j + 2, k - j - 2);
1026 StringMap::const_iterator smi = params.find(name);
1027 if(smi == params.end()) {
1028 result.erase(j, k-j + 1);
1029 i = j;
1030 } else {
1031 if(smi->second.find_first_of("%\\./") != string::npos) {
1032 string tmp = smi->second; // replace all % in params with %% for strftime
1033 string::size_type m = 0;
1034 while(( m = tmp.find('%', m)) != string::npos) {
1035 tmp.replace(m, 1, "%%");
1036 m+=2;
1037 }
1038 if(filter) {
1039 // Filter chars that produce bad effects on file systems
1040 m = 0;
1041 while(( m = tmp.find_first_of("\\./", m)) != string::npos) {
1042 tmp[m] = '_';
1043 }
1044 }
1045
1046 result.replace(j, k-j + 1, tmp);
1047 i = j + tmp.size();
1048 } else {
1049 result.replace(j, k-j + 1, smi->second);
1050 i = j + smi->second.size();
1051 }
1052 }
1053 }
1054
1055 result = formatTime(result, time(NULL));
1056
1057 return result;
1058 }
1059
formatTime(const string & msg,const time_t t)1060 string Util::formatTime(const string &msg, const time_t t) {
1061 if (!msg.empty()) {
1062 tm* loc = localtime(&t);
1063
1064 if(!loc) {
1065 return Util::emptyString;
1066 }
1067 size_t bufsize = msg.size() + 256;
1068 string buf(bufsize, 0);
1069
1070 errno = 0;
1071
1072 buf.resize(strftime(&buf[0], bufsize-1, msg.c_str(), loc));
1073
1074 while(buf.empty()) {
1075 if(errno == EINVAL)
1076 return Util::emptyString;
1077 bufsize+=64;
1078 buf.resize(bufsize);
1079 buf.resize(strftime(&buf[0], bufsize-1, msg.c_str(), loc));
1080 }
1081
1082 #ifdef _WIN32
1083 if(!Text::validateUtf8(buf))
1084 #endif
1085 {
1086 buf = Text::toUtf8(buf);
1087 }
1088 return buf;
1089 }
1090 return Util::emptyString;
1091 }
1092
1093 /* Below is a high-speed random number generator with much
1094 better granularity than the CRT one in msvc...(no, I didn't
1095 write it...see copyright) */
1096 /* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura.
1097 Any feedback is very welcome. For any question, comments,
1098 see http://www.math.keio.ac.jp/matumoto/emt.html or email
1099 matumoto@math.keio.ac.jp */
1100 /* Period parameters */
1101 #define N 624
1102 #define M 397
1103 #define MATRIX_A 0x9908b0df /* constant vector a */
1104 #define UPPER_MASK 0x80000000 /* most significant w-r bits */
1105 #define LOWER_MASK 0x7fffffff /* least significant r bits */
1106
1107 /* Tempering parameters */
1108 #define TEMPERING_MASK_B 0x9d2c5680
1109 #define TEMPERING_MASK_C 0xefc60000
1110 #define TEMPERING_SHIFT_U(y) (y >> 11)
1111 #define TEMPERING_SHIFT_S(y) (y << 7)
1112 #define TEMPERING_SHIFT_T(y) (y << 15)
1113 #define TEMPERING_SHIFT_L(y) (y >> 18)
1114
1115 static unsigned long mt[N]; /* the array for the state vector */
1116 static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
1117
1118 /* initializing the array with a NONZERO seed */
sgenrand(unsigned long seed)1119 static void sgenrand(unsigned long seed) {
1120 /* setting initial seeds to mt[N] using */
1121 /* the generator Line 25 of Table 1 in */
1122 /* [KNUTH 1981, The Art of Computer Programming */
1123 /* Vol. 2 (2nd Ed.), pp102] */
1124 mt[0]= seed & 0xffffffff;
1125 for (mti=1; mti<N; mti++)
1126 mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;
1127 }
1128
rand()1129 uint32_t Util::rand() {
1130 unsigned long y;
1131 static unsigned long mag01[2]={0x0, MATRIX_A};
1132 /* mag01[x] = x * MATRIX_A for x=0,1 */
1133
1134 if (mti >= N) { /* generate N words at one time */
1135 int kk;
1136
1137 if (mti == N+1) /* if sgenrand() has not been called, */
1138 sgenrand(4357); /* a default initial seed is used */
1139
1140 for (kk=0;kk<N-M;kk++) {
1141 y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
1142 mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
1143 }
1144 for (;kk<N-1;kk++) {
1145 y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
1146 mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
1147 }
1148 y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
1149 mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
1150
1151 mti = 0;
1152 }
1153
1154 y = mt[mti++];
1155 y ^= TEMPERING_SHIFT_U(y);
1156 y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
1157 y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
1158 y ^= TEMPERING_SHIFT_L(y);
1159
1160 return y;
1161 }
1162
1163 /* getIpCountry
1164 This function returns the country(Abbreviation) of an ip
1165 for exemple: it returns "PT", whitch standards for "Portugal"
1166 more info: http://www.maxmind.com/app/csv
1167 */
getIpCountry(string IP)1168 string Util::getIpCountry (string IP) {
1169 if (BOOLSETTING(GET_USER_COUNTRY)) {
1170 dcassert(count(IP.begin(), IP.end(), '.') == 3);
1171
1172 //e.g IP 23.24.25.26 : w=23, x=24, y=25, z=26
1173 string::size_type a = IP.find('.');
1174 string::size_type b = IP.find('.', a+1);
1175 string::size_type c = IP.find('.', b+2);
1176
1177 uint32_t ipnum = (Util::toUInt32(IP.c_str()) << 24) |
1178 (Util::toUInt32(IP.c_str() + a + 1) << 16) |
1179 (Util::toUInt32(IP.c_str() + b + 1) << 8) |
1180 (Util::toUInt32(IP.c_str() + c + 1) );
1181
1182 CountryIter i = countries.lower_bound(ipnum);
1183
1184 if(i != countries.end()) {
1185 return string((char*)&(i->second), 2);
1186 }
1187 }
1188
1189 return Util::emptyString; //if doesn't returned anything already, something is wrong...
1190 }
1191
getTimeString()1192 string Util::getTimeString() {
1193 char buf[64];
1194 time_t _tt;
1195 time(&_tt);
1196 tm* _tm = localtime(&_tt);
1197 if(_tm == NULL) {
1198 strcpy(buf, "xx:xx:xx");
1199 } else {
1200 strftime(buf, 64, "%X", _tm);
1201 }
1202 return buf;
1203 }
1204
toAdcFile(const string & file)1205 string Util::toAdcFile(const string& file) {
1206 if(file == "files.xml.bz2" || file == "files.xml")
1207 return file;
1208
1209 string ret;
1210 ret.reserve(file.length() + 1);
1211 ret += '/';
1212 ret += file;
1213 for(string::size_type i = 0; i < ret.length(); ++i) {
1214 if(ret[i] == '\\') {
1215 ret[i] = '/';
1216 }
1217 }
1218 return ret;
1219 }
1220
toNmdcFile(const string & file)1221 string Util::toNmdcFile(const string& file) {
1222 if(file.empty())
1223 return Util::emptyString;
1224
1225 string ret(file.substr(1));
1226 for(string::size_type i = 0; i < ret.length(); ++i) {
1227 if(ret[i] == '/') {
1228 ret[i] = '\\';
1229 }
1230 }
1231 return ret;
1232 }
1233
translateError(int aError)1234 string Util::translateError(int aError) {
1235 #ifdef _WIN32
1236 LPTSTR lpMsgBuf;
1237 DWORD chars = FormatMessage(
1238 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1239 FORMAT_MESSAGE_FROM_SYSTEM |
1240 FORMAT_MESSAGE_IGNORE_INSERTS,
1241 NULL,
1242 aError,
1243 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1244 (LPTSTR) &lpMsgBuf,
1245 0,
1246 NULL
1247 );
1248 if(chars == 0) {
1249 return string();
1250 }
1251 string tmp = Text::fromT(lpMsgBuf);
1252 // Free the buffer.
1253 LocalFree( lpMsgBuf );
1254 string::size_type i = 0;
1255
1256 while( (i = tmp.find_first_of("\r\n", i)) != string::npos) {
1257 tmp.erase(i, 1);
1258 }
1259 return tmp;
1260 #else // _WIN32
1261 return Text::toUtf8(strerror(aError));
1262 #endif // _WIN32
1263 }
1264
getAway()1265 bool Util::getAway() {
1266 return away;
1267 }
1268
setAway(bool aAway)1269 void Util::setAway(bool aAway) {
1270 bool changed = aAway != away;
1271 away = aAway;
1272 if(away)
1273 awayTime = time(NULL);
1274
1275 if(changed)
1276 ClientManager::getInstance()->infoUpdated();
1277 }
1278
switchAway()1279 void Util::switchAway() {
1280 setAway(!away);
1281 }
1282
formatAdditionalInfo(const string & aIp,bool sIp,bool sCC)1283 string Util::formatAdditionalInfo(const string& aIp, bool sIp, bool sCC) {
1284 string ret = Util::emptyString;
1285
1286 if(!aIp.empty()) {
1287 string cc = Util::getIpCountry(aIp);
1288 bool showIp = BOOLSETTING(USE_IP) || sIp;
1289 bool showCc = (BOOLSETTING(GET_USER_COUNTRY) || sCC) && !cc.empty();
1290
1291 if(showIp) {
1292 int ll = 15 - aIp.size();
1293 if (ll >0) {
1294 string tmp = " "; size_t sz=tmp.size();
1295 tmp.resize(sz+ll-1,' ');
1296 ret = "[" + tmp + aIp + "] ";
1297 } else
1298 ret = "[" + aIp + "] ";
1299 }
1300 //printf("%s\n",ret.c_str());
1301 if(showCc) {
1302 ret += "[" + cc + "] ";
1303 //printf("%s\n",ret.c_str());
1304 }
1305 //printf("%s\n",ret.c_str());
1306 }
1307 return Text::toT(ret);
1308 }
1309
fileExists(const string & aFile)1310 bool Util::fileExists(const string &aFile) {
1311 #ifdef _WIN32
1312 DWORD attr = GetFileAttributes(Text::toT(aFile).c_str());
1313 return (attr != 0xFFFFFFFF);
1314 #else
1315 struct stat stFileInfo;
1316 return (stat(aFile.c_str(),&stFileInfo) == 0);
1317 #endif
1318 }
1319
1320 } // namespace dcpp
1321