|
|
@@ -115,7 +115,7 @@ void split(std::pair<std::string, std::string>& hp, const std::string& src, char
|
|
|
hp.second = A2STR::NIL;
|
|
|
std::string::size_type p = src.find(delim);
|
|
|
if(p == std::string::npos) {
|
|
|
- hp.first = src;
|
|
|
+ hp.first = trim(src);
|
|
|
hp.second = A2STR::NIL;
|
|
|
} else {
|
|
|
hp.first = trim(src.substr(0, p));
|
|
|
@@ -130,7 +130,7 @@ std::pair<std::string, std::string> split(const std::string& src, const std::str
|
|
|
hp.second = A2STR::NIL;
|
|
|
std::string::size_type p = src.find_first_of(delims);
|
|
|
if(p == std::string::npos) {
|
|
|
- hp.first = src;
|
|
|
+ hp.first = trim(src);
|
|
|
hp.second = A2STR::NIL;
|
|
|
} else {
|
|
|
hp.first = trim(src.substr(0, p));
|
|
|
@@ -201,6 +201,21 @@ std::string replace(const std::string& target, const std::string& oldstr, const
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+bool isAlpha(const char c)
|
|
|
+{
|
|
|
+ return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
|
|
+}
|
|
|
+
|
|
|
+bool isDigit(const char c)
|
|
|
+{
|
|
|
+ return '0' <= c && c <= '9';
|
|
|
+}
|
|
|
+
|
|
|
+bool isHexDigit(const char c)
|
|
|
+{
|
|
|
+ return isDigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
|
|
|
+}
|
|
|
+
|
|
|
bool inRFC3986ReservedChars(const char c)
|
|
|
{
|
|
|
static const char reserved[] = {
|
|
|
@@ -214,15 +229,34 @@ bool inRFC3986ReservedChars(const char c)
|
|
|
bool inRFC3986UnreservedChars(const char c)
|
|
|
{
|
|
|
static const char unreserved[] = { '-', '.', '_', '~' };
|
|
|
- return
|
|
|
- // ALPHA
|
|
|
- ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') ||
|
|
|
- // DIGIT
|
|
|
- ('0' <= c && c <= '9') ||
|
|
|
+ return isAlpha(c) || isDigit(c) ||
|
|
|
std::find(&unreserved[0], &unreserved[arrayLength(unreserved)], c) !=
|
|
|
&unreserved[arrayLength(unreserved)];
|
|
|
}
|
|
|
|
|
|
+bool inRFC2978MIMECharset(const char c)
|
|
|
+{
|
|
|
+ static const char chars[] = {
|
|
|
+ '!', '#', '$', '%', '&',
|
|
|
+ '\'', '+', '-', '^', '_',
|
|
|
+ '`', '{', '}', '~'
|
|
|
+ };
|
|
|
+ return isAlpha(c) || isDigit(c) ||
|
|
|
+ std::find(&chars[0], &chars[arrayLength(chars)], c) !=
|
|
|
+ &chars[arrayLength(chars)];
|
|
|
+}
|
|
|
+
|
|
|
+bool inRFC2616HttpToken(const char c)
|
|
|
+{
|
|
|
+ static const char chars[] = {
|
|
|
+ '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.',
|
|
|
+ '^', '_', '`', '|', '~'
|
|
|
+ };
|
|
|
+ return isAlpha(c) || isDigit(c) ||
|
|
|
+ std::find(&chars[0], &chars[arrayLength(chars)], c) !=
|
|
|
+ &chars[arrayLength(chars)];
|
|
|
+}
|
|
|
+
|
|
|
std::string urlencode(const unsigned char* target, size_t len) {
|
|
|
std::string dest;
|
|
|
for(size_t i = 0; i < len; ++i) {
|
|
|
@@ -244,9 +278,7 @@ std::string urlencode(const std::string& target)
|
|
|
std::string torrentUrlencode(const unsigned char* target, size_t len) {
|
|
|
std::string dest;
|
|
|
for(size_t i = 0; i < len; ++i) {
|
|
|
- if(('0' <= target[i] && target[i] <= '9') ||
|
|
|
- ('A' <= target[i] && target[i] <= 'Z') ||
|
|
|
- ('a' <= target[i] && target[i] <= 'z')) {
|
|
|
+ if(isAlpha(target[i]) || isDigit(target[i])) {
|
|
|
dest += target[i];
|
|
|
} else {
|
|
|
dest.append(StringFormat("%%%02X", target[i]).str());
|
|
|
@@ -267,7 +299,7 @@ std::string urldecode(const std::string& target) {
|
|
|
itr != target.end(); ++itr) {
|
|
|
if(*itr == '%') {
|
|
|
if(itr+1 != target.end() && itr+2 != target.end() &&
|
|
|
- isxdigit(*(itr+1)) && isxdigit(*(itr+2))) {
|
|
|
+ isHexDigit(*(itr+1)) && isHexDigit(*(itr+2))) {
|
|
|
result += parseInt(std::string(itr+1, itr+3), 16);
|
|
|
itr += 2;
|
|
|
} else {
|
|
|
@@ -610,36 +642,139 @@ void parsePrioritizePieceRange
|
|
|
result.insert(result.end(), indexes.begin(), indexes.end());
|
|
|
}
|
|
|
|
|
|
-std::string getContentDispositionFilename(const std::string& header) {
|
|
|
- static const std::string keyName = "filename=";
|
|
|
- std::string::size_type attributesp = header.find(keyName);
|
|
|
- if(attributesp == std::string::npos) {
|
|
|
- return A2STR::NIL;
|
|
|
- }
|
|
|
- std::string::size_type filenamesp = attributesp+keyName.size();
|
|
|
- std::string::size_type filenameep;
|
|
|
- if(filenamesp == header.size()) {
|
|
|
- return A2STR::NIL;
|
|
|
- }
|
|
|
-
|
|
|
- if(header[filenamesp] == '\'' || header[filenamesp] == '"') {
|
|
|
- char quoteChar = header[filenamesp];
|
|
|
- filenameep = header.find(quoteChar, filenamesp+1);
|
|
|
- } else {
|
|
|
- filenameep = header.find(';', filenamesp);
|
|
|
+static std::string trimBasename(const std::string& src)
|
|
|
+{
|
|
|
+ static const std::string TRIMMED("\r\n\t '\"");
|
|
|
+ std::string fn = File(trim(src, TRIMMED)).getBasename();
|
|
|
+ std::string::iterator enditer = std::remove(fn.begin(), fn.end(), '\\');
|
|
|
+ fn = std::string(fn.begin(), enditer);
|
|
|
+ if(fn == ".." || fn == A2STR::DOT_C) {
|
|
|
+ fn = A2STR::NIL;
|
|
|
}
|
|
|
- if(filenameep == std::string::npos) {
|
|
|
- filenameep = header.size();
|
|
|
+ return fn;
|
|
|
+}
|
|
|
+
|
|
|
+// Converts ISO/IEC 8859-1 string to UTF-8 string. If there is a
|
|
|
+// character not in ISO/IEC 8859-1, returns empty string.
|
|
|
+std::string iso8859ToUtf8(const std::string& src)
|
|
|
+{
|
|
|
+ std::string dest;
|
|
|
+ for(std::string::const_iterator itr = src.begin(); itr != src.end(); ++itr) {
|
|
|
+ unsigned char c = *itr;
|
|
|
+ if(0xa0 <= c && c <= 0xff) {
|
|
|
+ if(c <= 0xbf) {
|
|
|
+ dest += 0xc2;
|
|
|
+ } else {
|
|
|
+ dest += 0xc3;
|
|
|
+ }
|
|
|
+ dest += c&(~0x40);
|
|
|
+ } else if(0x80 <= c && c <= 0x9f) {
|
|
|
+ return A2STR::NIL;
|
|
|
+ } else {
|
|
|
+ dest += c;
|
|
|
+ }
|
|
|
}
|
|
|
- static const std::string TRIMMED("\r\n '\"");
|
|
|
- std::string fn =
|
|
|
- File(trim(header.substr
|
|
|
- (filenamesp, filenameep-filenamesp), TRIMMED)).getBasename();
|
|
|
- if(fn == ".." || fn == A2STR::DOT_C) {
|
|
|
- return A2STR::NIL;
|
|
|
- } else {
|
|
|
- return fn;
|
|
|
+ return dest;
|
|
|
+}
|
|
|
+
|
|
|
+std::string getContentDispositionFilename(const std::string& header)
|
|
|
+{
|
|
|
+ std::string filename;
|
|
|
+ std::vector<std::string> params;
|
|
|
+ split(header, std::back_inserter(params), A2STR::SEMICOLON_C, true);
|
|
|
+ for(std::vector<std::string>::iterator i = params.begin();
|
|
|
+ i != params.end(); ++i) {
|
|
|
+ std::string& param = *i;
|
|
|
+ static const std::string keyName = "filename";
|
|
|
+ if(!startsWith(toLower(param), keyName) || param.size() == keyName.size()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ std::string::iterator markeritr = param.begin()+keyName.size();
|
|
|
+ if(*markeritr == '*') {
|
|
|
+ // See RFC2231 Section4 and draft-reschke-rfc2231-in-http.
|
|
|
+ // Please note that this function doesn't do charset conversion
|
|
|
+ // except that if iso-8859-1 is specified, it is converted to
|
|
|
+ // utf-8.
|
|
|
+ ++markeritr;
|
|
|
+ for(; markeritr != param.end() && *markeritr == ' '; ++markeritr);
|
|
|
+ if(markeritr == param.end() || *markeritr != '=') {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ std::pair<std::string, std::string> paramPair;
|
|
|
+ split(paramPair, param, '=');
|
|
|
+ std::string value = paramPair.second;
|
|
|
+ std::vector<std::string> extValues;
|
|
|
+ split(value, std::back_inserter(extValues), "'", false, true);
|
|
|
+ if(extValues.size() != 3) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bool bad = false;
|
|
|
+ const std::string& charset = extValues[0];
|
|
|
+ for(std::string::const_iterator j = charset.begin(); j != charset.end();
|
|
|
+ ++j) {
|
|
|
+ // Since we first split parameter by ', we can safely assume
|
|
|
+ // that ' is not included in charset.
|
|
|
+ if(!inRFC2978MIMECharset(*j)) {
|
|
|
+ bad = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(bad) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bad = false;
|
|
|
+ value = extValues[2];
|
|
|
+ for(std::string::const_iterator j = value.begin(); j != value.end(); ++j){
|
|
|
+ if(*j == '%') {
|
|
|
+ if(j+1 != value.end() && isHexDigit(*(j+1)) &&
|
|
|
+ j+2 != value.end() && isHexDigit(*(j+2))) {
|
|
|
+ j += 2;
|
|
|
+ } else {
|
|
|
+ bad = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if(*j == '*' || *j == '\'' || !inRFC2616HttpToken(*j)) {
|
|
|
+ bad = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(bad) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ value = trimBasename(urldecode(value));
|
|
|
+ if(toLower(extValues[0]) == "iso-8859-1") {
|
|
|
+ value = iso8859ToUtf8(value);
|
|
|
+ }
|
|
|
+ filename = value;
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ for(; markeritr != param.end() && *markeritr == ' '; ++markeritr);
|
|
|
+ if(markeritr == param.end() || *markeritr != '=') {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ std::pair<std::string, std::string> paramPair;
|
|
|
+ split(paramPair, param, '=');
|
|
|
+ std::string value = paramPair.second;
|
|
|
+ if(value.empty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ std::string::iterator filenameLast;
|
|
|
+ if(*value.begin() == '\'' || *value.begin() == '"') {
|
|
|
+ char qc = *value.begin();
|
|
|
+ for(filenameLast = value.begin()+1;
|
|
|
+ filenameLast != value.end() && *filenameLast != qc;
|
|
|
+ ++filenameLast);
|
|
|
+ } else {
|
|
|
+ filenameLast = value.end();
|
|
|
+ }
|
|
|
+ value = trimBasename(urldecode(std::string(value.begin(), filenameLast)));
|
|
|
+ filename = value;
|
|
|
+ // continue because there is a chance we can find filename*=...
|
|
|
+ }
|
|
|
}
|
|
|
+ return filename;
|
|
|
}
|
|
|
|
|
|
std::string randomAlpha(size_t length, const RandomizerHandle& randomizer) {
|
|
|
@@ -801,8 +936,8 @@ bool isNumber(const std::string& what)
|
|
|
if(what.empty()) {
|
|
|
return false;
|
|
|
}
|
|
|
- for(uint32_t i = 0; i < what.size(); ++i) {
|
|
|
- if(!isdigit(what[i])) {
|
|
|
+ for(std::string::const_iterator i = what.begin(); i != what.end(); ++i) {
|
|
|
+ if(!isDigit(*i)) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|