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