This source file includes following definitions.
- HexDigitsPrefix
- ParseLeadingHex64Value
- AppendSegment
- EncodeSegment
- Decode
- LegacyEscape
#include <stdlib.h>
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "net/base/net_util.h"
#include "net/tools/dump_cache/url_to_filename_encoder.h"
using std::string;
namespace {
int HexDigitsPrefix(const char* buf, int num_digits) {
for (int i = 0; i < num_digits; i++) {
if (!IsHexDigit(buf[i]))
return 0;
}
return 1;
}
#ifdef WIN32
#define strtoull _strtoui64
#endif
uint64 ParseLeadingHex64Value(const char *str, uint64 deflt) {
char *error = NULL;
const uint64 value = strtoull(str, &error, 16);
return (error == str) ? deflt : value;
}
}
namespace net {
const char UrlToFilenameEncoder::kEscapeChar = ',';
const char UrlToFilenameEncoder::kTruncationChar = '-';
const size_t UrlToFilenameEncoder::kMaximumSubdirectoryLength = 128;
void UrlToFilenameEncoder::AppendSegment(string* segment, string* dest) {
CHECK(!segment->empty());
if ((*segment == ".") || (*segment == "..")) {
dest->append(1, kEscapeChar);
dest->append(*segment);
segment->clear();
} else {
size_t segment_size = segment->size();
if (segment_size > kMaximumSubdirectoryLength) {
segment_size = kMaximumSubdirectoryLength - 2;
if ((*segment)[segment_size - 1] == kEscapeChar) {
segment_size -= 1;
} else if ((*segment)[segment_size - 2] == kEscapeChar) {
segment_size -= 2;
}
dest->append(segment->data(), segment_size);
dest->append(1, kEscapeChar);
dest->append(1, kTruncationChar);
segment->erase(0, segment_size);
} else {
dest->append(*segment);
segment->clear();
}
}
}
void UrlToFilenameEncoder::EncodeSegment(const string& filename_prefix,
const string& escaped_ending,
char dir_separator,
string* encoded_filename) {
string filename_ending = UrlUtilities::Unescape(escaped_ending);
char encoded[3];
int encoded_len;
string segment;
size_t start_of_segment = filename_prefix.find_last_of(dir_separator);
if (start_of_segment == string::npos) {
segment = filename_prefix;
} else {
segment = filename_prefix.substr(start_of_segment + 1);
*encoded_filename = filename_prefix.substr(0, start_of_segment + 1);
}
size_t index = 0;
if (!filename_ending.empty() && (filename_ending[0] == dir_separator)) {
encoded_filename->append(segment);
segment.clear();
encoded_filename->append(1, dir_separator);
++index;
}
for (; index < filename_ending.length(); ++index) {
unsigned char ch = static_cast<unsigned char>(filename_ending[index]);
if ((ch == dir_separator) && !segment.empty()) {
AppendSegment(&segment, encoded_filename);
encoded_filename->append(1, dir_separator);
segment.clear();
} else {
if ((ch == '_') || (ch == '.') || (ch == '=') || (ch == '+') ||
(ch == '-') || (('0' <= ch) && (ch <= '9')) ||
(('A' <= ch) && (ch <= 'Z')) || (('a' <= ch) && (ch <= 'z'))) {
encoded[0] = ch;
encoded_len = 1;
} else {
encoded[0] = kEscapeChar;
encoded[1] = ch / 16;
encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0';
encoded[2] = ch % 16;
encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0';
encoded_len = 3;
}
segment.append(encoded, encoded_len);
if (segment.size() > kMaximumSubdirectoryLength) {
AppendSegment(&segment, encoded_filename);
encoded_filename->append(1, dir_separator);
}
}
}
segment += kEscapeChar;
AppendSegment(&segment, encoded_filename);
if (!segment.empty()) {
encoded_filename->append(1, dir_separator);
encoded_filename->append(segment);
}
}
bool UrlToFilenameEncoder::Decode(const string& encoded_filename,
char dir_separator,
string* decoded_url) {
enum State {
kStart,
kEscape,
kFirstDigit,
kTruncate,
kEscapeDot
};
State state = kStart;
char hex_buffer[3];
hex_buffer[2] = '\0';
for (size_t i = 0; i < encoded_filename.size(); ++i) {
char ch = encoded_filename[i];
switch (state) {
case kStart:
if (ch == kEscapeChar) {
state = kEscape;
} else if (ch == dir_separator) {
decoded_url->append(1, '/');
} else {
decoded_url->append(1, ch);
}
break;
case kEscape:
if (HexDigitsPrefix(&ch, 1) == 1) {
hex_buffer[0] = ch;
state = kFirstDigit;
} else if (ch == kTruncationChar) {
state = kTruncate;
} else if (ch == '.') {
decoded_url->append(1, '.');
state = kEscapeDot;
} else if (ch == dir_separator) {
decoded_url->append(1, '/');
state = kStart;
} else {
return false;
}
break;
case kFirstDigit:
if (HexDigitsPrefix(&ch, 1) == 1) {
hex_buffer[1] = ch;
uint64 hex_value = ParseLeadingHex64Value(hex_buffer, 0);
decoded_url->append(1, static_cast<char>(hex_value));
state = kStart;
} else {
return false;
}
break;
case kTruncate:
if (ch == dir_separator) {
state = kStart;
} else {
return false;
}
break;
case kEscapeDot:
decoded_url->append(1, ch);
state = kStart;
break;
}
}
return (state == kEscape);
}
string UrlToFilenameEncoder::LegacyEscape(const string& path) {
string output;
int last_slash = 0;
for (size_t index = 0; index < path.length(); index++) {
char ch = path[index];
if (ch == 0x5C)
last_slash = index;
if ((ch == 0x2D) ||
(ch == 0x5C) || (ch == 0x5F) ||
((0x30 <= ch) && (ch <= 0x39)) ||
((0x41 <= ch) && (ch <= 0x5A)) ||
((0x61 <= ch) && (ch <= 0x7A))) {
output.append(&path[index], 1);
} else {
char encoded[3];
encoded[0] = 'x';
encoded[1] = ch / 16;
encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0';
encoded[2] = ch % 16;
encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0';
output.append(encoded, 3);
}
if (index - last_slash > kMaximumSubdirectoryLength) {
#ifdef WIN32
char slash = '\\';
#else
char slash = '/';
#endif
output.append(&slash, 1);
last_slash = index;
}
}
return output;
}
}