aria2/src/Util.cc

611 lines
15 KiB
C++

/* <!-- copyright */
/*
* aria2 - The high speed download utility
*
* Copyright (C) 2006 Tatsuhiro Tsujikawa
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
* You must obey the GNU General Public License in all respects
* for all of the code used other than OpenSSL. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you
* do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source
* files in the program, then also delete it here.
*/
/* copyright --> */
#include "Util.h"
#include "DlAbortEx.h"
#include "File.h"
#include "message.h"
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
string Util::itos(int value, bool comma) {
string str = llitos(value, comma);
return str;
}
string Util::llitos(long long int value, bool comma)
{
string str;
bool flag = false;
if(value < 0) {
flag = true;
value = -value;
} else if(value == 0) {
str = "0";
return str;
}
int count = 0;
while(value) {
++count;
char digit = value%10+'0';
str.insert(str.begin(), digit);
value /= 10;
if(comma && count > 3 && count%3 == 1) {
str.insert(str.begin()+1, ',');
}
}
if(flag) {
str.insert(str.begin(), '-');
}
return str;
}
string Util::trim(const string& src) {
string::size_type sp = src.find_first_not_of("\r\n\t ");
string::size_type ep = src.find_last_not_of("\r\n\t ");
if(sp == string::npos || ep == string::npos) {
return "";
} else {
return src.substr(sp, ep-sp+1);
}
}
void Util::split(pair<string, string>& hp, const string& src, char delim) {
hp.first = "";
hp.second = "";
string::size_type p = src.find(delim);
if(p == string::npos) {
hp.first = src;
hp.second = "";
} else {
hp.first = trim(src.substr(0, p));
hp.second = trim(src.substr(p+1));
}
}
long long int Util::difftv(struct timeval tv1, struct timeval tv2) {
if(tv1.tv_sec < tv2.tv_sec || tv1.tv_sec == tv2.tv_sec && tv1.tv_usec < tv2.tv_usec) {
return 0;
}
return ((long long int)(tv1.tv_sec-tv2.tv_sec)*1000000+
tv1.tv_usec-tv2.tv_usec);
}
int Util::difftvsec(struct timeval tv1, struct timeval tv2) {
if(tv1.tv_sec < tv2.tv_sec) {
return 0;
}
return tv1.tv_sec-tv2.tv_sec;
}
void Util::slice(Strings& result, const string& src, char delim, bool doTrim) {
string::size_type p = 0;
while(1) {
string::size_type np = src.find(delim, p);
if(np == string::npos) {
string term = src.substr(p);
if(doTrim) {
term = trim(term);
}
if(term.size()) {
result.push_back(term);
}
break;
}
string term = src.substr(p, np-p);
if(doTrim) {
term = trim(term);
}
p = np+1;
if(term.size()) {
result.push_back(term);
}
}
}
bool Util::startsWith(const string& target, const string& part) {
if(target.size() < part.size()) {
return false;
}
if(part == "") {
return true;
}
if(target.find(part) == 0) {
return true;
} else {
return false;
}
}
bool Util::endsWith(const string& target, const string& part) {
if(target.size() < part.size()) {
return false;
}
if(part == "") {
return true;
}
if(target.rfind(part) == target.size()-part.size()) {
return true;
} else {
return false;
}
}
string Util::replace(const string& target, const string& oldstr, const string& newstr) {
if(target == "" || oldstr == "" ) {
return target;
}
string result;
string::size_type p = 0;
string::size_type np = target.find(oldstr);
while(np != string::npos) {
result += target.substr(p, np-p)+newstr;
p = np+oldstr.size();
np = target.find(oldstr, p);
}
result += target.substr(p);
return result;
}
string Util::urlencode(const unsigned char* target, int len) {
string dest;
for(int i = 0; i < len; i++) {
if(!('0' <= target[i] && target[i] <= '9' ||
'A' <= target[i] && target[i] <= 'Z' ||
'a' <= target[i] && target[i] <= 'z' ||
'$' == target[i] || '-' == target[i] ||
'_' == target[i] || '.' == target[i] ||
'+' == target[i] || '!' == target[i] ||
'*' == target[i] || '\'' == target[i] ||
'(' == target[i] || ')' == target[i] ||
',' == target[i])) {
char temp[4];
sprintf(temp, "%%%02x", target[i]);
temp[sizeof(temp)-1] = '\0';
dest.append(temp);
} else {
dest += target[i];
}
}
return dest;
}
string Util::torrentUrlencode(const unsigned char* target, int len) {
string dest;
for(int i = 0; i < len; i++) {
if(isalpha(target[i]) || isdigit(target[i])) {
dest += target[i];
} else {
char temp[4];
sprintf(temp, "%%%02x", target[i]);
temp[sizeof(temp)-1] = '\0';
dest.append(temp);
}
}
return dest;
}
string Util::urldecode(const string& target) {
string result;
for(string::const_iterator itr = target.begin();
itr != target.end(); itr++) {
if(*itr == '%') {
if(itr+1 != target.end() && itr+2 != target.end() &&
isxdigit(*(itr+1)) && isxdigit(*(itr+2))) {
char temp[3];
temp[0] = *(itr+1);
temp[1] = *(itr+2);
temp[2] = '\0';
result += strtol(temp, 0, 16);
itr += 2;
} else {
result += *itr;
}
} else {
result += *itr;
}
}
return result;
}
string Util::toHex(const unsigned char* src, int len) {
char* temp = new char[len*2+1];
for(int i = 0; i < len; i++) {
sprintf(temp+i*2, "%02x", src[i]);
}
temp[len*2] = '\0';
string hex = temp;
delete [] temp;
return hex;
}
FILE* Util::openFile(const string& filename, const string& mode) {
FILE* file = fopen(filename.c_str(), mode.c_str());
return file;
}
void Util::fileCopy(const string& dest, const string& src) {
File file(src);
rangedFileCopy(dest, src, 0, file.size());
}
void Util::rangedFileCopy(const string& dest, const string& src, long long int srcOffset, long long int length) {
int bufSize = 4096;
char buf[bufSize];
int destFd = -1;
int srcFd = -1;
try {
if((destFd = open(dest.c_str(), O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR)) == -1) {
throw new DlAbortEx(EX_FILE_OPEN, dest.c_str(), strerror(errno));
}
if((srcFd = open(src.c_str(), O_RDONLY, S_IRUSR|S_IWUSR)) == -1) {
throw new DlAbortEx(EX_FILE_OPEN, src.c_str(), strerror(errno));
}
if(lseek(srcFd, srcOffset, SEEK_SET) != srcOffset) {
throw new DlAbortEx(EX_FILE_SEEK, src.c_str(), strerror(errno));
}
int x = length/bufSize;
int r = length%bufSize;
for(int i = 0; i < x; i++) {
int readLength;
if((readLength = read(srcFd, buf, bufSize)) == -1 || readLength != bufSize) {
throw new DlAbortEx(EX_FILE_READ, src.c_str(), strerror(errno));
}
if(write(destFd, buf, readLength) == -1) {
throw new DlAbortEx(EX_FILE_WRITE, dest.c_str(), strerror(errno));
}
}
if(r > 0) {
int readLength;
if((readLength = read(srcFd, buf, r)) == -1 || readLength != r) {
throw new DlAbortEx(EX_FILE_READ, src.c_str(), strerror(errno));
}
if(write(destFd, buf, r) == -1) {
throw new DlAbortEx(EX_FILE_WRITE, dest.c_str(), strerror(errno));
}
}
close(srcFd);
close(destFd);
srcFd = -1;
destFd = -1;
} catch(Exception* e) {
if(srcFd != -1) {
close(srcFd);
}
if(destFd != -1) {
close(destFd);
}
throw;
}
}
bool Util::isPowerOf(int num, int base) {
if(base <= 0) { return false; }
if(base == 1) { return true; }
while(num%base == 0) {
num /= base;
if(num == 1) {
return true;
}
}
return false;
}
string Util::secfmt(int sec) {
string str;
if(sec >= 3600) {
str = itos(sec/3600)+"h";
sec %= 3600;
}
if(sec >= 60) {
int min = sec/60;
if(min < 10) {
str += "0";
}
str += itos(min)+"m";
sec %= 60;
}
if(sec < 10) {
str += "0";
}
str += itos(sec)+"s";
return str;
}
int Util::expandBuffer(char** pbuf, int curLength, int newLength) {
char* newbuf = new char[newLength];
memcpy(newbuf, *pbuf, curLength);
delete [] *pbuf;
*pbuf = newbuf;
return newLength;
}
int getNum(const char* buf, int offset, int length) {
char* temp = new char[length+1];
memcpy(temp, buf+offset, length);
temp[length] = '\0';
int x = strtol(temp, NULL, 10);
delete [] temp;
return x;
}
void unfoldSubRange(const string& src, Integers& range) {
if(src.empty()) {
return;
}
string::size_type p = src.find_first_of(",-");
if(p == 0) {
return;
} else if(p == string::npos) {
range.push_back(atoi(src.c_str()));
} else {
if(src.at(p) == ',') {
int num = getNum(src.c_str(), 0, p);
range.push_back(num);
unfoldSubRange(src.substr(p+1), range);
} else if(src.at(p) == '-') {
int rightNumBegin = p+1;
string::size_type nextDelim = src.find_first_of(",", rightNumBegin);
if(nextDelim == string::npos) {
nextDelim = src.size();
}
int left = getNum(src.c_str(), 0, p);
int right = getNum(src.c_str(), rightNumBegin, nextDelim-rightNumBegin);
for(int i = left; i <= right; i++) {
range.push_back(i);
}
if(src.size() > nextDelim) {
unfoldSubRange(src.substr(nextDelim+1), range);
}
}
}
}
void Util::unfoldRange(const string& src, Integers& range) {
unfoldSubRange(src, range);
sort(range.begin(), range.end());
range.erase(unique(range.begin(), range.end()), range.end());
}
string Util::getContentDispositionFilename(const string& header) {
string::size_type attributesp = header.find("filename=\"");
if(attributesp == string::npos) {
return "";
}
string::size_type filenamesp = attributesp+strlen("filename=\"");
string::size_type filenameep = header.find("\"", filenamesp);
if(filenameep == string::npos) {
return "";
}
return trim(header.substr(filenamesp, filenameep-filenamesp));
}
#ifdef ENABLE_MESSAGE_DIGEST
void Util::sha1Sum(unsigned char* digest, const void* data, int dataLength) {
MessageDigestContext ctx(DIGEST_ALGO_SHA1);
ctx.digestInit();
ctx.digestUpdate(data, dataLength);
ctx.digestFinal(digest);
ctx.digestFree();
}
#endif // ENABLE_MESSAGE_DIGEST
#ifdef ENABLE_MESSAGE_DIGEST
void Util::fileChecksum(const string& filename, unsigned char* digest,
MessageDigestContext::DigestAlgo algo) {
MessageDigestContext ctx(algo);
ctx.digestInit();
int BUFLEN = 4096;
char buf[BUFLEN];
int fd;
if((fd = open(filename.c_str(), O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
throw new DlAbortEx(EX_FILE_OPEN, filename.c_str(), strerror(errno));
}
while(1) {
int size = read(fd, buf, BUFLEN);
if(size == -1) {
if(errno == EINTR) {
continue;
} else {
close(fd);
throw new DlAbortEx(EX_FILE_READ, filename.c_str(), strerror(errno));
}
} else if(size > 0) {
ctx.digestUpdate(buf, size);
}
if(size < BUFLEN) {
break;
}
}
ctx.digestFinal(digest);
ctx.digestFree();
}
#endif // ENABLE_MESSAGE_DIGEST
#ifdef ENABLE_BITTORRENT
Integers Util::computeFastSet(string ipaddr, const unsigned char* infoHash,
int pieces, int fastSetSize) {
Integers fastSet;
struct in_addr saddr;
if(inet_aton(ipaddr.c_str(), &saddr) == 0) {
abort();
}
unsigned char tx[24];
memcpy(tx, (void*)&saddr.s_addr, 4);
if((tx[0] & 0x80) == 0 || (tx[0] & 0x40) == 0) {
tx[2] = 0x00;
tx[3] = 0x00;
} else {
tx[3] = 0x00;
}
memcpy(tx+4, infoHash, 20);
unsigned char x[20];
sha1Sum(x, tx, 24);
while((int)fastSet.size() < fastSetSize) {
for(int i = 0; i < 5 && (int)fastSet.size() < fastSetSize; i++) {
int j = i*4;
unsigned int ny;
memcpy(&ny, x+j, 4);
unsigned int y = ntohl(ny);
int index = y%pieces;
if(find(fastSet.begin(), fastSet.end(), index) == fastSet.end()) {
fastSet.push_back(index);
}
}
unsigned char temp[20];
sha1Sum(temp, x, 20);
memcpy(x, temp, sizeof(x));
}
return fastSet;
}
#endif // ENABLE_BITTORRENT
/*
int Util::countBit(unsigned int n) {
int count = 0;
while(n > 0) {
count++;
n &= (n-1);
}
return count;
}
*/
static int nbits[] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};
int Util::countBit(unsigned int n) {
/*
return
nbits[n & 0xffu]+
nbits[(n >> 8) & 0xffu]+
nbits[(n >> 16) & 0xffu]+
nbits[(n >> 24) & 0xffu];
*/
int count = 0;
int size = sizeof(unsigned int);
for(int i = 0; i < size; i++) {
count += nbits[(n >> i*8) & 0xffu];
}
return count;
}
string Util::randomAlpha(int length) {
string str;
for(int i = 0; i < length; i++) {
int index = (int)(((double)52)*random()/(RAND_MAX+1.0));
char ch;
if(index < 26) {
ch = (char)('A'+index);
} else {
ch = (char)('a'+index-26);
}
str += ch;
}
return str;
}
class UpperCase {
public:
void operator()(char& ch) {
ch = toupper(ch);
}
};
string Util::toUpper(const string& src) {
string temp = src;
for_each(temp.begin(), temp.end(), UpperCase());
return temp;
}
class LowerCase {
public:
void operator()(char& ch) {
ch = tolower(ch);
}
};
string Util::toLower(const string& src) {
string temp = src;
for_each(temp.begin(), temp.end(), LowerCase());
return temp;
}
bool Util::isNumbersAndDotsNotation(const string& name) {
struct sockaddr_in sockaddr;
if(inet_aton(name.c_str(), &sockaddr.sin_addr)) {
return true;
} else {
return false;
}
}
void Util::setGlobalSignalHandler(int signal, void (*handler)(int), int flags) {
struct sigaction sigact;
sigact.sa_handler = handler;
sigact.sa_flags = flags;
sigemptyset(&sigact.sa_mask);
sigaction(signal, &sigact, NULL);
}