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