1/* 2 * Copyright (C) 2009 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 "CredentialStorage.h" 28 29#include "Credential.h" 30#include "URL.h" 31#include "ProtectionSpaceHash.h" 32#include <wtf/text/WTFString.h> 33#include <wtf/text/StringHash.h> 34#include <wtf/HashMap.h> 35#include <wtf/HashSet.h> 36#include <wtf/MainThread.h> 37#include <wtf/StdLibExtras.h> 38 39#if PLATFORM(IOS) 40#include "WebCoreThread.h" 41#endif 42 43namespace WebCore { 44 45typedef HashMap<ProtectionSpace, Credential> ProtectionSpaceToCredentialMap; 46static ProtectionSpaceToCredentialMap& protectionSpaceToCredentialMap() 47{ 48 ASSERT(isMainThread()); 49 DEPRECATED_DEFINE_STATIC_LOCAL(ProtectionSpaceToCredentialMap, map, ()); 50 return map; 51} 52 53static HashSet<String>& originsWithCredentials() 54{ 55 ASSERT(isMainThread()); 56 DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<String>, set, ()); 57 return set; 58} 59 60typedef HashMap<String, ProtectionSpace> PathToDefaultProtectionSpaceMap; 61static PathToDefaultProtectionSpaceMap& pathToDefaultProtectionSpaceMap() 62{ 63 ASSERT(isMainThread()); 64 DEPRECATED_DEFINE_STATIC_LOCAL(PathToDefaultProtectionSpaceMap, map, ()); 65 return map; 66} 67 68static String originStringFromURL(const URL& url) 69{ 70 if (url.port()) 71 return url.protocol() + "://" + url.host() + ':' + String::number(url.port()) + '/'; 72 73 return url.protocol() + "://" + url.host() + '/'; 74} 75 76static String protectionSpaceMapKeyFromURL(const URL& url) 77{ 78 ASSERT(url.isValid()); 79 80 // Remove the last path component that is not a directory to determine the subtree for which credentials will apply. 81 // We keep a leading slash, but remove a trailing one. 82 String directoryURL = url.string().substring(0, url.pathEnd()); 83 unsigned directoryURLPathStart = url.pathStart(); 84 ASSERT(directoryURL[directoryURLPathStart] == '/'); 85 if (directoryURL.length() > directoryURLPathStart + 1) { 86 size_t index = directoryURL.reverseFind('/'); 87 ASSERT(index != notFound); 88 directoryURL = directoryURL.substring(0, (index != directoryURLPathStart) ? index : directoryURLPathStart + 1); 89 } 90 91 return directoryURL; 92} 93 94void CredentialStorage::set(const Credential& credential, const ProtectionSpace& protectionSpace, const URL& url) 95{ 96 ASSERT(protectionSpace.isProxy() || url.protocolIsInHTTPFamily()); 97 ASSERT(protectionSpace.isProxy() || url.isValid()); 98 99 protectionSpaceToCredentialMap().set(protectionSpace, credential); 100 101#if PLATFORM(IOS) 102 saveToPersistentStorage(protectionSpace, credential); 103#endif 104 105 if (!protectionSpace.isProxy()) { 106 originsWithCredentials().add(originStringFromURL(url)); 107 108 ProtectionSpaceAuthenticationScheme scheme = protectionSpace.authenticationScheme(); 109 if (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic || scheme == ProtectionSpaceAuthenticationSchemeDefault) { 110 // The map can contain both a path and its subpath - while redundant, this makes lookups faster. 111 pathToDefaultProtectionSpaceMap().set(protectionSpaceMapKeyFromURL(url), protectionSpace); 112 } 113 } 114} 115 116Credential CredentialStorage::get(const ProtectionSpace& protectionSpace) 117{ 118 return protectionSpaceToCredentialMap().get(protectionSpace); 119} 120 121void CredentialStorage::remove(const ProtectionSpace& protectionSpace) 122{ 123 protectionSpaceToCredentialMap().remove(protectionSpace); 124} 125 126static PathToDefaultProtectionSpaceMap::iterator findDefaultProtectionSpaceForURL(const URL& url) 127{ 128 ASSERT(url.protocolIsInHTTPFamily()); 129 ASSERT(url.isValid()); 130 131 PathToDefaultProtectionSpaceMap& map = pathToDefaultProtectionSpaceMap(); 132 133 // Don't spend time iterating the path for origins that don't have any credentials. 134 if (!originsWithCredentials().contains(originStringFromURL(url))) 135 return map.end(); 136 137 String directoryURL = protectionSpaceMapKeyFromURL(url); 138 unsigned directoryURLPathStart = url.pathStart(); 139 while (true) { 140 PathToDefaultProtectionSpaceMap::iterator iter = map.find(directoryURL); 141 if (iter != map.end()) 142 return iter; 143 144 if (directoryURL.length() == directoryURLPathStart + 1) // path is "/" already, cannot shorten it any more 145 return map.end(); 146 147 size_t index = directoryURL.reverseFind('/', directoryURL.length() - 2); 148 ASSERT(index != notFound); 149 directoryURL = directoryURL.substring(0, (index == directoryURLPathStart) ? index + 1 : index); 150 ASSERT(directoryURL.length() > directoryURLPathStart); 151 ASSERT(directoryURL.length() == directoryURLPathStart + 1 || directoryURL[directoryURL.length() - 1] != '/'); 152 } 153} 154 155bool CredentialStorage::set(const Credential& credential, const URL& url) 156{ 157 ASSERT(url.protocolIsInHTTPFamily()); 158 ASSERT(url.isValid()); 159 PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url); 160 if (iter == pathToDefaultProtectionSpaceMap().end()) 161 return false; 162 ASSERT(originsWithCredentials().contains(originStringFromURL(url))); 163 protectionSpaceToCredentialMap().set(iter->value, credential); 164 return true; 165} 166 167Credential CredentialStorage::get(const URL& url) 168{ 169 PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url); 170 if (iter == pathToDefaultProtectionSpaceMap().end()) 171 return Credential(); 172 return protectionSpaceToCredentialMap().get(iter->value); 173} 174 175#if PLATFORM(IOS) 176void CredentialStorage::clearCredentials() 177{ 178 pathToDefaultProtectionSpaceMap().clear(); 179 originsWithCredentials().clear(); 180 protectionSpaceToCredentialMap().clear(); 181} 182#endif 183 184void CredentialStorage::setPrivateMode(bool mode) 185{ 186 if (!mode) 187 protectionSpaceToCredentialMap().clear(); 188} 189 190} // namespace WebCore 191