1/* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "ManifestParser.h" 28 29#include "TextResourceDecoder.h" 30#include "URL.h" 31#include <wtf/text/StringView.h> 32#include <wtf/unicode/CharacterNames.h> 33 34namespace WebCore { 35 36enum Mode { Explicit, Fallback, OnlineWhitelist, Unknown }; 37 38bool parseManifest(const URL& manifestURL, const char* data, int length, Manifest& manifest) 39{ 40 ASSERT(manifest.explicitURLs.isEmpty()); 41 ASSERT(manifest.onlineWhitelistedURLs.isEmpty()); 42 ASSERT(manifest.fallbackURLs.isEmpty()); 43 manifest.allowAllNetworkRequests = false; 44 45 Mode mode = Explicit; 46 47 String s = TextResourceDecoder::create("text/cache-manifest", "UTF-8")->decodeAndFlush(data, length); 48 49 // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?" (the BOM is removed by TextResourceDecoder). 50 // Example: "CACHE MANIFEST #comment" is a valid signature. 51 // Example: "CACHE MANIFEST;V2" is not. 52 if (!s.startsWith("CACHE MANIFEST")) 53 return false; 54 55 StringView manifestAfterSignature = StringView(s).substring(14); // "CACHE MANIFEST" is 14 characters. 56 auto upconvertedCharacters = manifestAfterSignature.upconvertedCharacters(); 57 const UChar* p = upconvertedCharacters; 58 const UChar* end = p + manifestAfterSignature.length(); 59 60 if (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') 61 return false; 62 63 // Skip to the end of the line. 64 while (p < end && *p != '\r' && *p != '\n') 65 p++; 66 67 while (1) { 68 // Skip whitespace 69 while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t')) 70 p++; 71 72 if (p == end) 73 break; 74 75 const UChar* lineStart = p; 76 77 // Find the end of the line 78 while (p < end && *p != '\r' && *p != '\n') 79 p++; 80 81 // Check if we have a comment 82 if (*lineStart == '#') 83 continue; 84 85 // Get rid of trailing whitespace 86 const UChar* tmp = p - 1; 87 while (tmp > lineStart && (*tmp == ' ' || *tmp == '\t')) 88 tmp--; 89 90 String line(lineStart, tmp - lineStart + 1); 91 92 if (line == "CACHE:") 93 mode = Explicit; 94 else if (line == "FALLBACK:") 95 mode = Fallback; 96 else if (line == "NETWORK:") 97 mode = OnlineWhitelist; 98 else if (line.endsWith(':')) 99 mode = Unknown; 100 else if (mode == Unknown) 101 continue; 102 else if (mode == Explicit || mode == OnlineWhitelist) { 103 auto upconvertedLineCharacters = StringView(line).upconvertedCharacters(); 104 const UChar* p = upconvertedLineCharacters; 105 const UChar* lineEnd = p + line.length(); 106 107 // Look for whitespace separating the URL from subsequent ignored tokens. 108 while (p < lineEnd && *p != '\t' && *p != ' ') 109 p++; 110 111 if (mode == OnlineWhitelist && p - upconvertedLineCharacters == 1 && line[0] == '*') { 112 // Wildcard was found. 113 manifest.allowAllNetworkRequests = true; 114 continue; 115 } 116 117 URL url(manifestURL, line.substring(0, p - upconvertedLineCharacters)); 118 119 if (!url.isValid()) 120 continue; 121 122 if (url.hasFragmentIdentifier()) 123 url.removeFragmentIdentifier(); 124 125 if (!equalIgnoringCase(url.protocol(), manifestURL.protocol())) 126 continue; 127 128 if (mode == Explicit && manifestURL.protocolIs("https") && !protocolHostAndPortAreEqual(manifestURL, url)) 129 continue; 130 131 if (mode == Explicit) 132 manifest.explicitURLs.add(url.string()); 133 else 134 manifest.onlineWhitelistedURLs.append(url); 135 136 } else if (mode == Fallback) { 137 auto upconvertedLineCharacters = StringView(line).upconvertedCharacters(); 138 const UChar* p = upconvertedLineCharacters; 139 const UChar* lineEnd = p + line.length(); 140 141 // Look for whitespace separating the two URLs 142 while (p < lineEnd && *p != '\t' && *p != ' ') 143 p++; 144 145 if (p == lineEnd) { 146 // There was no whitespace separating the URLs. 147 continue; 148 } 149 150 URL namespaceURL(manifestURL, line.substring(0, p - upconvertedLineCharacters)); 151 if (!namespaceURL.isValid()) 152 continue; 153 if (namespaceURL.hasFragmentIdentifier()) 154 namespaceURL.removeFragmentIdentifier(); 155 156 if (!protocolHostAndPortAreEqual(manifestURL, namespaceURL)) 157 continue; 158 159 // Skip whitespace separating fallback namespace from URL. 160 while (p < lineEnd && (*p == '\t' || *p == ' ')) 161 p++; 162 163 // Look for whitespace separating the URL from subsequent ignored tokens. 164 const UChar* fallbackStart = p; 165 while (p < lineEnd && *p != '\t' && *p != ' ') 166 p++; 167 168 URL fallbackURL(manifestURL, String(fallbackStart, p - fallbackStart)); 169 if (!fallbackURL.isValid()) 170 continue; 171 if (fallbackURL.hasFragmentIdentifier()) 172 fallbackURL.removeFragmentIdentifier(); 173 174 if (!protocolHostAndPortAreEqual(manifestURL, fallbackURL)) 175 continue; 176 177 manifest.fallbackURLs.append(std::make_pair(namespaceURL, fallbackURL)); 178 } else 179 ASSERT_NOT_REACHED(); 180 } 181 182 return true; 183} 184 185} 186