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