📄 util.cpp
字号:
/*
* Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "stdinc.h"
#include "DCPlusPlus.h"
#include "Util.h"
#include "File.h"
#include "SettingsManager.h"
#include "ResourceManager.h"
#include "StringTokenizer.h"
#include "SettingsManager.h"
#include "version.h"
#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/utsname.h>
#include <ctype.h>
#endif
#include <locale.h>
#include "CID.h"
#include "FastAlloc.h"
#ifndef _DEBUG
FastCriticalSection FastAllocBase::cs;
#endif
string Util::emptyString;
wstring Util::emptyStringW;
tstring Util::emptyStringT;
bool Util::away = false;
string Util::awayMsg;
time_t Util::awayTime;
Util::CountryList Util::countries;
string Util::appPath;
static void sgenrand(unsigned long seed);
void Util::initialize() {
setlocale(LC_ALL, "");
Text::initialize();
sgenrand((unsigned long)time(NULL));
#ifdef _WIN32
TCHAR buf[MAX_PATH+1];
GetModuleFileName(NULL, buf, MAX_PATH);
appPath = Text::fromT(buf);
appPath.erase(appPath.rfind('\\') + 1);
#else // _WIN32
char* home = getenv("HOME");
if (home) {
appPath = Text::fromT(home);
appPath += "/.dc++/";
}
#endif // _WIN32
try {
// This product includes GeoIP data created by MaxMind, available from http://maxmind.com/
// Updates at http://www.maxmind.com/app/geoip_country
string file = Util::getAppPath() + "GeoIpCountryWhois.csv";
string data = File(file, File::READ, File::OPEN).read();
const char* start = data.c_str();
string::size_type i = 0;
string::size_type j = 0;
string::size_type k = 0;
CountryIter last = countries.end();
for(;;) {
i = data.find(',', k);
if(i == string::npos)
break;
j = data.find('\n', i);
if(j == string::npos)
break;
u_int16_t* country = (u_int16_t*)(start + i + 1);
last = countries.insert(last, make_pair(Util::toUInt32(start + k), *country));
k = j + 1;
}
} catch(const FileException&) {
}
}
string Util::getConfigPath() {
#ifdef _WIN32
return getAppPath();
#else
char* home = getenv("HOME");
if (home) {
#ifdef __APPLE__
return string(home) + "/Library/Application Support/Mac DC++/";
#else
return string(home) + "/.dc++/";
#endif // __APPLE__
}
return emptyString;
#endif // _WIN32
}
string Util::validateMessage(string tmp, bool reverse, bool checkNewLines) {
string::size_type i = 0;
if(reverse) {
while( (i = tmp.find("$", i)) != string::npos) {
tmp.replace(i, 5, "$");
i++;
}
i = 0;
while( (i = tmp.find("|", i)) != string::npos) {
tmp.replace(i, 6, "|");
i++;
}
i = 0;
while( (i = tmp.find("&", i)) != string::npos) {
tmp.replace(i, 5, "&");
i++;
}
if(checkNewLines) {
// Check all '<' and '|' after newlines...
i = 0;
while( (i = tmp.find('\n', i)) != string::npos) {
if(i + 1 < tmp.length()) {
if(tmp[i+1] == '[' || tmp[i+1] == '<') {
tmp.insert(i+1, "- ");
i += 2;
}
}
i++;
}
}
} else {
i = 0;
while( (i = tmp.find("&", i)) != string::npos) {
tmp.replace(i, 1, "&");
i += 4;
}
i = 0;
while( (i = tmp.find("$", i)) != string::npos) {
tmp.replace(i, 1, "&");
i += 4;
}
i = 0;
while( (i = tmp.find("|", i)) != string::npos) {
tmp.replace(i, 1, "&");
i += 4;
}
i = 0;
while( (i = tmp.find('$', i)) != string::npos) {
tmp.replace(i, 1, "$");
i += 4;
}
i = 0;
while( (i = tmp.find('|', i)) != string::npos) {
tmp.replace(i, 1, "|");
i += 5;
}
}
return tmp;
}
#ifdef _WIN32
static const char badChars[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, '<', '>', '/', '"', '|', '?', '*', 0
};
#else
static const char badChars[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, '<', '>', '\\', '"', '|', '?', '*', 0
};
#endif
/**
* Replaces all strange characters in a file with '_'
* @todo Check for invalid names such as nul and aux...
*/
string Util::validateFileName(string tmp) {
string::size_type i = 0;
// First, eliminate forbidden chars
while( (i = tmp.find_first_of(badChars, i)) != string::npos) {
tmp[i] = '_';
i++;
}
// Then, eliminate all ':' that are not the second letter ("c:\...")
i = 0;
while( (i = tmp.find(':', i)) != string::npos) {
if(i == 1) {
i++;
continue;
}
tmp[i] = '_';
i++;
}
// Remove the .\ that doesn't serve any purpose
i = 0;
while( (i = tmp.find("\\.\\", i)) != string::npos) {
tmp.erase(i+1, 2);
}
i = 0;
while( (i = tmp.find("/./", i)) != string::npos) {
tmp.erase(i+1, 2);
}
// Remove any double \\ that are not at the beginning of the path...
i = 1;
while( (i = tmp.find("\\\\", i)) != string::npos) {
tmp.erase(i+1, 1);
}
i = 1;
while( (i = tmp.find("//", i)) != string::npos) {
tmp.erase(i+1, 1);
}
// And last, but not least, the infamous ..\! ...
i = 0;
while( ((i = tmp.find("\\..\\", i)) != string::npos) ) {
tmp[i + 1] = '_';
tmp[i + 2] = '_';
tmp[i + 3] = '_';
i += 2;
}
i = 0;
while( ((i = tmp.find("/../", i)) != string::npos) ) {
tmp[i + 1] = '_';
tmp[i + 2] = '_';
tmp[i + 3] = '_';
i += 2;
}
return tmp;
}
string Util::getShortTimeString() {
char buf[255];
time_t _tt = time(NULL);
tm* _tm = localtime(&_tt);
if(_tm == NULL) {
strcpy(buf, "xx:xx");
} else {
strftime(buf, 254, SETTING(TIME_STAMPS_FORMAT).c_str(), _tm);
}
return buf;
}
/**
* Decodes a URL the best it can...
* Default ports:
* http:// -> port 80
* dchub:// -> port 411
*/
void Util::decodeUrl(const string& url, string& aServer, u_int16_t& aPort, string& aFile) {
// First, check for a protocol: xxxx://
string::size_type i = 0, j, k;
aServer = emptyString;
aFile = emptyString;
if( (j=url.find("://", i)) != string::npos) {
// Protocol found
string protocol = url.substr(0, j);
i = j + 3;
if(protocol == "http") {
aPort = 80;
} else if(protocol == "dchub") {
aPort = 411;
}
}
if( (j=url.find('/', i)) != string::npos) {
// We have a filename...
aFile = url.substr(j);
}
if( (k=url.find(':', i)) != string::npos) {
// Port
if(j == string::npos) {
aPort = (short)Util::toInt(url.substr(k+1));
} else if(k < j) {
aPort = (short)Util::toInt(url.substr(k+1, j-k-1));
}
} else {
k = j;
}
if(k == string::npos)
aServer = url;
else
aServer = url.substr(i, k-i);
}
string Util::getAwayMessage() {
return (formatTime(awayMsg.empty() ? SETTING(DEFAULT_AWAY_MESSAGE) : awayMsg, awayTime)) + " <DC++ v" VERSIONSTRING ">";
}
string Util::formatBytes(int64_t aBytes) {
char buf[64];
if(aBytes < 1024) {
sprintf(buf, "%d %s", (int)(aBytes&0xffffffff), CSTRING(B));
} else if(aBytes < 1024*1024) {
sprintf(buf, "%.02f %s", (double)aBytes/(1024.0), CSTRING(KiB));
} else if(aBytes < 1024*1024*1024) {
sprintf(buf, "%.02f %s", (double)aBytes/(1024.0*1024.0), CSTRING(MiB));
} else if(aBytes < (int64_t)1024*1024*1024*1024) {
sprintf(buf, "%.02f %s", (double)aBytes/(1024.0*1024.0*1024.0), CSTRING(GiB));
} else if(aBytes < (int64_t)1024*1024*1024*1024*1024) {
sprintf(buf, "%.02f %s", (double)aBytes/(1024.0*1024.0*1024.0*1024.0), CSTRING(TiB));
} else {
sprintf(buf, "%.02f %s", (double)aBytes/(1024.0*1024.0*1024.0*1024.0*1024.0), CSTRING(PIB));
}
return buf;
}
string Util::formatExactSize(int64_t aBytes) {
char buf[64];
#ifdef _WIN32
char number[64];
NUMBERFMTA nf;
sprintf(number, "%I64d", aBytes);
char Dummy[16];
/*No need to read these values from the system because they are not
used to format the exact size*/
nf.NumDigits = 0;
nf.LeadingZero = 0;
nf.NegativeOrder = 0;
nf.lpDecimalSep = ",";
GetLocaleInfoA( LOCALE_SYSTEM_DEFAULT, LOCALE_SGROUPING, Dummy, 16 );
nf.Grouping = atoi(Dummy);
GetLocaleInfoA( LOCALE_SYSTEM_DEFAULT, LOCALE_STHOUSAND, Dummy, 16 );
nf.lpThousandSep = Dummy;
GetNumberFormatA(LOCALE_USER_DEFAULT, 0, number, &nf, buf, sizeof(buf)/sizeof(buf[0]));
#else
sprintf(buf, "%'lld", aBytes);
#endif
sprintf(buf, "%s %s", buf, CSTRING(B));
return buf;
}
string Util::getLocalIp() {
string tmp;
char buf[256];
gethostname(buf, 255);
hostent* he = gethostbyname(buf);
if(he == NULL || he->h_addr_list[0] == 0)
return Util::emptyString;
sockaddr_in dest;
int i = 0;
// We take the first ip as default, but if we can find a better one, use it instead...
memcpy(&(dest.sin_addr), he->h_addr_list[i++], he->h_length);
tmp = inet_ntoa(dest.sin_addr);
if(Util::isPrivateIp(tmp) || strncmp(tmp.c_str(), "169", 3) == 0) {
while(he->h_addr_list[i]) {
memcpy(&(dest.sin_addr), he->h_addr_list[i], he->h_length);
string tmp2 = inet_ntoa(dest.sin_addr);
if(!Util::isPrivateIp(tmp2) && strncmp(tmp2.c_str(), "169", 3) != 0) {
tmp = tmp2;
}
i++;
}
}
return tmp;
}
bool Util::isPrivateIp(string const& ip) {
struct in_addr addr;
addr.s_addr = inet_addr(ip.c_str());
if (addr.s_addr != INADDR_NONE) {
unsigned long haddr = ntohl(addr.s_addr);
return ((haddr & 0xff000000) == 0x0a000000 || // 10.0.0.0/8
(haddr & 0xff000000) == 0x7f000000 || // 127.0.0.0/8
(haddr & 0xfff00000) == 0xac100000 || // 172.16.0.0/12
(haddr & 0xffff0000) == 0xc0a80000); // 192.168.0.0/16
}
return false;
}
typedef const u_int8_t* ccp;
static wchar_t utf8ToLC(ccp& str) {
wchar_t c = 0;
if(str[0] & 0x80) {
if(str[0] & 0x40) {
if(str[0] & 0x20) {
if(str[1] == 0 || str[2] == 0 ||
!((((unsigned char)str[1]) & ~0x3f) == 0x80) ||
!((((unsigned char)str[2]) & ~0x3f) == 0x80))
{
str++;
return 0;
}
c = ((wchar_t)(unsigned char)str[0] & 0xf) << 12 |
((wchar_t)(unsigned char)str[1] & 0x3f) << 6 |
((wchar_t)(unsigned char)str[2] & 0x3f);
str += 3;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -