1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache2/CacheFileUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,290 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "CacheLog.h" 1.9 +#include "CacheFileUtils.h" 1.10 +#include "LoadContextInfo.h" 1.11 +#include "nsCOMPtr.h" 1.12 +#include "nsAutoPtr.h" 1.13 +#include "nsString.h" 1.14 + 1.15 + 1.16 +namespace mozilla { 1.17 +namespace net { 1.18 +namespace CacheFileUtils { 1.19 + 1.20 +namespace { // anon 1.21 + 1.22 +/** 1.23 + * A simple recursive descent parser for the mapping key. 1.24 + */ 1.25 +class KeyParser 1.26 +{ 1.27 +public: 1.28 + KeyParser(nsACString::const_iterator aCaret, nsACString::const_iterator aEnd) 1.29 + : caret(aCaret) 1.30 + , end(aEnd) 1.31 + // Initialize attributes to their default values 1.32 + , appId(nsILoadContextInfo::NO_APP_ID) 1.33 + , isPrivate(false) 1.34 + , isInBrowser(false) 1.35 + , isAnonymous(false) 1.36 + // Initialize the cache key to a zero length by default 1.37 + , cacheKey(aEnd) 1.38 + , lastTag(0) 1.39 + { 1.40 + } 1.41 + 1.42 +private: 1.43 + // Current character being parsed 1.44 + nsACString::const_iterator caret; 1.45 + // The end of the buffer 1.46 + nsACString::const_iterator const end; 1.47 + 1.48 + // Results 1.49 + uint32_t appId; 1.50 + bool isPrivate; 1.51 + bool isInBrowser; 1.52 + bool isAnonymous; 1.53 + nsCString idEnhance; 1.54 + // Position of the cache key, if present 1.55 + nsACString::const_iterator cacheKey; 1.56 + 1.57 + // Keeps the last tag name, used for alphabetical sort checking 1.58 + char lastTag; 1.59 + 1.60 + bool ParseTags() 1.61 + { 1.62 + // Expects to be at the tag name or at the end 1.63 + if (caret == end) 1.64 + return true; 1.65 + 1.66 + // 'Read' the tag name and move to the next char 1.67 + char const tag = *caret++; 1.68 + // Check the alphabetical order, hard-fail on disobedience 1.69 + if (!(lastTag < tag || tag == ':')) 1.70 + return false; 1.71 + 1.72 + lastTag = tag; 1.73 + 1.74 + switch (tag) { 1.75 + case ':': 1.76 + // last possible tag, when present there is the cacheKey following, 1.77 + // not terminated with ',' and no need to unescape. 1.78 + cacheKey = caret; 1.79 + caret = end; 1.80 + return true; 1.81 + case 'p': 1.82 + isPrivate = true; 1.83 + break; 1.84 + case 'b': 1.85 + isInBrowser = true; 1.86 + break; 1.87 + case 'a': 1.88 + isAnonymous = true; 1.89 + break; 1.90 + case 'i': { 1.91 + nsAutoCString appIdString; 1.92 + if (!ParseValue(&appIdString)) 1.93 + return false; 1.94 + 1.95 + nsresult rv; 1.96 + int64_t appId64 = appIdString.ToInteger64(&rv); 1.97 + if (NS_FAILED(rv)) 1.98 + return false; // appid value is mandatory 1.99 + if (appId64 < 0 || appId64 > PR_UINT32_MAX) 1.100 + return false; // not in the range 1.101 + appId = static_cast<uint32_t>(appId64); 1.102 + 1.103 + break; 1.104 + } 1.105 + case '~': 1.106 + if (!ParseValue(&idEnhance)) 1.107 + return false; 1.108 + break; 1.109 + default: 1.110 + if (!ParseValue()) // skip any tag values, optional 1.111 + return false; 1.112 + break; 1.113 + } 1.114 + 1.115 + // Recurse to the next tag 1.116 + return ParseNextTagOrEnd(); 1.117 + } 1.118 + 1.119 + bool ParseNextTagOrEnd() 1.120 + { 1.121 + // We expect a comma after every tag 1.122 + if (caret == end || *caret++ != ',') 1.123 + return false; 1.124 + 1.125 + // Go to another tag 1.126 + return ParseTags(); 1.127 + } 1.128 + 1.129 + bool ParseValue(nsACString * result = nullptr) 1.130 + { 1.131 + // If at the end, fail since we expect a comma ; value may be empty tho 1.132 + if (caret == end) 1.133 + return false; 1.134 + 1.135 + // Remeber where the value starts 1.136 + nsACString::const_iterator val = caret; 1.137 + nsACString::const_iterator comma = end; 1.138 + bool escape = false; 1.139 + while (caret != end) { 1.140 + nsACString::const_iterator at = caret; 1.141 + ++caret; // we can safely break/continue the loop now 1.142 + 1.143 + if (*at == ',') { 1.144 + if (comma != end) { 1.145 + // another comma (we have found ",," -> escape) 1.146 + comma = end; 1.147 + escape = true; 1.148 + } else { 1.149 + comma = at; 1.150 + } 1.151 + continue; 1.152 + } 1.153 + 1.154 + if (comma != end) { 1.155 + // after a single comma 1.156 + break; 1.157 + } 1.158 + } 1.159 + 1.160 + // At this point |comma| points to the last and lone ',' we've hit. 1.161 + // If a lone comma was not found, |comma| is at the end of the buffer, 1.162 + // that is not expected and we return failure. 1.163 + 1.164 + caret = comma; 1.165 + if (result) { 1.166 + if (escape) { 1.167 + // No ReplaceSubstring on nsACString.. 1.168 + nsAutoCString _result(Substring(val, caret)); 1.169 + _result.ReplaceSubstring(NS_LITERAL_CSTRING(",,"), NS_LITERAL_CSTRING(",")); 1.170 + result->Assign(_result); 1.171 + } else { 1.172 + result->Assign(Substring(val, caret)); 1.173 + } 1.174 + } 1.175 + 1.176 + return caret != end; 1.177 + } 1.178 + 1.179 +public: 1.180 + already_AddRefed<LoadContextInfo> Parse() 1.181 + { 1.182 + nsRefPtr<LoadContextInfo> info; 1.183 + if (ParseTags()) 1.184 + info = GetLoadContextInfo(isPrivate, appId, isInBrowser, isAnonymous); 1.185 + 1.186 + return info.forget(); 1.187 + } 1.188 + 1.189 + void URISpec(nsACString &result) 1.190 + { 1.191 + // cacheKey is either pointing to end or the position where the cache key is. 1.192 + result.Assign(Substring(cacheKey, end)); 1.193 + } 1.194 + 1.195 + void IdEnhance(nsACString &result) 1.196 + { 1.197 + result.Assign(idEnhance); 1.198 + } 1.199 +}; 1.200 + 1.201 +} // anon 1.202 + 1.203 +already_AddRefed<nsILoadContextInfo> 1.204 +ParseKey(const nsCSubstring &aKey, 1.205 + nsCSubstring *aIdEnhance, 1.206 + nsCSubstring *aURISpec) 1.207 +{ 1.208 + nsACString::const_iterator caret, end; 1.209 + aKey.BeginReading(caret); 1.210 + aKey.EndReading(end); 1.211 + 1.212 + KeyParser parser(caret, end); 1.213 + nsRefPtr<LoadContextInfo> info = parser.Parse(); 1.214 + 1.215 + if (info) { 1.216 + if (aIdEnhance) 1.217 + parser.IdEnhance(*aIdEnhance); 1.218 + if (aURISpec) 1.219 + parser.URISpec(*aURISpec); 1.220 + } 1.221 + 1.222 + return info.forget(); 1.223 +} 1.224 + 1.225 +void 1.226 +AppendKeyPrefix(nsILoadContextInfo* aInfo, nsACString &_retval) 1.227 +{ 1.228 + /** 1.229 + * This key is used to salt file hashes. When form of the key is changed 1.230 + * cache entries will fail to find on disk. 1.231 + * 1.232 + * IMPORTANT NOTE: 1.233 + * Keep the attributes list sorted according their ASCII code. 1.234 + */ 1.235 + 1.236 + if (aInfo->IsAnonymous()) { 1.237 + _retval.Append(NS_LITERAL_CSTRING("a,")); 1.238 + } 1.239 + 1.240 + if (aInfo->IsInBrowserElement()) { 1.241 + _retval.Append(NS_LITERAL_CSTRING("b,")); 1.242 + } 1.243 + 1.244 + if (aInfo->AppId() != nsILoadContextInfo::NO_APP_ID) { 1.245 + _retval.Append('i'); 1.246 + _retval.AppendInt(aInfo->AppId()); 1.247 + _retval.Append(','); 1.248 + } 1.249 + 1.250 + if (aInfo->IsPrivate()) { 1.251 + _retval.Append(NS_LITERAL_CSTRING("p,")); 1.252 + } 1.253 +} 1.254 + 1.255 +void 1.256 +AppendTagWithValue(nsACString & aTarget, char const aTag, nsCSubstring const & aValue) 1.257 +{ 1.258 + aTarget.Append(aTag); 1.259 + 1.260 + // First check the value string to save some memory copying 1.261 + // for cases we don't need to escape at all (most likely). 1.262 + if (!aValue.IsEmpty()) { 1.263 + if (aValue.FindChar(',') == kNotFound) { 1.264 + // No need to escape 1.265 + aTarget.Append(aValue); 1.266 + } else { 1.267 + nsAutoCString escapedValue(aValue); 1.268 + escapedValue.ReplaceSubstring( 1.269 + NS_LITERAL_CSTRING(","), NS_LITERAL_CSTRING(",,")); 1.270 + aTarget.Append(escapedValue); 1.271 + } 1.272 + } 1.273 + 1.274 + aTarget.Append(','); 1.275 +} 1.276 + 1.277 +nsresult 1.278 +KeyMatchesLoadContextInfo(const nsACString &aKey, nsILoadContextInfo *aInfo, 1.279 + bool *_retval) 1.280 +{ 1.281 + nsCOMPtr<nsILoadContextInfo> info = ParseKey(aKey); 1.282 + 1.283 + if (!info) { 1.284 + return NS_ERROR_FAILURE; 1.285 + } 1.286 + 1.287 + *_retval = info->Equals(aInfo); 1.288 + return NS_OK; 1.289 +} 1.290 + 1.291 +} // CacheFileUtils 1.292 +} // net 1.293 +} // mozilla