blob: 6098400f9263ce3bcac708f94a09db92058bf139 [file] [log] [blame]
Jeff Thompson9c41dfe2013-06-27 12:10:25 -07001/*
2 * Author: Jeff Thompson
3 *
4 * BSD license, See the LICENSE file for more information.
5 */
6
Jeff Thompson54909772013-07-07 22:38:57 -07007#include <stdexcept>
Jeff Thompsone6063512013-07-01 15:11:28 -07008#include <sstream>
Jeff Thompson9c41dfe2013-06-27 12:10:25 -07009#include "Name.hpp"
10
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)
34 result << ".";
35 }
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 {
48 result << "%";
49 if (x < 16)
50 result << "0";
51 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 Thompson443398d2013-07-02 19:45:46 -0700151bool NameComponent::setFromEscapedString(const char *first, const char *last)
152{
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
176Name::Name(const char *uri_cstr)
177{
178 string uri = uri_cstr;
179 trim(uri);
180 if (uri.size() == 0)
181 return;
182
183 size_t iColon = uri.find(':');
184 if (iColon != string::npos) {
185 // Make sure the colon came before a '/'.
186 size_t iFirstSlash = uri.find('/');
187 if (iFirstSlash == string::npos || iColon < iFirstSlash) {
188 // Omit the leading protocol such as ndn:
189 uri.erase(0, iColon + 1);
190 trim(uri);
191 }
192 }
193
194 // Trim the leading slash and possibly the authority.
195 if (uri[0] == '/') {
196 if (uri.size() >= 2 && uri[1] == '/') {
197 // Strip the authority following "//".
198 size_t iAfterAuthority = uri.find('/', 2);
199 if (iAfterAuthority == string::npos)
200 // Unusual case: there was only an authority.
201 return;
202 else {
203 uri.erase(0, iAfterAuthority + 1);
204 trim(uri);
205 }
206 }
207 else {
208 uri.erase(0, 1);
209 trim(uri);
210 }
211 }
212
213 size_t iComponentStart = 0;
214
215 // Unescape the components.
216 while (iComponentStart < uri.size()) {
217 size_t iComponentEnd = uri.find("/", iComponentStart);
218 if (iComponentEnd == string::npos)
219 iComponentEnd = uri.size();
220
221 components_.push_back(NameComponent());
222 if (!components_[components_.size() - 1].setFromEscapedString(&uri[iComponentStart], &uri[iComponentEnd]))
223 // Ignore the illegal component. This also gets rid of a trailing '/'.
224 components_.pop_back();
225
226 iComponentStart = iComponentEnd + 1;
227 }
228}
229
Jeff Thompson016ed642013-07-02 14:39:06 -0700230void Name::get(struct ndn_Name &nameStruct)
Jeff Thompson48815112013-06-28 18:22:48 -0700231{
Jeff Thompson016ed642013-07-02 14:39:06 -0700232 if (nameStruct.maxComponents < components_.size())
233 throw runtime_error("nameStruct.maxComponents must be >= this name getNComponents()");
234
235 nameStruct.nComponents = components_.size();
236 for (unsigned int i = 0; i < nameStruct.nComponents; ++i)
237 components_[i].get(nameStruct.components[i]);
Jeff Thompson48815112013-06-28 18:22:48 -0700238}
239
Jeff Thompsonb468c312013-07-01 17:50:14 -0700240void Name::set(struct ndn_Name &nameStruct)
241{
242 clear();
Jeff Thompsonccb13c12013-07-01 18:16:00 -0700243 for (unsigned int i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompsonb468c312013-07-01 17:50:14 -0700244 addComponent(nameStruct.components[i].value, nameStruct.components[i].valueLength);
245}
246
Jeff Thompsone6063512013-07-01 15:11:28 -0700247std::string Name::to_uri()
248{
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700249 if (components_.size() == 0)
250 return "/";
251
252 ostringstream result;
Jeff Thompsonccb13c12013-07-01 18:16:00 -0700253 for (unsigned int i = 0; i < components_.size(); ++i) {
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700254 result << "/";
255 toEscapedString(components_[i].getValue(), result);
Jeff Thompsone6063512013-07-01 15:11:28 -0700256 }
257
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700258 return result.str();
Jeff Thompsone6063512013-07-01 15:11:28 -0700259}
260
Jeff Thompson9c41dfe2013-06-27 12:10:25 -0700261}