blob: 74df14680e9a7b418b19fd63cc835f8bcf8d6784 [file] [log] [blame]
Jeff Thompson47eecfc2013-07-07 22:56:46 -07001/**
2 * @author: Jeff Thompson
3 * See COPYING for copyright and distribution information.
Jeff Thompson9c41dfe2013-06-27 12:10:25 -07004 */
5
Jeff Thompson54909772013-07-07 22:38:57 -07006#include <stdexcept>
Jeff Thompsone6063512013-07-01 15:11:28 -07007#include <sstream>
Jeff Thompson53412192013-08-06 13:35:50 -07008#include "name.hpp"
Jeff Thompson9c41dfe2013-06-27 12:10:25 -07009
10using namespace std;
11
12namespace ndn {
13
Jeff Thompson4b2479a2013-07-02 15:37:39 -070014/**
15 * Write the value to result, escaping characters according to the NDN URI Scheme.
16 * This also adds "..." to a value with zero or more ".".
17 * @param value the buffer with the value to escape
18 * @param result the string stream to write to.
19 */
20static void toEscapedString(const vector<unsigned char> &value, ostringstream &result)
21{
22 bool gotNonDot = false;
23 for (unsigned i = 0; i < value.size(); ++i) {
24 if (value[i] != 0x2e) {
25 gotNonDot = true;
26 break;
27 }
28 }
29 if (!gotNonDot) {
30 // Special case for component of zero or more periods. Add 3 periods.
31 result << "...";
32 for (unsigned int i = 0; i < value.size(); ++i)
Jeff Thompsond0694862013-08-06 10:49:18 -070033 result << '.';
Jeff Thompson4b2479a2013-07-02 15:37:39 -070034 }
35 else {
36 // In case we need to escape, set to upper case hex and save the previous flags.
37 ios::fmtflags saveFlags = result.flags(ios::hex | ios::uppercase);
38
39 for (unsigned int i = 0; i < value.size(); ++i) {
40 unsigned char x = value[i];
41 // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
42 if (x >= 0x30 && x <= 0x39 || x >= 0x41 && x <= 0x5a ||
43 x >= 0x61 && x <= 0x7a || x == 0x2b || x == 0x2d ||
44 x == 0x2e || x == 0x5f)
45 result << x;
46 else {
Jeff Thompsond0694862013-08-06 10:49:18 -070047 result << '%';
Jeff Thompson4b2479a2013-07-02 15:37:39 -070048 if (x < 16)
Jeff Thompsond0694862013-08-06 10:49:18 -070049 result << '0';
Jeff Thompson4b2479a2013-07-02 15:37:39 -070050 result << (unsigned int)x;
51 }
52 }
53
54 // Restore.
55 result.flags(saveFlags);
56 }
57}
Jeff Thompson26c63d62013-07-02 18:00:26 -070058
59static const char *WHITESPACE_CHARS = " \n\r\t";
60
61/**
62 * Modify str in place to erase whitespace on the left.
63 * @param str
64 */
65static inline void trimLeft(string &str)
66{
67 size_t found = str.find_first_not_of(WHITESPACE_CHARS);
68 if (found != string::npos) {
69 if (found > 0)
70 str.erase(0, found);
71 }
72 else
73 // All whitespace
74 str.clear();
75}
76
77/**
78 * Modify str in place to erase whitespace on the right.
79 * @param str
80 */
81static inline void trimRight(string &str)
82{
83 size_t found = str.find_last_not_of(WHITESPACE_CHARS);
84 if (found != string::npos) {
85 if (found + 1 < str.size())
86 str.erase(found + 1);
87 }
88 else
89 // All whitespace
90 str.clear();
91}
92
93/**
94 * Modify str in place to erase whitespace on the left and right.
95 * @param str
96 */
97static void trim(string &str)
98{
99 trimLeft(str);
100 trimRight(str);
101}
Jeff Thompson443398d2013-07-02 19:45:46 -0700102
Jeff Thompson26c63d62013-07-02 18:00:26 -0700103/**
104 * Convert the hex character to an integer from 0 to 15, or -1 if not a hex character.
105 * @param c
106 * @return
107 */
108static int fromHexChar(unsigned char c)
109{
110 if (c >= '0' && c <= '9')
111 return (int)c - (int)'0';
112 else if (c >= 'A' && c <= 'F')
113 return (int)c - (int)'A' + 10;
114 else if (c >= 'a' && c <= 'f')
115 return (int)c - (int)'a' + 10;
116 else
117 return -1;
118}
119
120/**
121 * Return a copy of str, converting each escaped "%XX" to the char value.
122 * @param str
123 */
124static string unescape(const string &str)
125{
126 ostringstream result;
127
128 for (unsigned int i = 0; i < str.size(); ++i) {
129 if (str[i] == '%' && i + 2 < str.size()) {
130 int hi = fromHexChar(str[i + 1]);
131 int lo = fromHexChar(str[i + 2]);
132
133 if (hi < 0 || lo < 0)
134 // Invalid hex characters, so just keep the escaped string.
135 result << str[i] << str[i + 1] << str[i + 2];
136 else
137 result << (unsigned char)(16 * hi + lo);
138
139 // Skip ahead past the escaped value.
140 i += 2;
141 }
142 else
143 // Just copy through.
144 result << str[i];
145 }
146
147 return result.str();
148}
149
Jeff Thompson5a6b5ab2013-08-05 15:43:47 -0700150bool Name::Component::setFromEscapedString(const char *first, const char *last)
Jeff Thompson443398d2013-07-02 19:45:46 -0700151{
152 string trimmedString(first, last);
153 trim(trimmedString);
154 string component = unescape(trimmedString);
155
156 if (component.find_first_not_of(".") == string::npos) {
157 // Special case for component of only periods.
158 if (component.size() <= 2)
159 // Zero, one or two periods is illegal. Ignore this component.
160 return false;
161 else {
162 // Remove 3 periods.
163 value_.clear();
164 value_.insert(value_.begin(), component.begin() + 3, component.end());
165 }
166 }
167 else {
168 value_.clear();
169 value_.insert(value_.begin(), component.begin(), component.end());
170 }
171
172 return true;
173}
174
Jeff Thompson8aac1992013-08-12 17:26:02 -0700175void Name::Component::setSegment(unsigned long segment)
176{
177 value_.clear();
178
179 // First encode in little endian.
180 while (segment != 0) {
181 value_.push_back(segment & 0xff);
182 segment >>= 8;
183 }
184
185 // Append a zero which will become the leading zero when we reverse.
186 value_.push_back(0);
187
188 // Make it big endian.
189 reverse(value_.begin(), value_.end());
190}
191
Jeff Thompson443398d2013-07-02 19:45:46 -0700192Name::Name(const char *uri_cstr)
193{
194 string uri = uri_cstr;
195 trim(uri);
196 if (uri.size() == 0)
197 return;
198
199 size_t iColon = uri.find(':');
200 if (iColon != string::npos) {
201 // Make sure the colon came before a '/'.
202 size_t iFirstSlash = uri.find('/');
203 if (iFirstSlash == string::npos || iColon < iFirstSlash) {
204 // Omit the leading protocol such as ndn:
205 uri.erase(0, iColon + 1);
206 trim(uri);
207 }
208 }
209
210 // Trim the leading slash and possibly the authority.
211 if (uri[0] == '/') {
212 if (uri.size() >= 2 && uri[1] == '/') {
213 // Strip the authority following "//".
214 size_t iAfterAuthority = uri.find('/', 2);
215 if (iAfterAuthority == string::npos)
216 // Unusual case: there was only an authority.
217 return;
218 else {
219 uri.erase(0, iAfterAuthority + 1);
220 trim(uri);
221 }
222 }
223 else {
224 uri.erase(0, 1);
225 trim(uri);
226 }
227 }
228
229 size_t iComponentStart = 0;
230
231 // Unescape the components.
232 while (iComponentStart < uri.size()) {
233 size_t iComponentEnd = uri.find("/", iComponentStart);
234 if (iComponentEnd == string::npos)
235 iComponentEnd = uri.size();
236
Jeff Thompson5a6b5ab2013-08-05 15:43:47 -0700237 components_.push_back(Component());
Jeff Thompson443398d2013-07-02 19:45:46 -0700238 if (!components_[components_.size() - 1].setFromEscapedString(&uri[iComponentStart], &uri[iComponentEnd]))
239 // Ignore the illegal component. This also gets rid of a trailing '/'.
240 components_.pop_back();
241
242 iComponentStart = iComponentEnd + 1;
243 }
244}
245
Jeff Thompsond345a5b2013-07-08 16:18:23 -0700246void Name::get(struct ndn_Name &nameStruct) const
Jeff Thompson48815112013-06-28 18:22:48 -0700247{
Jeff Thompson016ed642013-07-02 14:39:06 -0700248 if (nameStruct.maxComponents < components_.size())
249 throw runtime_error("nameStruct.maxComponents must be >= this name getNComponents()");
250
251 nameStruct.nComponents = components_.size();
252 for (unsigned int i = 0; i < nameStruct.nComponents; ++i)
253 components_[i].get(nameStruct.components[i]);
Jeff Thompson48815112013-06-28 18:22:48 -0700254}
255
Jeff Thompsondd3d2292013-07-10 11:59:44 -0700256void Name::set(const struct ndn_Name &nameStruct)
Jeff Thompsonb468c312013-07-01 17:50:14 -0700257{
258 clear();
Jeff Thompsonccb13c12013-07-01 18:16:00 -0700259 for (unsigned int i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompsonb468c312013-07-01 17:50:14 -0700260 addComponent(nameStruct.components[i].value, nameStruct.components[i].valueLength);
261}
262
Jeff Thompson21844fc2013-08-08 14:52:51 -0700263std::string Name::toUri() const
Jeff Thompsone6063512013-07-01 15:11:28 -0700264{
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700265 if (components_.size() == 0)
266 return "/";
267
268 ostringstream result;
Jeff Thompsonccb13c12013-07-01 18:16:00 -0700269 for (unsigned int i = 0; i < components_.size(); ++i) {
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700270 result << "/";
271 toEscapedString(components_[i].getValue(), result);
Jeff Thompsone6063512013-07-01 15:11:28 -0700272 }
273
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700274 return result.str();
Jeff Thompsone6063512013-07-01 15:11:28 -0700275}
276
Jeff Thompson9c41dfe2013-06-27 12:10:25 -0700277}