1231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block/*
2231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * Copyright (C) 2009 Apple Inc. All Rights Reserved.
3231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block *
4231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * Redistribution and use in source and binary forms, with or without
5231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * modification, are permitted provided that the following conditions
6231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * are met:
7231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * 1. Redistributions of source code must retain the above copyright
8231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block *    notice, this list of conditions and the following disclaimer.
9231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * 2. Redistributions in binary form must reproduce the above copyright
10231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block *    notice, this list of conditions and the following disclaimer in the
11231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block *    documentation and/or other materials provided with the distribution.
12231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block *
13231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block */
25231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
26231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block#include "config.h"
27231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block#include "CredentialStorage.h"
28231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
29231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block#include "Credential.h"
30231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block#include "KURL.h"
31231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block#include "ProtectionSpaceHash.h"
32a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch#include <wtf/text/StringConcatenate.h>
33f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick#include <wtf/text/StringHash.h>
34d0825bca7fe65beaee391d30da42e937db621564Steve Block#include <wtf/HashMap.h>
35d0825bca7fe65beaee391d30da42e937db621564Steve Block#include <wtf/HashSet.h>
36231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block#include <wtf/StdLibExtras.h>
37231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
38231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blocknamespace WebCore {
39231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
40231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blocktypedef HashMap<ProtectionSpace, Credential> ProtectionSpaceToCredentialMap;
41231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockstatic ProtectionSpaceToCredentialMap& protectionSpaceToCredentialMap()
42231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block{
43231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    DEFINE_STATIC_LOCAL(ProtectionSpaceToCredentialMap, map, ());
44231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    return map;
45231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block}
46231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
47cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Blockstatic HashSet<String>& originsWithCredentials()
48231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block{
49cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    DEFINE_STATIC_LOCAL(HashSet<String>, set, ());
50cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    return set;
51cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block}
52cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
53cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Blocktypedef HashMap<String, ProtectionSpace> PathToDefaultProtectionSpaceMap;
54cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Blockstatic PathToDefaultProtectionSpaceMap& pathToDefaultProtectionSpaceMap()
55cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block{
56cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    DEFINE_STATIC_LOCAL(PathToDefaultProtectionSpaceMap, map, ());
57231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    return map;
58231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block}
59cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
60231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockstatic String originStringFromURL(const KURL& url)
61231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block{
62231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    if (url.port())
63a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        return makeString(url.protocol(), "://", url.host(), ':', String::number(url.port()), '/');
64a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
65a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    return makeString(url.protocol(), "://", url.host(), '/');
66231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block}
67231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
68cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Blockstatic String protectionSpaceMapKeyFromURL(const KURL& url)
69cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block{
70cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    ASSERT(url.isValid());
71cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
72cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    // Remove the last path component that is not a directory to determine the subtree for which credentials will apply.
73cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    // We keep a leading slash, but remove a trailing one.
74cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    String directoryURL = url.string().substring(0, url.pathEnd());
75cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    unsigned directoryURLPathStart = url.pathStart();
76cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    ASSERT(directoryURL[directoryURLPathStart] == '/');
77cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    if (directoryURL.length() > directoryURLPathStart + 1) {
78f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick        size_t index = directoryURL.reverseFind('/');
79f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick        ASSERT(index != notFound);
80f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick        directoryURL = directoryURL.substring(0, (index != directoryURLPathStart) ? index : directoryURLPathStart + 1);
81cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    }
82cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
83cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    return directoryURL;
84cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block}
85cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
86231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockvoid CredentialStorage::set(const Credential& credential, const ProtectionSpace& protectionSpace, const KURL& url)
87231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block{
88643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    ASSERT(protectionSpace.isProxy() || url.protocolInHTTPFamily());
89643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    ASSERT(protectionSpace.isProxy() || url.isValid());
90231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
91231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    protectionSpaceToCredentialMap().set(protectionSpace, credential);
92643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if (!protectionSpace.isProxy()) {
93643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        originsWithCredentials().add(originStringFromURL(url));
94643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
95643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        ProtectionSpaceAuthenticationScheme scheme = protectionSpace.authenticationScheme();
96643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        if (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic || scheme == ProtectionSpaceAuthenticationSchemeDefault) {
97643ca7872b450ea4efacab6188849e5aac2ba161Steve Block            // The map can contain both a path and its subpath - while redundant, this makes lookups faster.
98643ca7872b450ea4efacab6188849e5aac2ba161Steve Block            pathToDefaultProtectionSpaceMap().set(protectionSpaceMapKeyFromURL(url), protectionSpace);
99643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        }
100231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    }
101231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block}
102231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
103231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve BlockCredential CredentialStorage::get(const ProtectionSpace& protectionSpace)
104231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block{
105231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    return protectionSpaceToCredentialMap().get(protectionSpace);
106231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block}
107231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
108967717af5423377c967781471ee106e2bb4e11c8Ben Murdochvoid CredentialStorage::remove(const ProtectionSpace& protectionSpace)
109967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch{
110967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch    protectionSpaceToCredentialMap().remove(protectionSpace);
111967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch}
112967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch
113cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Blockstatic PathToDefaultProtectionSpaceMap::iterator findDefaultProtectionSpaceForURL(const KURL& url)
114231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block{
115231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    ASSERT(url.protocolInHTTPFamily());
116cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    ASSERT(url.isValid());
117cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
118cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    PathToDefaultProtectionSpaceMap& map = pathToDefaultProtectionSpaceMap();
119cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
120cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    // Don't spend time iterating the path for origins that don't have any credentials.
121cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    if (!originsWithCredentials().contains(originStringFromURL(url)))
122cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block        return map.end();
123cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
124cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    String directoryURL = protectionSpaceMapKeyFromURL(url);
125cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    unsigned directoryURLPathStart = url.pathStart();
126cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    while (true) {
127cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block        PathToDefaultProtectionSpaceMap::iterator iter = map.find(directoryURL);
128cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block        if (iter != map.end())
129cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block            return iter;
130cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
131cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block        if (directoryURL.length() == directoryURLPathStart + 1)  // path is "/" already, cannot shorten it any more
132cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block            return map.end();
133cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
134f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick        size_t index = directoryURL.reverseFind('/', directoryURL.length() - 2);
135f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick        ASSERT(index != notFound);
136f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick        directoryURL = directoryURL.substring(0, (index == directoryURLPathStart) ? index + 1 : index);
137cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block        ASSERT(directoryURL.length() > directoryURLPathStart);
138cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block        ASSERT(directoryURL.length() == directoryURLPathStart + 1 || directoryURL[directoryURL.length() - 1] != '/');
139231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    }
140cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block}
141cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
142cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Blockbool CredentialStorage::set(const Credential& credential, const KURL& url)
143cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block{
144cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    ASSERT(url.protocolInHTTPFamily());
145cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    ASSERT(url.isValid());
146cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
147cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    if (iter == pathToDefaultProtectionSpaceMap().end())
148cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block        return false;
149cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    ASSERT(originsWithCredentials().contains(originStringFromURL(url)));
150cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    protectionSpaceToCredentialMap().set(iter->second, credential);
151cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    return true;
152cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block}
153cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
154cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve BlockCredential CredentialStorage::get(const KURL& url)
155cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block{
156cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
157cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    if (iter == pathToDefaultProtectionSpaceMap().end())
158cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block        return Credential();
159cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    return protectionSpaceToCredentialMap().get(iter->second);
160231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block}
161231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
162231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block} // namespace WebCore
163