/src/mozilla-central/netwerk/base/nsURLParsers.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include <string.h> |
7 | | |
8 | | #include "mozilla/RangedPtr.h" |
9 | | #include "mozilla/TextUtils.h" |
10 | | |
11 | | #include "nsURLParsers.h" |
12 | | #include "nsURLHelper.h" |
13 | | #include "nsString.h" |
14 | | |
15 | | using namespace mozilla; |
16 | | |
17 | | //---------------------------------------------------------------------------- |
18 | | |
19 | | static uint32_t |
20 | | CountConsecutiveSlashes(const char *str, int32_t len) |
21 | 1.12M | { |
22 | 1.12M | RangedPtr<const char> p(str, len); |
23 | 1.12M | uint32_t count = 0; |
24 | 3.29M | while (len-- && *p++ == '/') ++count; |
25 | 1.12M | return count; |
26 | 1.12M | } |
27 | | |
28 | | //---------------------------------------------------------------------------- |
29 | | // nsBaseURLParser implementation |
30 | | //---------------------------------------------------------------------------- |
31 | | |
32 | | NS_IMPL_ISUPPORTS(nsAuthURLParser, nsIURLParser) |
33 | | NS_IMPL_ISUPPORTS(nsNoAuthURLParser, nsIURLParser) |
34 | | |
35 | | #define SET_RESULT(component, pos, len) \ |
36 | 21.0M | PR_BEGIN_MACRO \ |
37 | 20.1M | if (component ## Pos) \ |
38 | 20.1M | *component ## Pos = uint32_t(pos); \ |
39 | 20.1M | if (component ## Len) \ |
40 | 20.1M | *component ## Len = int32_t(len); \ |
41 | 20.1M | PR_END_MACRO |
42 | | |
43 | | #define OFFSET_RESULT(component, offset) \ |
44 | 4.39M | PR_BEGIN_MACRO \ |
45 | 4.39M | if (component ## Pos) \ |
46 | 4.39M | *component ## Pos += offset; \ |
47 | 4.39M | PR_END_MACRO |
48 | | |
49 | | NS_IMETHODIMP |
50 | | nsBaseURLParser::ParseURL(const char *spec, int32_t specLen, |
51 | | uint32_t *schemePos, int32_t *schemeLen, |
52 | | uint32_t *authorityPos, int32_t *authorityLen, |
53 | | uint32_t *pathPos, int32_t *pathLen) |
54 | 3.86M | { |
55 | 3.86M | if (NS_WARN_IF(!spec)) { |
56 | 0 | return NS_ERROR_INVALID_POINTER; |
57 | 0 | } |
58 | 3.86M | |
59 | 3.86M | if (specLen < 0) |
60 | 0 | specLen = strlen(spec); |
61 | 3.86M | |
62 | 3.86M | const char *stop = nullptr; |
63 | 3.86M | const char *colon = nullptr; |
64 | 3.86M | const char *slash = nullptr; |
65 | 3.86M | const char *p = spec; |
66 | 3.86M | uint32_t offset = 0; |
67 | 3.86M | int32_t len = specLen; |
68 | 3.86M | |
69 | 3.86M | // skip leading whitespace |
70 | 3.86M | while (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') { |
71 | 0 | spec++; |
72 | 0 | specLen--; |
73 | 0 | offset++; |
74 | 0 |
|
75 | 0 | p++; |
76 | 0 | len--; |
77 | 0 | } |
78 | 3.86M | |
79 | 21.5M | for (; len && *p && !colon && !slash; ++p, --len) { |
80 | 17.6M | switch (*p) { |
81 | 17.6M | case ':': |
82 | 2.81M | if (!colon) |
83 | 2.81M | colon = p; |
84 | 2.81M | break; |
85 | 17.6M | case '/': // start of filepath |
86 | 408k | case '?': // start of query |
87 | 408k | case '#': // start of ref |
88 | 408k | if (!slash) |
89 | 408k | slash = p; |
90 | 408k | break; |
91 | 408k | case '@': // username@hostname |
92 | 25.3k | case '[': // start of IPv6 address literal |
93 | 25.3k | if (!stop) |
94 | 24.9k | stop = p; |
95 | 25.3k | break; |
96 | 17.6M | } |
97 | 17.6M | } |
98 | 3.86M | // disregard the first colon if it follows an '@' or a '[' |
99 | 3.86M | if (colon && stop && colon > stop) |
100 | 336 | colon = nullptr; |
101 | 3.86M | |
102 | 3.86M | // if the spec only contained whitespace ... |
103 | 3.86M | if (specLen == 0) { |
104 | 3.51k | SET_RESULT(scheme, 0, -1); |
105 | 3.51k | SET_RESULT(authority, 0, 0); |
106 | 3.51k | SET_RESULT(path, 0, 0); |
107 | 3.51k | return NS_OK; |
108 | 3.51k | } |
109 | 3.86M | |
110 | 3.86M | // ignore trailing whitespace and control characters |
111 | 3.86M | for (p = spec + specLen - 1; ((unsigned char) *p <= ' ') && (p != spec); --p) |
112 | 0 | ; |
113 | 3.86M | |
114 | 3.86M | specLen = p - spec + 1; |
115 | 3.86M | |
116 | 3.86M | if (colon && (colon < slash || !slash)) { |
117 | 2.80M | // |
118 | 2.80M | // spec = <scheme>:/<the-rest> |
119 | 2.80M | // |
120 | 2.80M | // or |
121 | 2.80M | // |
122 | 2.80M | // spec = <scheme>:<authority> |
123 | 2.80M | // spec = <scheme>:<path-no-slashes> |
124 | 2.80M | // |
125 | 2.80M | if (!net_IsValidScheme(spec, colon - spec) || (*(colon+1) == ':')) { |
126 | 5.77k | return NS_ERROR_MALFORMED_URI; |
127 | 5.77k | } |
128 | 2.80M | SET_RESULT(scheme, offset, colon - spec); |
129 | 2.80M | if (authorityLen || pathLen) { |
130 | 1.12M | uint32_t schemeLen = colon + 1 - spec; |
131 | 1.12M | offset += schemeLen; |
132 | 1.12M | ParseAfterScheme(colon + 1, specLen - schemeLen, |
133 | 1.12M | authorityPos, authorityLen, |
134 | 1.12M | pathPos, pathLen); |
135 | 1.12M | OFFSET_RESULT(authority, offset); |
136 | 1.12M | OFFSET_RESULT(path, offset); |
137 | 1.12M | } |
138 | 2.80M | } |
139 | 1.05M | else { |
140 | 1.05M | // |
141 | 1.05M | // spec = <authority-no-port-or-password>/<path> |
142 | 1.05M | // spec = <path> |
143 | 1.05M | // |
144 | 1.05M | // or |
145 | 1.05M | // |
146 | 1.05M | // spec = <authority-no-port-or-password>/<path-with-colon> |
147 | 1.05M | // spec = <path-with-colon> |
148 | 1.05M | // |
149 | 1.05M | // or |
150 | 1.05M | // |
151 | 1.05M | // spec = <authority-no-port-or-password> |
152 | 1.05M | // spec = <path-no-slashes-or-colon> |
153 | 1.05M | // |
154 | 1.05M | SET_RESULT(scheme, 0, -1); |
155 | 1.05M | if (authorityLen || pathLen) { |
156 | 0 | ParseAfterScheme(spec, specLen, |
157 | 0 | authorityPos, authorityLen, |
158 | 0 | pathPos, pathLen); |
159 | 0 | OFFSET_RESULT(authority, offset); |
160 | 0 | OFFSET_RESULT(path, offset); |
161 | 0 | } |
162 | 1.05M | } |
163 | 3.86M | return NS_OK; |
164 | 3.86M | } |
165 | | |
166 | | NS_IMETHODIMP |
167 | | nsBaseURLParser::ParseAuthority(const char *auth, int32_t authLen, |
168 | | uint32_t *usernamePos, int32_t *usernameLen, |
169 | | uint32_t *passwordPos, int32_t *passwordLen, |
170 | | uint32_t *hostnamePos, int32_t *hostnameLen, |
171 | | int32_t *port) |
172 | 0 | { |
173 | 0 | if (NS_WARN_IF(!auth)) { |
174 | 0 | return NS_ERROR_INVALID_POINTER; |
175 | 0 | } |
176 | 0 | |
177 | 0 | if (authLen < 0) |
178 | 0 | authLen = strlen(auth); |
179 | 0 |
|
180 | 0 | SET_RESULT(username, 0, -1); |
181 | 0 | SET_RESULT(password, 0, -1); |
182 | 0 | SET_RESULT(hostname, 0, authLen); |
183 | 0 | if (port) |
184 | 0 | *port = -1; |
185 | 0 | return NS_OK; |
186 | 0 | } |
187 | | |
188 | | NS_IMETHODIMP |
189 | | nsBaseURLParser::ParseUserInfo(const char *userinfo, int32_t userinfoLen, |
190 | | uint32_t *usernamePos, int32_t *usernameLen, |
191 | | uint32_t *passwordPos, int32_t *passwordLen) |
192 | 0 | { |
193 | 0 | SET_RESULT(username, 0, -1); |
194 | 0 | SET_RESULT(password, 0, -1); |
195 | 0 | return NS_OK; |
196 | 0 | } |
197 | | |
198 | | NS_IMETHODIMP |
199 | | nsBaseURLParser::ParseServerInfo(const char *serverinfo, int32_t serverinfoLen, |
200 | | uint32_t *hostnamePos, int32_t *hostnameLen, |
201 | | int32_t *port) |
202 | 0 | { |
203 | 0 | SET_RESULT(hostname, 0, -1); |
204 | 0 | if (port) |
205 | 0 | *port = -1; |
206 | 0 | return NS_OK; |
207 | 0 | } |
208 | | |
209 | | NS_IMETHODIMP |
210 | | nsBaseURLParser::ParsePath(const char *path, int32_t pathLen, |
211 | | uint32_t *filepathPos, int32_t *filepathLen, |
212 | | uint32_t *queryPos, int32_t *queryLen, |
213 | | uint32_t *refPos, int32_t *refLen) |
214 | 1.07M | { |
215 | 1.07M | if (NS_WARN_IF(!path)) { |
216 | 0 | return NS_ERROR_INVALID_POINTER; |
217 | 0 | } |
218 | 1.07M | |
219 | 1.07M | if (pathLen < 0) |
220 | 0 | pathLen = strlen(path); |
221 | 1.07M | |
222 | 1.07M | // path = [/]<segment1>/<segment2>/<...>/<segmentN>?<query>#<ref> |
223 | 1.07M | |
224 | 1.07M | // XXX PL_strnpbrk would be nice, but it's buggy |
225 | 1.07M | |
226 | 1.07M | // search for first occurrence of either ? or # |
227 | 1.07M | const char *query_beg = nullptr, *query_end = nullptr; |
228 | 1.07M | const char *ref_beg = nullptr; |
229 | 1.07M | const char *p = nullptr; |
230 | 9.32M | for (p = path; p < path + pathLen; ++p) { |
231 | 8.26M | // only match the query string if it precedes the reference fragment |
232 | 8.26M | if (!ref_beg && !query_beg && *p == '?') |
233 | 120k | query_beg = p + 1; |
234 | 8.14M | else if (*p == '#') { |
235 | 4.95k | ref_beg = p + 1; |
236 | 4.95k | if (query_beg) |
237 | 276 | query_end = p; |
238 | 4.95k | break; |
239 | 4.95k | } |
240 | 8.26M | } |
241 | 1.07M | |
242 | 1.07M | if (query_beg) { |
243 | 120k | if (query_end) |
244 | 120k | SET_RESULT(query, query_beg - path, query_end - query_beg); |
245 | 120k | else |
246 | 120k | SET_RESULT(query, query_beg - path, pathLen - (query_beg - path)); |
247 | 120k | } |
248 | 1.07M | else |
249 | 1.07M | SET_RESULT(query, 0, -1); |
250 | 1.07M | |
251 | 1.07M | if (ref_beg) |
252 | 1.07M | SET_RESULT(ref, ref_beg - path, pathLen - (ref_beg - path)); |
253 | 1.07M | else |
254 | 1.07M | SET_RESULT(ref, 0, -1); |
255 | 1.07M | |
256 | 1.07M | const char *end; |
257 | 1.07M | if (query_beg) |
258 | 120k | end = query_beg - 1; |
259 | 954k | else if (ref_beg) |
260 | 4.67k | end = ref_beg - 1; |
261 | 949k | else |
262 | 949k | end = path + pathLen; |
263 | 1.07M | |
264 | 1.07M | // an empty file path is no file path |
265 | 1.07M | if (end != path) |
266 | 1.07M | SET_RESULT(filepath, 0, end - path); |
267 | 1.07M | else |
268 | 1.07M | SET_RESULT(filepath, 0, -1); |
269 | 1.07M | return NS_OK; |
270 | 1.07M | } |
271 | | |
272 | | NS_IMETHODIMP |
273 | | nsBaseURLParser::ParseFilePath(const char *filepath, int32_t filepathLen, |
274 | | uint32_t *directoryPos, int32_t *directoryLen, |
275 | | uint32_t *basenamePos, int32_t *basenameLen, |
276 | | uint32_t *extensionPos, int32_t *extensionLen) |
277 | 1.07M | { |
278 | 1.07M | if (NS_WARN_IF(!filepath)) { |
279 | 0 | return NS_ERROR_INVALID_POINTER; |
280 | 0 | } |
281 | 1.07M | |
282 | 1.07M | if (filepathLen < 0) |
283 | 0 | filepathLen = strlen(filepath); |
284 | 1.07M | |
285 | 1.07M | if (filepathLen == 0) { |
286 | 0 | SET_RESULT(directory, 0, -1); |
287 | 0 | SET_RESULT(basename, 0, 0); // assume a zero length file basename |
288 | 0 | SET_RESULT(extension, 0, -1); |
289 | 0 | return NS_OK; |
290 | 0 | } |
291 | 1.07M | |
292 | 1.07M | const char *p; |
293 | 1.07M | const char *end = filepath + filepathLen; |
294 | 1.07M | |
295 | 1.07M | // search backwards for filename |
296 | 6.85M | for (p = end - 1; *p != '/' && p > filepath; --p) |
297 | 5.78M | ; |
298 | 1.07M | if (*p == '/') { |
299 | 1.06M | // catch /.. and /. |
300 | 1.06M | if ((p+1 < end && *(p+1) == '.') && |
301 | 1.06M | (p+2 == end || (*(p+2) == '.' && p+3 == end))) |
302 | 653 | p = end - 1; |
303 | 1.06M | // filepath = <directory><filename>.<extension> |
304 | 1.06M | SET_RESULT(directory, 0, p - filepath + 1); |
305 | 1.06M | ParseFileName(p + 1, end - (p + 1), |
306 | 1.06M | basenamePos, basenameLen, |
307 | 1.06M | extensionPos, extensionLen); |
308 | 1.06M | OFFSET_RESULT(basename, p + 1 - filepath); |
309 | 1.06M | OFFSET_RESULT(extension, p + 1 - filepath); |
310 | 1.06M | } |
311 | 1.20k | else { |
312 | 1.20k | // filepath = <filename>.<extension> |
313 | 1.20k | SET_RESULT(directory, 0, -1); |
314 | 1.20k | ParseFileName(filepath, filepathLen, |
315 | 1.20k | basenamePos, basenameLen, |
316 | 1.20k | extensionPos, extensionLen); |
317 | 1.20k | } |
318 | 1.07M | return NS_OK; |
319 | 1.07M | } |
320 | | |
321 | | nsresult |
322 | | nsBaseURLParser::ParseFileName(const char *filename, int32_t filenameLen, |
323 | | uint32_t *basenamePos, int32_t *basenameLen, |
324 | | uint32_t *extensionPos, int32_t *extensionLen) |
325 | 1.07M | { |
326 | 1.07M | if (NS_WARN_IF(!filename)) { |
327 | 0 | return NS_ERROR_INVALID_POINTER; |
328 | 0 | } |
329 | 1.07M | |
330 | 1.07M | if (filenameLen < 0) |
331 | 0 | filenameLen = strlen(filename); |
332 | 1.07M | |
333 | 1.07M | // no extension if filename ends with a '.' |
334 | 1.07M | if (filename[filenameLen-1] != '.') { |
335 | 1.06M | // ignore '.' at the beginning |
336 | 5.83M | for (const char *p = filename + filenameLen - 1; p > filename; --p) { |
337 | 4.76M | if (*p == '.') { |
338 | 3.05k | // filename = <basename.extension> |
339 | 3.05k | SET_RESULT(basename, 0, p - filename); |
340 | 3.05k | SET_RESULT(extension, p + 1 - filename, filenameLen - (p - filename + 1)); |
341 | 3.05k | return NS_OK; |
342 | 3.05k | } |
343 | 4.76M | } |
344 | 1.06M | } |
345 | 1.07M | // filename = <basename> |
346 | 1.07M | SET_RESULT(basename, 0, filenameLen); |
347 | 1.06M | SET_RESULT(extension, 0, -1); |
348 | 1.06M | return NS_OK; |
349 | 1.07M | } |
350 | | |
351 | | //---------------------------------------------------------------------------- |
352 | | // nsNoAuthURLParser implementation |
353 | | //---------------------------------------------------------------------------- |
354 | | |
355 | | NS_IMETHODIMP |
356 | | nsNoAuthURLParser::ParseAuthority(const char *auth, int32_t authLen, |
357 | | uint32_t *usernamePos, int32_t *usernameLen, |
358 | | uint32_t *passwordPos, int32_t *passwordLen, |
359 | | uint32_t *hostnamePos, int32_t *hostnameLen, |
360 | | int32_t *port) |
361 | 0 | { |
362 | 0 | MOZ_ASSERT_UNREACHABLE("Shouldn't parse auth in a NoAuthURL!"); |
363 | 0 | return NS_ERROR_UNEXPECTED; |
364 | 0 | } |
365 | | |
366 | | void |
367 | | nsNoAuthURLParser::ParseAfterScheme(const char *spec, int32_t specLen, |
368 | | uint32_t *authPos, int32_t *authLen, |
369 | | uint32_t *pathPos, int32_t *pathLen) |
370 | 7.01k | { |
371 | 7.01k | MOZ_ASSERT(specLen >= 0, "unexpected"); |
372 | 7.01k | |
373 | 7.01k | // everything is the path |
374 | 7.01k | uint32_t pos = 0; |
375 | 7.01k | switch (CountConsecutiveSlashes(spec, specLen)) { |
376 | 7.01k | case 0: |
377 | 2.38k | case 1: |
378 | 2.38k | break; |
379 | 2.38k | case 2: |
380 | 2.25k | { |
381 | 2.25k | const char *p = nullptr; |
382 | 2.25k | if (specLen > 2) { |
383 | 2.09k | // looks like there is an authority section |
384 | 2.09k | |
385 | 2.09k | // if the authority looks like a drive number then we |
386 | 2.09k | // really want to treat it as part of the path |
387 | 2.09k | // [a-zA-Z][:|]{/\} |
388 | 2.09k | // i.e one of: c: c:\foo c:/foo c| c|\foo c|/foo |
389 | 2.09k | if ((specLen > 3) && (spec[3] == ':' || spec[3] == '|') && |
390 | 2.09k | IsAsciiAlpha(spec[2]) && |
391 | 2.09k | ((specLen == 4) || (spec[4] == '/') || (spec[4] == '\\'))) { |
392 | 445 | pos = 1; |
393 | 445 | break; |
394 | 445 | } |
395 | 1.64k | // Ignore apparent authority; path is everything after it |
396 | 402k | for (p = spec + 2; p < spec + specLen; ++p) { |
397 | 401k | if (*p == '/' || *p == '?' || *p == '#') |
398 | 379 | break; |
399 | 401k | } |
400 | 1.64k | } |
401 | 2.25k | SET_RESULT(auth, 0, -1); |
402 | 1.80k | if (p && p != spec+specLen) |
403 | 1.80k | SET_RESULT(path, p - spec, specLen - (p - spec)); |
404 | 1.80k | else |
405 | 1.80k | SET_RESULT(path, 0, -1); |
406 | 1.80k | return; |
407 | 2.25k | } |
408 | 2.37k | default: |
409 | 2.37k | pos = 2; |
410 | 2.37k | break; |
411 | 5.20k | } |
412 | 5.20k | SET_RESULT(auth, pos, 0); |
413 | 5.20k | SET_RESULT(path, pos, specLen - pos); |
414 | 5.20k | } |
415 | | |
416 | | #if defined(XP_WIN) |
417 | | NS_IMETHODIMP |
418 | | nsNoAuthURLParser::ParseFilePath(const char *filepath, int32_t filepathLen, |
419 | | uint32_t *directoryPos, int32_t *directoryLen, |
420 | | uint32_t *basenamePos, int32_t *basenameLen, |
421 | | uint32_t *extensionPos, int32_t *extensionLen) |
422 | | { |
423 | | if (NS_WARN_IF(!filepath)) { |
424 | | return NS_ERROR_INVALID_POINTER; |
425 | | } |
426 | | |
427 | | if (filepathLen < 0) |
428 | | filepathLen = strlen(filepath); |
429 | | |
430 | | // look for a filepath consisting of only a drive number, which may or |
431 | | // may not have a leading slash. |
432 | | if (filepathLen > 1 && filepathLen < 4) { |
433 | | const char *end = filepath + filepathLen; |
434 | | const char *p = filepath; |
435 | | if (*p == '/') |
436 | | p++; |
437 | | if ((end-p == 2) && (p[1]==':' || p[1]=='|') && IsAsciiAlpha(*p)) { |
438 | | // filepath = <drive-number>: |
439 | | SET_RESULT(directory, 0, filepathLen); |
440 | | SET_RESULT(basename, 0, -1); |
441 | | SET_RESULT(extension, 0, -1); |
442 | | return NS_OK; |
443 | | } |
444 | | } |
445 | | |
446 | | // otherwise fallback on common implementation |
447 | | return nsBaseURLParser::ParseFilePath(filepath, filepathLen, |
448 | | directoryPos, directoryLen, |
449 | | basenamePos, basenameLen, |
450 | | extensionPos, extensionLen); |
451 | | } |
452 | | #endif |
453 | | |
454 | | //---------------------------------------------------------------------------- |
455 | | // nsAuthURLParser implementation |
456 | | //---------------------------------------------------------------------------- |
457 | | |
458 | | NS_IMETHODIMP |
459 | | nsAuthURLParser::ParseAuthority(const char *auth, int32_t authLen, |
460 | | uint32_t *usernamePos, int32_t *usernameLen, |
461 | | uint32_t *passwordPos, int32_t *passwordLen, |
462 | | uint32_t *hostnamePos, int32_t *hostnameLen, |
463 | | int32_t *port) |
464 | 1.09M | { |
465 | 1.09M | nsresult rv; |
466 | 1.09M | |
467 | 1.09M | if (NS_WARN_IF(!auth)) { |
468 | 0 | return NS_ERROR_INVALID_POINTER; |
469 | 0 | } |
470 | 1.09M | |
471 | 1.09M | if (authLen < 0) |
472 | 0 | authLen = strlen(auth); |
473 | 1.09M | |
474 | 1.09M | if (authLen == 0) { |
475 | 0 | SET_RESULT(username, 0, -1); |
476 | 0 | SET_RESULT(password, 0, -1); |
477 | 0 | SET_RESULT(hostname, 0, 0); |
478 | 0 | if (port) |
479 | 0 | *port = -1; |
480 | 0 | return NS_OK; |
481 | 0 | } |
482 | 1.09M | |
483 | 1.09M | // search backwards for @ |
484 | 1.09M | const char *p = auth + authLen - 1; |
485 | 13.8M | for (; (*p != '@') && (p > auth); --p) { |
486 | 12.7M | } |
487 | 1.09M | if ( *p == '@' ) { |
488 | 4.35k | // auth = <user-info@server-info> |
489 | 4.35k | rv = ParseUserInfo(auth, p - auth, |
490 | 4.35k | usernamePos, usernameLen, |
491 | 4.35k | passwordPos, passwordLen); |
492 | 4.35k | if (NS_FAILED(rv)) return rv; |
493 | 4.06k | rv = ParseServerInfo(p + 1, authLen - (p - auth + 1), |
494 | 4.06k | hostnamePos, hostnameLen, |
495 | 4.06k | port); |
496 | 4.06k | if (NS_FAILED(rv)) return rv; |
497 | 3.35k | OFFSET_RESULT(hostname, p + 1 - auth); |
498 | 3.35k | |
499 | 3.35k | // malformed if has a username or password |
500 | 3.35k | // but no host info, such as: http://u:p@/ |
501 | 3.35k | if ((usernamePos || passwordPos) && (!hostnamePos || !*hostnameLen)) { |
502 | 341 | return NS_ERROR_MALFORMED_URI; |
503 | 341 | } |
504 | 1.08M | } |
505 | 1.08M | else { |
506 | 1.08M | // auth = <server-info> |
507 | 1.08M | SET_RESULT(username, 0, -1); |
508 | 1.08M | SET_RESULT(password, 0, -1); |
509 | 1.08M | rv = ParseServerInfo(auth, authLen, |
510 | 1.08M | hostnamePos, hostnameLen, |
511 | 1.08M | port); |
512 | 1.08M | if (NS_FAILED(rv)) return rv; |
513 | 1.08M | } |
514 | 1.08M | return NS_OK; |
515 | 1.08M | } |
516 | | |
517 | | NS_IMETHODIMP |
518 | | nsAuthURLParser::ParseUserInfo(const char *userinfo, int32_t userinfoLen, |
519 | | uint32_t *usernamePos, int32_t *usernameLen, |
520 | | uint32_t *passwordPos, int32_t *passwordLen) |
521 | 4.35k | { |
522 | 4.35k | if (NS_WARN_IF(!userinfo)) { |
523 | 0 | return NS_ERROR_INVALID_POINTER; |
524 | 0 | } |
525 | 4.35k | |
526 | 4.35k | if (userinfoLen < 0) |
527 | 0 | userinfoLen = strlen(userinfo); |
528 | 4.35k | |
529 | 4.35k | if (userinfoLen == 0) { |
530 | 886 | SET_RESULT(username, 0, -1); |
531 | 886 | SET_RESULT(password, 0, -1); |
532 | 886 | return NS_OK; |
533 | 886 | } |
534 | 3.47k | |
535 | 3.47k | const char *p = (const char *) memchr(userinfo, ':', userinfoLen); |
536 | 3.47k | if (p) { |
537 | 1.87k | // userinfo = <username:password> |
538 | 1.87k | if (p == userinfo) { |
539 | 295 | // must have a username! |
540 | 295 | return NS_ERROR_MALFORMED_URI; |
541 | 295 | } |
542 | 1.58k | SET_RESULT(username, 0, p - userinfo); |
543 | 1.58k | SET_RESULT(password, p - userinfo + 1, userinfoLen - (p - userinfo + 1)); |
544 | 1.58k | } |
545 | 1.59k | else { |
546 | 1.59k | // userinfo = <username> |
547 | 1.59k | SET_RESULT(username, 0, userinfoLen); |
548 | 1.59k | SET_RESULT(password, 0, -1); |
549 | 1.59k | } |
550 | 3.47k | return NS_OK; |
551 | 3.47k | } |
552 | | |
553 | | NS_IMETHODIMP |
554 | | nsAuthURLParser::ParseServerInfo(const char *serverinfo, int32_t serverinfoLen, |
555 | | uint32_t *hostnamePos, int32_t *hostnameLen, |
556 | | int32_t *port) |
557 | 1.09M | { |
558 | 1.09M | if (NS_WARN_IF(!serverinfo)) { |
559 | 0 | return NS_ERROR_INVALID_POINTER; |
560 | 0 | } |
561 | 1.09M | |
562 | 1.09M | if (serverinfoLen < 0) |
563 | 0 | serverinfoLen = strlen(serverinfo); |
564 | 1.09M | |
565 | 1.09M | if (serverinfoLen == 0) { |
566 | 341 | SET_RESULT(hostname, 0, 0); |
567 | 341 | if (port) |
568 | 341 | *port = -1; |
569 | 341 | return NS_OK; |
570 | 341 | } |
571 | 1.09M | |
572 | 1.09M | // search backwards for a ':' but stop on ']' (IPv6 address literal |
573 | 1.09M | // delimiter). check for illegal characters in the hostname. |
574 | 1.09M | const char *p = serverinfo + serverinfoLen - 1; |
575 | 1.09M | const char *colon = nullptr, *bracket = nullptr; |
576 | 13.7M | for (; p > serverinfo; --p) { |
577 | 12.7M | switch (*p) { |
578 | 12.7M | case ']': |
579 | 5.23k | bracket = p; |
580 | 5.23k | break; |
581 | 12.7M | case ':': |
582 | 7.46k | if (bracket == nullptr) |
583 | 4.38k | colon = p; |
584 | 7.46k | break; |
585 | 12.7M | case ' ': |
586 | 0 | // hostname must not contain a space |
587 | 0 | return NS_ERROR_MALFORMED_URI; |
588 | 12.7M | } |
589 | 12.7M | } |
590 | 1.09M | |
591 | 1.09M | if (colon) { |
592 | 3.97k | // serverinfo = <hostname:port> |
593 | 3.97k | SET_RESULT(hostname, 0, colon - serverinfo); |
594 | 3.97k | if (port) { |
595 | 3.97k | // XXX unfortunately ToInteger is not defined for substrings |
596 | 3.97k | nsAutoCString buf(colon+1, serverinfoLen - (colon + 1 - serverinfo)); |
597 | 3.97k | if (buf.Length() == 0) { |
598 | 797 | *port = -1; |
599 | 797 | } |
600 | 3.17k | else { |
601 | 3.17k | const char* nondigit = NS_strspnp("0123456789", buf.get()); |
602 | 3.17k | if (nondigit && *nondigit) |
603 | 1.91k | return NS_ERROR_MALFORMED_URI; |
604 | 1.25k | |
605 | 1.25k | nsresult err; |
606 | 1.25k | *port = buf.ToInteger(&err); |
607 | 1.25k | if (NS_FAILED(err) || *port < 0 || *port > std::numeric_limits<uint16_t>::max()) |
608 | 851 | return NS_ERROR_MALFORMED_URI; |
609 | 1.08M | } |
610 | 3.97k | } |
611 | 1.08M | } |
612 | 1.08M | else { |
613 | 1.08M | // serverinfo = <hostname> |
614 | 1.08M | SET_RESULT(hostname, 0, serverinfoLen); |
615 | 1.08M | if (port) |
616 | 1.08M | *port = -1; |
617 | 1.08M | } |
618 | 1.09M | |
619 | 1.09M | // In case of IPv6 address check its validity |
620 | 1.09M | if (*hostnameLen > 1 && *(serverinfo + *hostnamePos) == '[' && |
621 | 1.09M | *(serverinfo + *hostnamePos + *hostnameLen - 1) == ']' && |
622 | 1.09M | !net_IsValidIPv6Addr(serverinfo + *hostnamePos + 1, *hostnameLen - 2)) |
623 | 2.85k | return NS_ERROR_MALFORMED_URI; |
624 | 1.08M | |
625 | 1.08M | return NS_OK; |
626 | 1.08M | } |
627 | | |
628 | | void |
629 | | nsAuthURLParser::ParseAfterScheme(const char *spec, int32_t specLen, |
630 | | uint32_t *authPos, int32_t *authLen, |
631 | | uint32_t *pathPos, int32_t *pathLen) |
632 | 1.08M | { |
633 | 1.08M | MOZ_ASSERT(specLen >= 0, "unexpected"); |
634 | 1.08M | |
635 | 1.08M | uint32_t nslash = CountConsecutiveSlashes(spec, specLen); |
636 | 1.08M | |
637 | 1.08M | // search for the end of the authority section |
638 | 1.08M | const char *end = spec + specLen; |
639 | 1.08M | const char *p; |
640 | 15.2M | for (p = spec + nslash; p < end; ++p) { |
641 | 15.1M | if (*p == '/' || *p == '?' || *p == '#') |
642 | 1.04M | break; |
643 | 15.1M | } |
644 | 1.08M | if (p < end) { |
645 | 1.04M | // spec = [/]<auth><path> |
646 | 1.04M | SET_RESULT(auth, nslash, p - (spec + nslash)); |
647 | 1.04M | SET_RESULT(path, p - spec, specLen - (p - spec)); |
648 | 1.04M | } |
649 | 45.9k | else { |
650 | 45.9k | // spec = [/]<auth> |
651 | 45.9k | SET_RESULT(auth, nslash, specLen - nslash); |
652 | 45.9k | SET_RESULT(path, 0, -1); |
653 | 45.9k | } |
654 | 1.08M | } |
655 | | |
656 | | //---------------------------------------------------------------------------- |
657 | | // nsStdURLParser implementation |
658 | | //---------------------------------------------------------------------------- |
659 | | |
660 | | void |
661 | | nsStdURLParser::ParseAfterScheme(const char *spec, int32_t specLen, |
662 | | uint32_t *authPos, int32_t *authLen, |
663 | | uint32_t *pathPos, int32_t *pathLen) |
664 | 30.8k | { |
665 | 30.8k | MOZ_ASSERT(specLen >= 0, "unexpected"); |
666 | 30.8k | |
667 | 30.8k | uint32_t nslash = CountConsecutiveSlashes(spec, specLen); |
668 | 30.8k | |
669 | 30.8k | // search for the end of the authority section |
670 | 30.8k | const char *end = spec + specLen; |
671 | 30.8k | const char *p; |
672 | 414k | for (p = spec + nslash; p < end; ++p) { |
673 | 403k | if (strchr("/?#;", *p)) |
674 | 19.8k | break; |
675 | 403k | } |
676 | 30.8k | switch (nslash) { |
677 | 30.8k | case 0: |
678 | 12.6k | case 2: |
679 | 12.6k | if (p < end) { |
680 | 8.17k | // spec = (//)<auth><path> |
681 | 8.17k | SET_RESULT(auth, nslash, p - (spec + nslash)); |
682 | 8.17k | SET_RESULT(path, p - spec, specLen - (p - spec)); |
683 | 8.17k | } |
684 | 4.51k | else { |
685 | 4.51k | // spec = (//)<auth> |
686 | 4.51k | SET_RESULT(auth, nslash, specLen - nslash); |
687 | 4.51k | SET_RESULT(path, 0, -1); |
688 | 4.51k | } |
689 | 12.6k | break; |
690 | 17.9k | case 1: |
691 | 17.9k | // spec = /<path> |
692 | 17.9k | SET_RESULT(auth, 0, -1); |
693 | 17.9k | SET_RESULT(path, 0, specLen); |
694 | 17.9k | break; |
695 | 12.6k | default: |
696 | 173 | // spec = ///[/]<path> |
697 | 173 | SET_RESULT(auth, 2, 0); |
698 | 173 | SET_RESULT(path, 2, specLen - 2); |
699 | 30.8k | } |
700 | 30.8k | } |