Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=4 sw=2 sts=2 et cindent: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/RangedPtr.h"
8
#include "mozilla/TextUtils.h"
9
10
#include <algorithm>
11
#include <iterator>
12
13
#include "nsASCIIMask.h"
14
#include "nsURLHelper.h"
15
#include "nsIFile.h"
16
#include "nsIURLParser.h"
17
#include "nsCOMPtr.h"
18
#include "nsCRT.h"
19
#include "nsNetCID.h"
20
#include "mozilla/Preferences.h"
21
#include "prnetdb.h"
22
#include "mozilla/Tokenizer.h"
23
#include "nsEscape.h"
24
#include "rust-helper/src/helper.h"
25
26
using namespace mozilla;
27
28
//----------------------------------------------------------------------------
29
// Init/Shutdown
30
//----------------------------------------------------------------------------
31
32
static bool gInitialized = false;
33
static nsIURLParser* gNoAuthURLParser = nullptr;
34
static nsIURLParser* gAuthURLParser = nullptr;
35
static nsIURLParser* gStdURLParser = nullptr;
36
static int32_t gMaxLength = 1048576; // Default: 1MB
37
38
static void InitGlobals() {
39
nsCOMPtr<nsIURLParser> parser;
40
41
parser = do_GetService(NS_NOAUTHURLPARSER_CONTRACTID);
42
NS_ASSERTION(parser, "failed getting 'noauth' url parser");
43
if (parser) {
44
gNoAuthURLParser = parser.get();
45
NS_ADDREF(gNoAuthURLParser);
46
}
47
48
parser = do_GetService(NS_AUTHURLPARSER_CONTRACTID);
49
NS_ASSERTION(parser, "failed getting 'auth' url parser");
50
if (parser) {
51
gAuthURLParser = parser.get();
52
NS_ADDREF(gAuthURLParser);
53
}
54
55
parser = do_GetService(NS_STDURLPARSER_CONTRACTID);
56
NS_ASSERTION(parser, "failed getting 'std' url parser");
57
if (parser) {
58
gStdURLParser = parser.get();
59
NS_ADDREF(gStdURLParser);
60
}
61
62
gInitialized = true;
63
Preferences::AddIntVarCache(&gMaxLength, "network.standard-url.max-length",
64
1048576);
65
}
66
67
void net_ShutdownURLHelper() {
68
if (gInitialized) {
69
NS_IF_RELEASE(gNoAuthURLParser);
70
NS_IF_RELEASE(gAuthURLParser);
71
NS_IF_RELEASE(gStdURLParser);
72
gInitialized = false;
73
}
74
}
75
76
int32_t net_GetURLMaxLength() { return gMaxLength; }
77
78
//----------------------------------------------------------------------------
79
// nsIURLParser getters
80
//----------------------------------------------------------------------------
81
82
nsIURLParser* net_GetAuthURLParser() {
83
if (!gInitialized) InitGlobals();
84
return gAuthURLParser;
85
}
86
87
nsIURLParser* net_GetNoAuthURLParser() {
88
if (!gInitialized) InitGlobals();
89
return gNoAuthURLParser;
90
}
91
92
nsIURLParser* net_GetStdURLParser() {
93
if (!gInitialized) InitGlobals();
94
return gStdURLParser;
95
}
96
97
//---------------------------------------------------------------------------
98
// GetFileFromURLSpec implementations
99
//---------------------------------------------------------------------------
100
nsresult net_GetURLSpecFromDir(nsIFile* aFile, nsACString& result) {
101
nsAutoCString escPath;
102
nsresult rv = net_GetURLSpecFromActualFile(aFile, escPath);
103
if (NS_FAILED(rv)) return rv;
104
105
if (escPath.Last() != '/') {
106
escPath += '/';
107
}
108
109
result = escPath;
110
return NS_OK;
111
}
112
113
nsresult net_GetURLSpecFromFile(nsIFile* aFile, nsACString& result) {
114
nsAutoCString escPath;
115
nsresult rv = net_GetURLSpecFromActualFile(aFile, escPath);
116
if (NS_FAILED(rv)) return rv;
117
118
// if this file references a directory, then we need to ensure that the
119
// URL ends with a slash. this is important since it affects the rules
120
// for relative URL resolution when this URL is used as a base URL.
121
// if the file does not exist, then we make no assumption about its type,
122
// and simply leave the URL unmodified.
123
if (escPath.Last() != '/') {
124
bool dir;
125
rv = aFile->IsDirectory(&dir);
126
if (NS_SUCCEEDED(rv) && dir) escPath += '/';
127
}
128
129
result = escPath;
130
return NS_OK;
131
}
132
133
//----------------------------------------------------------------------------
134
// file:// URL parsing
135
//----------------------------------------------------------------------------
136
137
nsresult net_ParseFileURL(const nsACString& inURL, nsACString& outDirectory,
138
nsACString& outFileBaseName,
139
nsACString& outFileExtension) {
140
nsresult rv;
141
142
if (inURL.Length() > (uint32_t)gMaxLength) {
143
return NS_ERROR_MALFORMED_URI;
144
}
145
146
outDirectory.Truncate();
147
outFileBaseName.Truncate();
148
outFileExtension.Truncate();
149
150
const nsPromiseFlatCString& flatURL = PromiseFlatCString(inURL);
151
const char* url = flatURL.get();
152
153
nsAutoCString scheme;
154
rv = net_ExtractURLScheme(flatURL, scheme);
155
if (NS_FAILED(rv)) return rv;
156
157
if (!scheme.EqualsLiteral("file")) {
158
NS_ERROR("must be a file:// url");
159
return NS_ERROR_UNEXPECTED;
160
}
161
162
nsIURLParser* parser = net_GetNoAuthURLParser();
163
NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
164
165
uint32_t pathPos, filepathPos, directoryPos, basenamePos, extensionPos;
166
int32_t pathLen, filepathLen, directoryLen, basenameLen, extensionLen;
167
168
// invoke the parser to extract the URL path
169
rv = parser->ParseURL(url, flatURL.Length(), nullptr,
170
nullptr, // don't care about scheme
171
nullptr, nullptr, // don't care about authority
172
&pathPos, &pathLen);
173
if (NS_FAILED(rv)) return rv;
174
175
// invoke the parser to extract filepath from the path
176
rv = parser->ParsePath(url + pathPos, pathLen, &filepathPos, &filepathLen,
177
nullptr, nullptr, // don't care about query
178
nullptr, nullptr); // don't care about ref
179
if (NS_FAILED(rv)) return rv;
180
181
filepathPos += pathPos;
182
183
// invoke the parser to extract the directory and filename from filepath
184
rv = parser->ParseFilePath(url + filepathPos, filepathLen, &directoryPos,
185
&directoryLen, &basenamePos, &basenameLen,
186
&extensionPos, &extensionLen);
187
if (NS_FAILED(rv)) return rv;
188
189
if (directoryLen > 0)
190
outDirectory = Substring(inURL, filepathPos + directoryPos, directoryLen);
191
if (basenameLen > 0)
192
outFileBaseName = Substring(inURL, filepathPos + basenamePos, basenameLen);
193
if (extensionLen > 0)
194
outFileExtension =
195
Substring(inURL, filepathPos + extensionPos, extensionLen);
196
// since we are using a no-auth url parser, there will never be a host
197
// XXX not strictly true... file://localhost/foo/bar.html is a valid URL
198
199
return NS_OK;
200
}
201
202
//----------------------------------------------------------------------------
203
// path manipulation functions
204
//----------------------------------------------------------------------------
205
206
// Replace all /./ with a / while resolving URLs
207
// But only till #?
208
void net_CoalesceDirs(netCoalesceFlags flags, char* path) {
209
/* Stolen from the old netlib's mkparse.c.
210
*
211
* modifies a url of the form /foo/../foo1 -> /foo1
212
* and /foo/./foo1 -> /foo/foo1
213
* and /foo/foo1/.. -> /foo/
214
*/
215
char* fwdPtr = path;
216
char* urlPtr = path;
217
char* lastslash = path;
218
uint32_t traversal = 0;
219
uint32_t special_ftp_len = 0;
220
221
/* Remember if this url is a special ftp one: */
222
if (flags & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) {
223
/* some schemes (for example ftp) have the speciality that
224
the path can begin // or /%2F to mark the root of the
225
servers filesystem, a simple / only marks the root relative
226
to the user loging in. We remember the length of the marker */
227
if (nsCRT::strncasecmp(path, "/%2F", 4) == 0)
228
special_ftp_len = 4;
229
else if (strncmp(path, "//", 2) == 0)
230
special_ftp_len = 2;
231
}
232
233
/* find the last slash before # or ? */
234
for (; (*fwdPtr != '\0') && (*fwdPtr != '?') && (*fwdPtr != '#'); ++fwdPtr) {
235
}
236
237
/* found nothing, but go back one only */
238
/* if there is something to go back to */
239
if (fwdPtr != path && *fwdPtr == '\0') {
240
--fwdPtr;
241
}
242
243
/* search the slash */
244
for (; (fwdPtr != path) && (*fwdPtr != '/'); --fwdPtr) {
245
}
246
lastslash = fwdPtr;
247
fwdPtr = path;
248
249
/* replace all %2E or %2e with . in the path */
250
/* but stop at lastchar if non null */
251
for (; (*fwdPtr != '\0') && (*fwdPtr != '?') && (*fwdPtr != '#') &&
252
(*lastslash == '\0' || fwdPtr != lastslash);
253
++fwdPtr) {
254
if (*fwdPtr == '%' && *(fwdPtr + 1) == '2' &&
255
(*(fwdPtr + 2) == 'E' || *(fwdPtr + 2) == 'e')) {
256
*urlPtr++ = '.';
257
++fwdPtr;
258
++fwdPtr;
259
} else {
260
*urlPtr++ = *fwdPtr;
261
}
262
}
263
// Copy remaining stuff past the #?;
264
for (; *fwdPtr != '\0'; ++fwdPtr) {
265
*urlPtr++ = *fwdPtr;
266
}
267
*urlPtr = '\0'; // terminate the url
268
269
// start again, this time for real
270
fwdPtr = path;
271
urlPtr = path;
272
273
for (; (*fwdPtr != '\0') && (*fwdPtr != '?') && (*fwdPtr != '#'); ++fwdPtr) {
274
if (*fwdPtr == '/' && *(fwdPtr + 1) == '.' && *(fwdPtr + 2) == '/') {
275
// remove . followed by slash
276
++fwdPtr;
277
} else if (*fwdPtr == '/' && *(fwdPtr + 1) == '.' && *(fwdPtr + 2) == '.' &&
278
(*(fwdPtr + 3) == '/' ||
279
*(fwdPtr + 3) == '\0' || // This will take care of
280
*(fwdPtr + 3) == '?' || // something like foo/bar/..#sometag
281
*(fwdPtr + 3) == '#')) {
282
// remove foo/..
283
// reverse the urlPtr to the previous slash if possible
284
// if url does not allow relative root then drop .. above root
285
// otherwise retain them in the path
286
if (traversal > 0 || !(flags & NET_COALESCE_ALLOW_RELATIVE_ROOT)) {
287
if (urlPtr != path) urlPtr--; // we must be going back at least by one
288
for (; *urlPtr != '/' && urlPtr != path; urlPtr--)
289
; // null body
290
--traversal; // count back
291
// forward the fwdPtr past the ../
292
fwdPtr += 2;
293
// if we have reached the beginning of the path
294
// while searching for the previous / and we remember
295
// that it is an url that begins with /%2F then
296
// advance urlPtr again by 3 chars because /%2F already
297
// marks the root of the path
298
if (urlPtr == path && special_ftp_len > 3) {
299
++urlPtr;
300
++urlPtr;
301
++urlPtr;
302
}
303
// special case if we have reached the end
304
// to preserve the last /
305
if (*fwdPtr == '.' && *(fwdPtr + 1) == '\0') ++urlPtr;
306
} else {
307
// there are to much /.. in this path, just copy them instead.
308
// forward the urlPtr past the /.. and copying it
309
310
// However if we remember it is an url that starts with
311
// /%2F and urlPtr just points at the "F" of "/%2F" then do
312
// not overwrite it with the /, just copy .. and move forward
313
// urlPtr.
314
if (special_ftp_len > 3 && urlPtr == path + special_ftp_len - 1)
315
++urlPtr;
316
else
317
*urlPtr++ = *fwdPtr;
318
++fwdPtr;
319
*urlPtr++ = *fwdPtr;
320
++fwdPtr;
321
*urlPtr++ = *fwdPtr;
322
}
323
} else {
324
// count the hierachie, but only if we do not have reached
325
// the root of some special urls with a special root marker
326
if (*fwdPtr == '/' && *(fwdPtr + 1) != '.' &&
327
(special_ftp_len != 2 || *(fwdPtr + 1) != '/'))
328
traversal++;
329
// copy the url incrementaly
330
*urlPtr++ = *fwdPtr;
331
}
332
}
333
334
/*
335
* Now lets remove trailing . case
336
* /foo/foo1/. -> /foo/foo1/
337
*/
338
339
if ((urlPtr > (path + 1)) && (*(urlPtr - 1) == '.') && (*(urlPtr - 2) == '/'))
340
urlPtr--;
341
342
// Copy remaining stuff past the #?;
343
for (; *fwdPtr != '\0'; ++fwdPtr) {
344
*urlPtr++ = *fwdPtr;
345
}
346
*urlPtr = '\0'; // terminate the url
347
}
348
349
nsresult net_ResolveRelativePath(const nsACString& relativePath,
350
const nsACString& basePath,
351
nsACString& result) {
352
nsAutoCString name;
353
nsAutoCString path(basePath);
354
bool needsDelim = false;
355
356
if (!path.IsEmpty()) {
357
char16_t last = path.Last();
358
needsDelim = !(last == '/');
359
}
360
361
nsACString::const_iterator beg, end;
362
relativePath.BeginReading(beg);
363
relativePath.EndReading(end);
364
365
bool stop = false;
366
char c;
367
for (; !stop; ++beg) {
368
c = (beg == end) ? '\0' : *beg;
369
// printf("%c [name=%s] [path=%s]\n", c, name.get(), path.get());
370
switch (c) {
371
case '\0':
372
case '#':
373
case '?':
374
stop = true;
375
MOZ_FALLTHROUGH;
376
case '/':
377
// delimiter found
378
if (name.EqualsLiteral("..")) {
379
// pop path
380
// If we already have the delim at end, then
381
// skip over that when searching for next one to the left
382
int32_t offset = path.Length() - (needsDelim ? 1 : 2);
383
// First check for errors
384
if (offset < 0) return NS_ERROR_MALFORMED_URI;
385
int32_t pos = path.RFind("/", false, offset);
386
if (pos >= 0)
387
path.Truncate(pos + 1);
388
else
389
path.Truncate();
390
} else if (name.IsEmpty() || name.EqualsLiteral(".")) {
391
// do nothing
392
} else {
393
// append name to path
394
if (needsDelim) path += '/';
395
path += name;
396
needsDelim = true;
397
}
398
name.Truncate();
399
break;
400
401
default:
402
// append char to name
403
name += c;
404
}
405
}
406
// append anything left on relativePath (e.g. #..., ;..., ?...)
407
if (c != '\0') path += Substring(--beg, end);
408
409
result = path;
410
return NS_OK;
411
}
412
413
//----------------------------------------------------------------------------
414
// scheme fu
415
//----------------------------------------------------------------------------
416
417
static bool net_IsValidSchemeChar(const char aChar) {
418
if (IsAsciiAlpha(aChar) || IsAsciiDigit(aChar) || aChar == '+' ||
419
aChar == '.' || aChar == '-') {
420
return true;
421
}
422
return false;
423
}
424
425
/* Extract URI-Scheme if possible */
426
nsresult net_ExtractURLScheme(const nsACString& inURI, nsACString& scheme) {
427
nsACString::const_iterator start, end;
428
inURI.BeginReading(start);
429
inURI.EndReading(end);
430
431
// Strip C0 and space from begining
432
while (start != end) {
433
if ((uint8_t)*start > 0x20) {
434
break;
435
}
436
start++;
437
}
438
439
Tokenizer p(Substring(start, end), "\r\n\t");
440
p.Record();
441
if (!p.CheckChar(IsAsciiAlpha)) {
442
// First char must be alpha
443
return NS_ERROR_MALFORMED_URI;
444
}
445
446
while (p.CheckChar(net_IsValidSchemeChar) || p.CheckWhite()) {
447
// Skip valid scheme characters or \r\n\t
448
}
449
450
if (!p.CheckChar(':')) {
451
return NS_ERROR_MALFORMED_URI;
452
}
453
454
p.Claim(scheme);
455
scheme.StripTaggedASCII(ASCIIMask::MaskCRLFTab());
456
ToLowerCase(scheme);
457
return NS_OK;
458
}
459
460
bool net_IsValidScheme(const char* scheme, uint32_t schemeLen) {
461
// first char must be alpha
462
if (!IsAsciiAlpha(*scheme)) return false;
463
464
// nsCStrings may have embedded nulls -- reject those too
465
for (; schemeLen; ++scheme, --schemeLen) {
466
if (!(IsAsciiAlpha(*scheme) || IsAsciiDigit(*scheme) || *scheme == '+' ||
467
*scheme == '.' || *scheme == '-'))
468
return false;
469
}
470
471
return true;
472
}
473
474
bool net_IsAbsoluteURL(const nsACString& uri) {
475
nsACString::const_iterator start, end;
476
uri.BeginReading(start);
477
uri.EndReading(end);
478
479
// Strip C0 and space from begining
480
while (start != end) {
481
if ((uint8_t)*start > 0x20) {
482
break;
483
}
484
start++;
485
}
486
487
Tokenizer p(Substring(start, end), "\r\n\t");
488
489
// First char must be alpha
490
if (!p.CheckChar(IsAsciiAlpha)) {
491
return false;
492
}
493
494
while (p.CheckChar(net_IsValidSchemeChar) || p.CheckWhite()) {
495
// Skip valid scheme characters or \r\n\t
496
}
497
if (!p.CheckChar(':')) {
498
return false;
499
}
500
p.SkipWhites();
501
502
if (!p.CheckChar('/')) {
503
return false;
504
}
505
p.SkipWhites();
506
507
if (p.CheckChar('/')) {
508
// aSpec is really absolute. Ignore aBaseURI in this case
509
return true;
510
}
511
return false;
512
}
513
514
void net_FilterURIString(const nsACString& input, nsACString& result) {
515
result.Truncate();
516
517
auto start = input.BeginReading();
518
auto end = input.EndReading();
519
520
// Trim off leading and trailing invalid chars.
521
auto charFilter = [](char c) { return static_cast<uint8_t>(c) > 0x20; };
522
auto newStart = std::find_if(start, end, charFilter);
523
auto newEnd =
524
std::find_if(std::reverse_iterator<decltype(end)>(end),
525
std::reverse_iterator<decltype(newStart)>(newStart),
526
charFilter)
527
.base();
528
529
// Check if chars need to be stripped.
530
bool needsStrip = false;
531
const ASCIIMaskArray& mask = ASCIIMask::MaskCRLFTab();
532
for (auto itr = start; itr != end; ++itr) {
533
if (ASCIIMask::IsMasked(mask, *itr)) {
534
needsStrip = true;
535
break;
536
}
537
}
538
539
// Just use the passed in string rather than creating new copies if no
540
// changes are necessary.
541
if (newStart == start && newEnd == end && !needsStrip) {
542
result = input;
543
return;
544
}
545
546
result.Assign(Substring(newStart, newEnd));
547
if (needsStrip) {
548
result.StripTaggedASCII(mask);
549
}
550
}
551
552
nsresult net_FilterAndEscapeURI(const nsACString& aInput, uint32_t aFlags,
553
nsACString& aResult) {
554
aResult.Truncate();
555
556
auto start = aInput.BeginReading();
557
auto end = aInput.EndReading();
558
559
// Trim off leading and trailing invalid chars.
560
auto charFilter = [](char c) { return static_cast<uint8_t>(c) > 0x20; };
561
auto newStart = std::find_if(start, end, charFilter);
562
auto newEnd =
563
std::find_if(std::reverse_iterator<decltype(end)>(end),
564
std::reverse_iterator<decltype(newStart)>(newStart),
565
charFilter)
566
.base();
567
568
const ASCIIMaskArray& mask = ASCIIMask::MaskCRLFTab();
569
return NS_EscapeAndFilterURL(Substring(newStart, newEnd), aFlags, &mask,
570
aResult, fallible);
571
}
572
573
#if defined(XP_WIN)
574
bool net_NormalizeFileURL(const nsACString& aURL, nsCString& aResultBuf) {
575
bool writing = false;
576
577
nsACString::const_iterator beginIter, endIter;
578
aURL.BeginReading(beginIter);
579
aURL.EndReading(endIter);
580
581
const char *s, *begin = beginIter.get();
582
583
for (s = begin; s != endIter.get(); ++s) {
584
if (*s == '\\') {
585
writing = true;
586
if (s > begin) aResultBuf.Append(begin, s - begin);
587
aResultBuf += '/';
588
begin = s + 1;
589
}
590
}
591
if (writing && s > begin) aResultBuf.Append(begin, s - begin);
592
593
return writing;
594
}
595
#endif
596
597
//----------------------------------------------------------------------------
598
// miscellaneous (i.e., stuff that should really be elsewhere)
599
//----------------------------------------------------------------------------
600
601
static inline void ToLower(char& c) {
602
if ((unsigned)(c - 'A') <= (unsigned)('Z' - 'A')) c += 'a' - 'A';
603
}
604
605
void net_ToLowerCase(char* str, uint32_t length) {
606
for (char* end = str + length; str < end; ++str) ToLower(*str);
607
}
608
609
void net_ToLowerCase(char* str) {
610
for (; *str; ++str) ToLower(*str);
611
}
612
613
char* net_FindCharInSet(const char* iter, const char* stop, const char* set) {
614
for (; iter != stop && *iter; ++iter) {
615
for (const char* s = set; *s; ++s) {
616
if (*iter == *s) return (char*)iter;
617
}
618
}
619
return (char*)iter;
620
}
621
622
char* net_FindCharNotInSet(const char* iter, const char* stop,
623
const char* set) {
624
repeat:
625
for (const char* s = set; *s; ++s) {
626
if (*iter == *s) {
627
if (++iter == stop) break;
628
goto repeat;
629
}
630
}
631
return (char*)iter;
632
}
633
634
char* net_RFindCharNotInSet(const char* stop, const char* iter,
635
const char* set) {
636
--iter;
637
--stop;
638
639
if (iter == stop) return (char*)iter;
640
641
repeat:
642
for (const char* s = set; *s; ++s) {
643
if (*iter == *s) {
644
if (--iter == stop) break;
645
goto repeat;
646
}
647
}
648
return (char*)iter;
649
}
650
651
#define HTTP_LWS " \t"
652
653
// Return the index of the closing quote of the string, if any
654
static uint32_t net_FindStringEnd(const nsCString& flatStr,
655
uint32_t stringStart, char stringDelim) {
656
NS_ASSERTION(stringStart < flatStr.Length() &&
657
flatStr.CharAt(stringStart) == stringDelim &&
658
(stringDelim == '"' || stringDelim == '\''),
659
"Invalid stringStart");
660
661
const char set[] = {stringDelim, '\\', '\0'};
662
do {
663
// stringStart points to either the start quote or the last
664
// escaped char (the char following a '\\')
665
666
// Write to searchStart here, so that when we get back to the
667
// top of the loop right outside this one we search from the
668
// right place.
669
uint32_t stringEnd = flatStr.FindCharInSet(set, stringStart + 1);
670
if (stringEnd == uint32_t(kNotFound)) return flatStr.Length();
671
672
if (flatStr.CharAt(stringEnd) == '\\') {
673
// Hit a backslash-escaped char. Need to skip over it.
674
stringStart = stringEnd + 1;
675
if (stringStart == flatStr.Length()) return stringStart;
676
677
// Go back to looking for the next escape or the string end
678
continue;
679
}
680
681
return stringEnd;
682
683
} while (true);
684
685
MOZ_ASSERT_UNREACHABLE("How did we get here?");
686
return flatStr.Length();
687
}
688
689
static uint32_t net_FindMediaDelimiter(const nsCString& flatStr,
690
uint32_t searchStart, char delimiter) {
691
do {
692
// searchStart points to the spot from which we should start looking
693
// for the delimiter.
694
const char delimStr[] = {delimiter, '"', '\0'};
695
uint32_t curDelimPos = flatStr.FindCharInSet(delimStr, searchStart);
696
if (curDelimPos == uint32_t(kNotFound)) return flatStr.Length();
697
698
char ch = flatStr.CharAt(curDelimPos);
699
if (ch == delimiter) {
700
// Found delimiter
701
return curDelimPos;
702
}
703
704
// We hit the start of a quoted string. Look for its end.
705
searchStart = net_FindStringEnd(flatStr, curDelimPos, ch);
706
if (searchStart == flatStr.Length()) return searchStart;
707
708
++searchStart;
709
710
// searchStart now points to the first char after the end of the
711
// string, so just go back to the top of the loop and look for
712
// |delimiter| again.
713
} while (true);
714
715
MOZ_ASSERT_UNREACHABLE("How did we get here?");
716
return flatStr.Length();
717
}
718
719
// aOffset should be added to aCharsetStart and aCharsetEnd if this
720
// function sets them.
721
static void net_ParseMediaType(const nsACString& aMediaTypeStr,
722
nsACString& aContentType,
723
nsACString& aContentCharset, int32_t aOffset,
724
bool* aHadCharset, int32_t* aCharsetStart,
725
int32_t* aCharsetEnd, bool aStrict) {
726
const nsCString& flatStr = PromiseFlatCString(aMediaTypeStr);
727
const char* start = flatStr.get();
728
const char* end = start + flatStr.Length();
729
730
// Trim LWS leading and trailing whitespace from type. We include '(' in
731
// the trailing trim set to catch media-type comments, which are not at all
732
// standard, but may occur in rare cases.
733
const char* type = net_FindCharNotInSet(start, end, HTTP_LWS);
734
const char* typeEnd = net_FindCharInSet(type, end, HTTP_LWS ";(");
735
736
const char* charset = "";
737
const char* charsetEnd = charset;
738
int32_t charsetParamStart = 0;
739
int32_t charsetParamEnd = 0;
740
741
uint32_t consumed = typeEnd - type;
742
743
// Iterate over parameters
744
bool typeHasCharset = false;
745
uint32_t paramStart = flatStr.FindChar(';', typeEnd - start);
746
if (paramStart != uint32_t(kNotFound)) {
747
// We have parameters. Iterate over them.
748
uint32_t curParamStart = paramStart + 1;
749
do {
750
uint32_t curParamEnd =
751
net_FindMediaDelimiter(flatStr, curParamStart, ';');
752
753
const char* paramName = net_FindCharNotInSet(
754
start + curParamStart, start + curParamEnd, HTTP_LWS);
755
static const char charsetStr[] = "charset=";
756
if (PL_strncasecmp(paramName, charsetStr, sizeof(charsetStr) - 1) == 0) {
757
charset = paramName + sizeof(charsetStr) - 1;
758
charsetEnd = start + curParamEnd;
759
typeHasCharset = true;
760
charsetParamStart = curParamStart - 1;
761
charsetParamEnd = curParamEnd;
762
}
763
764
consumed = curParamEnd;
765
curParamStart = curParamEnd + 1;
766
} while (curParamStart < flatStr.Length());
767
}
768
769
bool charsetNeedsQuotedStringUnescaping = false;
770
if (typeHasCharset) {
771
// Trim LWS leading and trailing whitespace from charset. We include
772
// '(' in the trailing trim set to catch media-type comments, which are
773
// not at all standard, but may occur in rare cases.
774
charset = net_FindCharNotInSet(charset, charsetEnd, HTTP_LWS);
775
if (*charset == '"') {
776
charsetNeedsQuotedStringUnescaping = true;
777
charsetEnd =
778
start + net_FindStringEnd(flatStr, charset - start, *charset);
779
charset++;
780
NS_ASSERTION(charsetEnd >= charset, "Bad charset parsing");
781
} else {
782
charsetEnd = net_FindCharInSet(charset, charsetEnd, HTTP_LWS ";(");
783
}
784
}
785
786
// if the server sent "*/*", it is meaningless, so do not store it.
787
// also, if type is the same as aContentType, then just update the
788
// charset. however, if charset is empty and aContentType hasn't
789
// changed, then don't wipe-out an existing aContentCharset. We
790
// also want to reject a mime-type if it does not include a slash.
791
// some servers give junk after the charset parameter, which may
792
// include a comma, so this check makes us a bit more tolerant.
793
794
if (type != typeEnd && memchr(type, '/', typeEnd - type) != nullptr &&
795
(aStrict ? (net_FindCharNotInSet(start + consumed, end, HTTP_LWS) == end)
796
: (strncmp(type, "*/*", typeEnd - type) != 0))) {
797
// Common case here is that aContentType is empty
798
bool eq = !aContentType.IsEmpty() &&
799
aContentType.Equals(Substring(type, typeEnd),
800
nsCaseInsensitiveCStringComparator());
801
if (!eq) {
802
aContentType.Assign(type, typeEnd - type);
803
ToLowerCase(aContentType);
804
}
805
806
if ((!eq && *aHadCharset) || typeHasCharset) {
807
*aHadCharset = true;
808
if (charsetNeedsQuotedStringUnescaping) {
809
// parameters using the "quoted-string" syntax need
810
// backslash-escapes to be unescaped (see RFC 2616 Section 2.2)
811
aContentCharset.Truncate();
812
for (const char* c = charset; c != charsetEnd; c++) {
813
if (*c == '\\' && c + 1 != charsetEnd) {
814
// eat escape
815
c++;
816
}
817
aContentCharset.Append(*c);
818
}
819
} else {
820
aContentCharset.Assign(charset, charsetEnd - charset);
821
}
822
if (typeHasCharset) {
823
*aCharsetStart = charsetParamStart + aOffset;
824
*aCharsetEnd = charsetParamEnd + aOffset;
825
}
826
}
827
// Only set a new charset position if this is a different type
828
// from the last one we had and it doesn't already have a
829
// charset param. If this is the same type, we probably want
830
// to leave the charset position on its first occurrence.
831
if (!eq && !typeHasCharset) {
832
int32_t charsetStart = int32_t(paramStart);
833
if (charsetStart == kNotFound) charsetStart = flatStr.Length();
834
835
*aCharsetEnd = *aCharsetStart = charsetStart + aOffset;
836
}
837
}
838
}
839
840
#undef HTTP_LWS
841
842
void net_ParseContentType(const nsACString& aHeaderStr,
843
nsACString& aContentType, nsACString& aContentCharset,
844
bool* aHadCharset) {
845
int32_t dummy1, dummy2;
846
net_ParseContentType(aHeaderStr, aContentType, aContentCharset, aHadCharset,
847
&dummy1, &dummy2);
848
}
849
850
void net_ParseContentType(const nsACString& aHeaderStr,
851
nsACString& aContentType, nsACString& aContentCharset,
852
bool* aHadCharset, int32_t* aCharsetStart,
853
int32_t* aCharsetEnd) {
854
//
855
// Augmented BNF (from RFC 2616 section 3.7):
856
//
857
// header-value = media-type *( LWS "," LWS media-type )
858
// media-type = type "/" subtype *( LWS ";" LWS parameter )
859
// type = token
860
// subtype = token
861
// parameter = attribute "=" value
862
// attribute = token
863
// value = token | quoted-string
864
//
865
//
866
// Examples:
867
//
868
// text/html
869
// text/html, text/html
870
// text/html,text/html; charset=ISO-8859-1
871
// text/html,text/html; charset="ISO-8859-1"
872
// text/html;charset=ISO-8859-1, text/html
873
// text/html;charset='ISO-8859-1', text/html
874
// application/octet-stream
875
//
876
877
*aHadCharset = false;
878
const nsCString& flatStr = PromiseFlatCString(aHeaderStr);
879
880
// iterate over media-types. Note that ',' characters can happen
881
// inside quoted strings, so we need to watch out for that.
882
uint32_t curTypeStart = 0;
883
do {
884
// curTypeStart points to the start of the current media-type. We want
885
// to look for its end.
886
uint32_t curTypeEnd = net_FindMediaDelimiter(flatStr, curTypeStart, ',');
887
888
// At this point curTypeEnd points to the spot where the media-type
889
// starting at curTypeEnd ends. Time to parse that!
890
net_ParseMediaType(
891
Substring(flatStr, curTypeStart, curTypeEnd - curTypeStart),
892
aContentType, aContentCharset, curTypeStart, aHadCharset, aCharsetStart,
893
aCharsetEnd, false);
894
895
// And let's move on to the next media-type
896
curTypeStart = curTypeEnd + 1;
897
} while (curTypeStart < flatStr.Length());
898
}
899
900
void net_ParseRequestContentType(const nsACString& aHeaderStr,
901
nsACString& aContentType,
902
nsACString& aContentCharset,
903
bool* aHadCharset) {
904
//
905
// Augmented BNF (from RFC 7231 section 3.1.1.1):
906
//
907
// media-type = type "/" subtype *( OWS ";" OWS parameter )
908
// type = token
909
// subtype = token
910
// parameter = token "=" ( token / quoted-string )
911
//
912
// Examples:
913
//
914
// text/html
915
// text/html; charset=ISO-8859-1
916
// text/html; charset="ISO-8859-1"
917
// application/octet-stream
918
//
919
920
aContentType.Truncate();
921
aContentCharset.Truncate();
922
*aHadCharset = false;
923
const nsCString& flatStr = PromiseFlatCString(aHeaderStr);
924
925
// At this point curTypeEnd points to the spot where the media-type
926
// starting at curTypeEnd ends. Time to parse that!
927
nsAutoCString contentType, contentCharset;
928
bool hadCharset = false;
929
int32_t dummy1, dummy2;
930
uint32_t typeEnd = net_FindMediaDelimiter(flatStr, 0, ',');
931
if (typeEnd != flatStr.Length()) {
932
// We have some stuff left at the end, so this is not a valid
933
// request Content-Type header.
934
return;
935
}
936
net_ParseMediaType(flatStr, contentType, contentCharset, 0, &hadCharset,
937
&dummy1, &dummy2, true);
938
939
aContentType = contentType;
940
aContentCharset = contentCharset;
941
*aHadCharset = hadCharset;
942
}
943
944
bool net_IsValidHostName(const nsACString& host) {
945
const char* end = host.EndReading();
946
// Use explicit whitelists to select which characters we are
947
// willing to send to lower-level DNS logic. This is more
948
// self-documenting, and can also be slightly faster than the
949
// blacklist approach, since DNS names are the common case, and
950
// the commonest characters will tend to be near the start of
951
// the list.
952
953
// Whitelist for DNS names (RFC 1035) with extra characters added
954
// for pragmatic reasons "$+_"
956
if (net_FindCharNotInSet(host.BeginReading(), end,
957
"abcdefghijklmnopqrstuvwxyz"
958
".-0123456789"
959
"ABCDEFGHIJKLMNOPQRSTUVWXYZ$+_") == end)
960
return true;
961
962
// Might be a valid IPv6 link-local address containing a percent sign
963
nsAutoCString strhost(host);
964
PRNetAddr addr;
965
return PR_StringToNetAddr(strhost.get(), &addr) == PR_SUCCESS;
966
}
967
968
bool net_IsValidIPv4Addr(const nsACString& aAddr) {
969
return rust_net_is_valid_ipv4_addr(aAddr);
970
}
971
972
bool net_IsValidIPv6Addr(const nsACString& aAddr) {
973
return rust_net_is_valid_ipv6_addr(aAddr);
974
}