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