Home | History | Annotate | Download | only in network
      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