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