| /** |
| * Copyright (C) 2013 Regents of the University of California. |
| * @author: Jeff Thompson <jefft0@remap.ucla.edu> |
| * See COPYING for copyright and distribution information. |
| */ |
| |
| #include <stdexcept> |
| #include <algorithm> |
| #include "name.hpp" |
| |
| using namespace std; |
| |
| namespace ndn { |
| |
| static const char *WHITESPACE_CHARS = " \n\r\t"; |
| |
| /** |
| * Modify str in place to erase whitespace on the left. |
| * @param str |
| */ |
| static inline void trimLeft(string& str) |
| { |
| size_t found = str.find_first_not_of(WHITESPACE_CHARS); |
| if (found != string::npos) { |
| if (found > 0) |
| str.erase(0, found); |
| } |
| else |
| // All whitespace |
| str.clear(); |
| } |
| |
| /** |
| * Modify str in place to erase whitespace on the right. |
| * @param str |
| */ |
| static inline void trimRight(string& str) |
| { |
| size_t found = str.find_last_not_of(WHITESPACE_CHARS); |
| if (found != string::npos) { |
| if (found + 1 < str.size()) |
| str.erase(found + 1); |
| } |
| else |
| // All whitespace |
| str.clear(); |
| } |
| |
| /** |
| * Modify str in place to erase whitespace on the left and right. |
| * @param str |
| */ |
| static void trim(string& str) |
| { |
| trimLeft(str); |
| trimRight(str); |
| } |
| |
| /** |
| * Convert the hex character to an integer from 0 to 15, or -1 if not a hex character. |
| * @param c |
| * @return |
| */ |
| static int fromHexChar(unsigned char c) |
| { |
| if (c >= '0' && c <= '9') |
| return (int)c - (int)'0'; |
| else if (c >= 'A' && c <= 'F') |
| return (int)c - (int)'A' + 10; |
| else if (c >= 'a' && c <= 'f') |
| return (int)c - (int)'a' + 10; |
| else |
| return -1; |
| } |
| |
| /** |
| * Return a copy of str, converting each escaped "%XX" to the char value. |
| * @param str |
| */ |
| static string unescape(const string& str) |
| { |
| ostringstream result; |
| |
| for (unsigned int i = 0; i < str.size(); ++i) { |
| if (str[i] == '%' && i + 2 < str.size()) { |
| int hi = fromHexChar(str[i + 1]); |
| int lo = fromHexChar(str[i + 2]); |
| |
| if (hi < 0 || lo < 0) |
| // Invalid hex characters, so just keep the escaped string. |
| result << str[i] << str[i + 1] << str[i + 2]; |
| else |
| result << (unsigned char)(16 * hi + lo); |
| |
| // Skip ahead past the escaped value. |
| i += 2; |
| } |
| else |
| // Just copy through. |
| result << str[i]; |
| } |
| |
| return result.str(); |
| } |
| |
| bool Name::Component::setFromEscapedString(const char *first, const char *last) |
| { |
| string trimmedString(first, last); |
| trim(trimmedString); |
| string component = unescape(trimmedString); |
| |
| if (component.find_first_not_of(".") == string::npos) { |
| // Special case for component of only periods. |
| if (component.size() <= 2) |
| // Zero, one or two periods is illegal. Ignore this component. |
| return false; |
| else |
| // Remove 3 periods. |
| value_ = Blob((const unsigned char *)&component[3], component.size() - 3); |
| } |
| else |
| value_ = Blob((const unsigned char *)&component[0], component.size()); |
| |
| return true; |
| } |
| |
| void Name::Component::setSegment(unsigned long segment) |
| { |
| ptr_lib::shared_ptr<vector<unsigned char> > value; |
| |
| // Add the leading zero. |
| value->push_back(0); |
| |
| // First encode in little endian. |
| while (segment != 0) { |
| value->push_back(segment & 0xff); |
| segment >>= 8; |
| } |
| |
| // Make it big endian. |
| reverse(value->begin() + 1, value->end()); |
| value_ = value; |
| } |
| |
| void Name::set(const char *uri_cstr) |
| { |
| components_.clear(); |
| |
| string uri = uri_cstr; |
| trim(uri); |
| if (uri.size() == 0) |
| return; |
| |
| size_t iColon = uri.find(':'); |
| if (iColon != string::npos) { |
| // Make sure the colon came before a '/'. |
| size_t iFirstSlash = uri.find('/'); |
| if (iFirstSlash == string::npos || iColon < iFirstSlash) { |
| // Omit the leading protocol such as ndn: |
| uri.erase(0, iColon + 1); |
| trim(uri); |
| } |
| } |
| |
| // Trim the leading slash and possibly the authority. |
| if (uri[0] == '/') { |
| if (uri.size() >= 2 && uri[1] == '/') { |
| // Strip the authority following "//". |
| size_t iAfterAuthority = uri.find('/', 2); |
| if (iAfterAuthority == string::npos) |
| // Unusual case: there was only an authority. |
| return; |
| else { |
| uri.erase(0, iAfterAuthority + 1); |
| trim(uri); |
| } |
| } |
| else { |
| uri.erase(0, 1); |
| trim(uri); |
| } |
| } |
| |
| size_t iComponentStart = 0; |
| |
| // Unescape the components. |
| while (iComponentStart < uri.size()) { |
| size_t iComponentEnd = uri.find("/", iComponentStart); |
| if (iComponentEnd == string::npos) |
| iComponentEnd = uri.size(); |
| |
| components_.push_back(Component()); |
| if (!components_[components_.size() - 1].setFromEscapedString(&uri[iComponentStart], &uri[iComponentEnd])) |
| // Ignore the illegal component. This also gets rid of a trailing '/'. |
| components_.pop_back(); |
| |
| iComponentStart = iComponentEnd + 1; |
| } |
| } |
| |
| void Name::get(struct ndn_Name& nameStruct) const |
| { |
| if (nameStruct.maxComponents < components_.size()) |
| throw runtime_error("nameStruct.maxComponents must be >= this name getNComponents()"); |
| |
| nameStruct.nComponents = components_.size(); |
| for (unsigned int i = 0; i < nameStruct.nComponents; ++i) |
| components_[i].get(nameStruct.components[i]); |
| } |
| |
| void Name::set(const struct ndn_Name& nameStruct) |
| { |
| clear(); |
| for (unsigned int i = 0; i < nameStruct.nComponents; ++i) |
| addComponent(nameStruct.components[i].value, nameStruct.components[i].valueLength); |
| } |
| |
| std::string Name::toUri() const |
| { |
| if (components_.size() == 0) |
| return "/"; |
| |
| ostringstream result; |
| for (unsigned int i = 0; i < components_.size(); ++i) { |
| result << "/"; |
| toEscapedString(*components_[i].getValue(), result); |
| } |
| |
| return result.str(); |
| } |
| |
| bool Name::match(const Name& name) const |
| { |
| // Imitate ndn_Name_match. |
| |
| // This name is longer than the name we are checking it against. |
| if (components_.size() > name.components_.size()) |
| return 0; |
| |
| // Check if at least one of given components doesn't match. |
| unsigned int i; |
| for (i = 0; i < components_.size(); ++i) { |
| const Component &selfComponent = components_[i]; |
| const Component &nameComponent = name.components_[i]; |
| |
| if (selfComponent.getValue() != nameComponent.getValue()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void Name::toEscapedString(const vector<unsigned char>& value, ostringstream& result) |
| { |
| bool gotNonDot = false; |
| for (unsigned i = 0; i < value.size(); ++i) { |
| if (value[i] != 0x2e) { |
| gotNonDot = true; |
| break; |
| } |
| } |
| if (!gotNonDot) { |
| // Special case for component of zero or more periods. Add 3 periods. |
| result << "..."; |
| for (unsigned int i = 0; i < value.size(); ++i) |
| result << '.'; |
| } |
| else { |
| // In case we need to escape, set to upper case hex and save the previous flags. |
| ios::fmtflags saveFlags = result.flags(ios::hex | ios::uppercase); |
| |
| for (unsigned int i = 0; i < value.size(); ++i) { |
| unsigned char x = value[i]; |
| // Check for 0-9, A-Z, a-z, (+), (-), (.), (_) |
| if (x >= 0x30 && x <= 0x39 || x >= 0x41 && x <= 0x5a || |
| x >= 0x61 && x <= 0x7a || x == 0x2b || x == 0x2d || |
| x == 0x2e || x == 0x5f) |
| result << x; |
| else { |
| result << '%'; |
| if (x < 16) |
| result << '0'; |
| result << (unsigned int)x; |
| } |
| } |
| |
| // Restore. |
| result.flags(saveFlags); |
| } |
| } |
| |
| } |