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