• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "CString.h"
30 #include "Credential.h"
31 #include "KURL.h"
32 #include "ProtectionSpaceHash.h"
33 #include "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;
protectionSpaceToCredentialMap()41 static ProtectionSpaceToCredentialMap& protectionSpaceToCredentialMap()
42 {
43     DEFINE_STATIC_LOCAL(ProtectionSpaceToCredentialMap, map, ());
44     return map;
45 }
46 
originsWithCredentials()47 static HashSet<String>& originsWithCredentials()
48 {
49     DEFINE_STATIC_LOCAL(HashSet<String>, set, ());
50     return set;
51 }
52 
53 typedef HashMap<String, ProtectionSpace> PathToDefaultProtectionSpaceMap;
pathToDefaultProtectionSpaceMap()54 static PathToDefaultProtectionSpaceMap& pathToDefaultProtectionSpaceMap()
55 {
56     DEFINE_STATIC_LOCAL(PathToDefaultProtectionSpaceMap, map, ());
57     return map;
58 }
59 
originStringFromURL(const KURL & url)60 static String originStringFromURL(const KURL& url)
61 {
62     if (url.port())
63         return url.protocol() + "://" + url.host() + String::format(":%i/", url.port());
64 
65     return url.protocol() + "://" + url.host() + "/";
66 }
67 
protectionSpaceMapKeyFromURL(const KURL & url)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         int index = directoryURL.reverseFind('/');
79         ASSERT(index > 0);
80         directoryURL = directoryURL.substring(0, (static_cast<unsigned>(index) != directoryURLPathStart) ? index : directoryURLPathStart + 1);
81     }
82     ASSERT(directoryURL.length() == directoryURLPathStart + 1 || directoryURL[directoryURL.length() - 1] != '/');
83 
84     return directoryURL;
85 }
86 
set(const Credential & credential,const ProtectionSpace & protectionSpace,const KURL & url)87 void CredentialStorage::set(const Credential& credential, const ProtectionSpace& protectionSpace, const KURL& url)
88 {
89     ASSERT(protectionSpace.isProxy() || url.protocolInHTTPFamily());
90     ASSERT(protectionSpace.isProxy() || url.isValid());
91 
92     protectionSpaceToCredentialMap().set(protectionSpace, credential);
93     if (!protectionSpace.isProxy()) {
94         originsWithCredentials().add(originStringFromURL(url));
95 
96         ProtectionSpaceAuthenticationScheme scheme = protectionSpace.authenticationScheme();
97         if (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic || scheme == ProtectionSpaceAuthenticationSchemeDefault) {
98             // The map can contain both a path and its subpath - while redundant, this makes lookups faster.
99             pathToDefaultProtectionSpaceMap().set(protectionSpaceMapKeyFromURL(url), protectionSpace);
100         }
101     }
102 }
103 
get(const ProtectionSpace & protectionSpace)104 Credential CredentialStorage::get(const ProtectionSpace& protectionSpace)
105 {
106     return protectionSpaceToCredentialMap().get(protectionSpace);
107 }
108 
findDefaultProtectionSpaceForURL(const KURL & url)109 static PathToDefaultProtectionSpaceMap::iterator findDefaultProtectionSpaceForURL(const KURL& url)
110 {
111     ASSERT(url.protocolInHTTPFamily());
112     ASSERT(url.isValid());
113 
114     PathToDefaultProtectionSpaceMap& map = pathToDefaultProtectionSpaceMap();
115 
116     // Don't spend time iterating the path for origins that don't have any credentials.
117     if (!originsWithCredentials().contains(originStringFromURL(url)))
118         return map.end();
119 
120     String directoryURL = protectionSpaceMapKeyFromURL(url);
121     unsigned directoryURLPathStart = url.pathStart();
122     while (true) {
123         PathToDefaultProtectionSpaceMap::iterator iter = map.find(directoryURL);
124         if (iter != map.end())
125             return iter;
126 
127         if (directoryURL.length() == directoryURLPathStart + 1)  // path is "/" already, cannot shorten it any more
128             return map.end();
129 
130         int index = directoryURL.reverseFind('/', -2);
131         ASSERT(index > 0);
132         directoryURL = directoryURL.substring(0, (static_cast<unsigned>(index) == directoryURLPathStart) ? index + 1 : index);
133         ASSERT(directoryURL.length() > directoryURLPathStart);
134         ASSERT(directoryURL.length() == directoryURLPathStart + 1 || directoryURL[directoryURL.length() - 1] != '/');
135     }
136 }
137 
set(const Credential & credential,const KURL & url)138 bool CredentialStorage::set(const Credential& credential, const KURL& url)
139 {
140     ASSERT(url.protocolInHTTPFamily());
141     ASSERT(url.isValid());
142     PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
143     if (iter == pathToDefaultProtectionSpaceMap().end())
144         return false;
145     ASSERT(originsWithCredentials().contains(originStringFromURL(url)));
146     protectionSpaceToCredentialMap().set(iter->second, credential);
147     return true;
148 }
149 
get(const KURL & url)150 Credential CredentialStorage::get(const KURL& url)
151 {
152     PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
153     if (iter == pathToDefaultProtectionSpaceMap().end())
154         return Credential();
155     return protectionSpaceToCredentialMap().get(iter->second);
156 }
157 
158 } // namespace WebCore
159