Back to index

lightning-sunbird  0.9+nobinonly
nsHttpAuthCache.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Darin Fisher <darin@netscape.com> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include <stdlib.h>
00040 #include "nsHttp.h"
00041 #include "nsHttpAuthCache.h"
00042 #include "nsString.h"
00043 #include "nsCRT.h"
00044 #include "prprf.h"
00045 
00046 static inline void
00047 GetAuthKey(const char *scheme, const char *host, PRInt32 port, nsCString &key)
00048 {
00049     key.Assign(scheme);
00050     key.AppendLiteral("://");
00051     key.Append(host);
00052     key.Append(':');
00053     key.AppendInt(port);
00054 }
00055 
00056 // return true if the two strings are equal or both empty.  an empty string
00057 // is either null or zero length.
00058 static PRBool
00059 StrEquivalent(const PRUnichar *a, const PRUnichar *b)
00060 {
00061     static const PRUnichar emptyStr[] = {0};
00062 
00063     if (!a)
00064         a = emptyStr;
00065     if (!b)
00066         b = emptyStr;
00067 
00068     return nsCRT::strcmp(a, b) == 0;
00069 }
00070 
00071 //-----------------------------------------------------------------------------
00072 // nsHttpAuthCache <public>
00073 //-----------------------------------------------------------------------------
00074 
00075 nsHttpAuthCache::nsHttpAuthCache()
00076     : mDB(nsnull)
00077 {
00078 }
00079 
00080 nsHttpAuthCache::~nsHttpAuthCache()
00081 {
00082     if (mDB)
00083         ClearAll();
00084 }
00085 
00086 nsresult
00087 nsHttpAuthCache::Init()
00088 {
00089     NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED);
00090 
00091     LOG(("nsHttpAuthCache::Init\n"));
00092 
00093     mDB = PL_NewHashTable(128, (PLHashFunction) PL_HashString,
00094                                (PLHashComparator) PL_CompareStrings,
00095                                (PLHashComparator) 0, &gHashAllocOps, this);
00096     if (!mDB)
00097         return NS_ERROR_OUT_OF_MEMORY;
00098 
00099     return NS_OK;
00100 }
00101 
00102 nsresult
00103 nsHttpAuthCache::GetAuthEntryForPath(const char *scheme,
00104                                      const char *host,
00105                                      PRInt32     port,
00106                                      const char *path,
00107                                      nsHttpAuthEntry **entry)
00108 {
00109     LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n",
00110         scheme, host, port, path));
00111 
00112     nsCAutoString key;
00113     nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key);
00114     if (!node)
00115         return NS_ERROR_NOT_AVAILABLE;
00116 
00117     *entry = node->LookupEntryByPath(path);
00118     return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
00119 }
00120 
00121 nsresult
00122 nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme,
00123                                        const char *host,
00124                                        PRInt32     port,
00125                                        const char *realm,
00126                                        nsHttpAuthEntry **entry)
00127 
00128 {
00129     LOG(("nsHttpAuthCache::GetAuthEntryForDomain [key=%s://%s:%d realm=%s]\n",
00130         scheme, host, port, realm));
00131 
00132     nsCAutoString key;
00133     nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key);
00134     if (!node)
00135         return NS_ERROR_NOT_AVAILABLE;
00136 
00137     *entry = node->LookupEntryByRealm(realm);
00138     return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
00139 }
00140 
00141 nsresult
00142 nsHttpAuthCache::SetAuthEntry(const char *scheme,
00143                               const char *host,
00144                               PRInt32     port,
00145                               const char *path,
00146                               const char *realm,
00147                               const char *creds,
00148                               const char *challenge,
00149                               const nsHttpAuthIdentity &ident,
00150                               nsISupports *metadata)
00151 {
00152     nsresult rv;
00153 
00154     LOG(("nsHttpAuthCache::SetAuthEntry [key=%s://%s:%d realm=%s path=%s metadata=%x]\n",
00155         scheme, host, port, realm, path, metadata));
00156 
00157     if (!mDB) {
00158         rv = Init();
00159         if (NS_FAILED(rv)) return rv;
00160     }
00161 
00162     nsCAutoString key;
00163     nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key);
00164 
00165     if (!node) {
00166         // create a new entry node and set the given entry
00167         node = new nsHttpAuthNode();
00168         if (!node)
00169             return NS_ERROR_OUT_OF_MEMORY;
00170         rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
00171         if (NS_FAILED(rv))
00172             delete node;
00173         else
00174             PL_HashTableAdd(mDB, nsCRT::strdup(key.get()), node);
00175         return rv;
00176     }
00177 
00178     return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
00179 }
00180 
00181 void
00182 nsHttpAuthCache::ClearAuthEntry(const char *scheme,
00183                                 const char *host,
00184                                 PRInt32     port,
00185                                 const char *realm)
00186 {
00187     if (!mDB)
00188         return;
00189 
00190     nsCAutoString key;
00191     GetAuthKey(scheme, host, port, key);
00192     PL_HashTableRemove(mDB, key.get());
00193 }
00194 
00195 nsresult
00196 nsHttpAuthCache::ClearAll()
00197 {
00198     LOG(("nsHttpAuthCache::ClearAll\n"));
00199 
00200     if (mDB) {
00201         PL_HashTableDestroy(mDB);
00202         mDB = 0;
00203     }
00204     return NS_OK;
00205 }
00206 
00207 //-----------------------------------------------------------------------------
00208 // nsHttpAuthCache <private>
00209 //-----------------------------------------------------------------------------
00210 
00211 nsHttpAuthNode *
00212 nsHttpAuthCache::LookupAuthNode(const char *scheme,
00213                                 const char *host,
00214                                 PRInt32     port,
00215                                 nsCString  &key)
00216 {
00217     if (!mDB)
00218         return nsnull;
00219 
00220     GetAuthKey(scheme, host, port, key);
00221 
00222     return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get());
00223 }
00224 
00225 void *
00226 nsHttpAuthCache::AllocTable(void *self, PRSize size)
00227 {
00228     return malloc(size);
00229 }
00230 
00231 void
00232 nsHttpAuthCache::FreeTable(void *self, void *item)
00233 {
00234     free(item);
00235 }
00236 
00237 PLHashEntry *
00238 nsHttpAuthCache::AllocEntry(void *self, const void *key)
00239 {
00240     return (PLHashEntry *) malloc(sizeof(PLHashEntry));
00241 }
00242 
00243 void
00244 nsHttpAuthCache::FreeEntry(void *self, PLHashEntry *he, PRUintn flag)
00245 {
00246     if (flag == HT_FREE_VALUE) {
00247         // this would only happen if PL_HashTableAdd were to replace an
00248         // existing entry in the hash table, but we _always_ do a lookup
00249         // before adding a new entry to avoid this case.
00250         NS_NOTREACHED("should never happen");
00251     }
00252     else if (flag == HT_FREE_ENTRY) {
00253         // three wonderful flavors of freeing memory ;-)
00254         delete (nsHttpAuthNode *) he->value;
00255         nsCRT::free((char *) he->key);
00256         free(he);
00257     }
00258 }
00259 
00260 PLHashAllocOps nsHttpAuthCache::gHashAllocOps =
00261 {
00262     nsHttpAuthCache::AllocTable,
00263     nsHttpAuthCache::FreeTable,
00264     nsHttpAuthCache::AllocEntry,
00265     nsHttpAuthCache::FreeEntry
00266 };
00267 
00268 //-----------------------------------------------------------------------------
00269 // nsHttpAuthIdentity
00270 //-----------------------------------------------------------------------------
00271 
00272 nsresult
00273 nsHttpAuthIdentity::Set(const PRUnichar *domain,
00274                         const PRUnichar *user,
00275                         const PRUnichar *pass)
00276 {
00277     PRUnichar *newUser, *newPass, *newDomain;
00278 
00279     int domainLen = domain ? nsCRT::strlen(domain) : 0;
00280     int userLen   = user   ? nsCRT::strlen(user)   : 0;
00281     int passLen   = pass   ? nsCRT::strlen(pass)   : 0; 
00282 
00283     int len = userLen + 1 + passLen + 1 + domainLen + 1;
00284     newUser = (PRUnichar *) malloc(len * sizeof(PRUnichar));
00285     if (!newUser)
00286         return NS_ERROR_OUT_OF_MEMORY;
00287 
00288     if (user)
00289         memcpy(newUser, user, userLen * sizeof(PRUnichar));
00290     newUser[userLen] = 0;
00291 
00292     newPass = &newUser[userLen + 1];
00293     if (pass)
00294         memcpy(newPass, pass, passLen * sizeof(PRUnichar));
00295     newPass[passLen] = 0;
00296 
00297     newDomain = &newPass[passLen + 1];
00298     if (domain)
00299         memcpy(newDomain, domain, domainLen * sizeof(PRUnichar));
00300     newDomain[domainLen] = 0;
00301 
00302     // wait until the end to clear member vars in case input params
00303     // reference our members!
00304     if (mUser)
00305         free(mUser);
00306     mUser = newUser;
00307     mPass = newPass;
00308     mDomain = newDomain;
00309     return NS_OK;
00310 }
00311 
00312 void
00313 nsHttpAuthIdentity::Clear()
00314 {
00315     if (mUser) {
00316         free(mUser);
00317         mUser = nsnull;
00318         mPass = nsnull;
00319         mDomain = nsnull;
00320     }
00321 }
00322 
00323 PRBool
00324 nsHttpAuthIdentity::Equals(const nsHttpAuthIdentity &ident) const
00325 {
00326     // we could probably optimize this with a single loop, but why bother?
00327     return StrEquivalent(mUser, ident.mUser) &&
00328            StrEquivalent(mPass, ident.mPass) &&
00329            StrEquivalent(mDomain, ident.mDomain);
00330 }
00331 
00332 //-----------------------------------------------------------------------------
00333 // nsHttpAuthEntry
00334 //-----------------------------------------------------------------------------
00335 
00336 nsHttpAuthEntry::~nsHttpAuthEntry()
00337 {
00338     if (mRealm)
00339         free(mRealm);
00340 
00341     while (mRoot) {
00342         nsHttpAuthPath *ap = mRoot;
00343         mRoot = mRoot->mNext;
00344         free(ap);
00345     }
00346 }
00347 
00348 nsresult
00349 nsHttpAuthEntry::AddPath(const char *aPath)
00350 {
00351     // null path matches empty path
00352     if (!aPath)
00353         aPath = "";
00354 
00355     nsHttpAuthPath *tempPtr = mRoot;
00356     while (tempPtr) {
00357         const char *curpath = tempPtr->mPath;
00358         if (strncmp(aPath, curpath, nsCRT::strlen(curpath)) == 0)
00359             return NS_OK; // subpath already exists in the list
00360 
00361         tempPtr = tempPtr->mNext;
00362 
00363     }
00364     
00365     //Append the aPath
00366     nsHttpAuthPath *newAuthPath;
00367     int newpathLen = nsCRT::strlen(aPath);
00368     newAuthPath = (nsHttpAuthPath *) malloc(sizeof(nsHttpAuthPath) + newpathLen);
00369     if (!newAuthPath)
00370         return NS_ERROR_OUT_OF_MEMORY;
00371 
00372     memcpy(newAuthPath->mPath, aPath, newpathLen+1);
00373     newAuthPath->mNext = nsnull;
00374 
00375     if (!mRoot)
00376         mRoot = newAuthPath; //first entry
00377     else
00378         mTail->mNext = newAuthPath; // Append newAuthPath
00379 
00380     //update the tail pointer.
00381     mTail = newAuthPath;
00382     return NS_OK;
00383 }
00384 
00385 nsresult
00386 nsHttpAuthEntry::Set(const char *path,
00387                      const char *realm,
00388                      const char *creds,
00389                      const char *chall,
00390                      const nsHttpAuthIdentity &ident,
00391                      nsISupports *metadata)
00392 {
00393     char *newRealm, *newCreds, *newChall;
00394 
00395     int realmLen = realm ? nsCRT::strlen(realm) : 0;
00396     int credsLen = creds ? nsCRT::strlen(creds) : 0;
00397     int challLen = chall ? nsCRT::strlen(chall) : 0;
00398 
00399     int len = realmLen + 1 + credsLen + 1 + challLen + 1;
00400     newRealm = (char *) malloc(len);
00401     if (!newRealm)
00402         return NS_ERROR_OUT_OF_MEMORY;
00403 
00404     if (realm)
00405         memcpy(newRealm, realm, realmLen);
00406     newRealm[realmLen] = 0;
00407 
00408     newCreds = &newRealm[realmLen + 1];
00409     if (creds)
00410         memcpy(newCreds, creds, credsLen);
00411     newCreds[credsLen] = 0;
00412 
00413     newChall = &newCreds[credsLen + 1];
00414     if (chall)
00415         memcpy(newChall, chall, challLen);
00416     newChall[challLen] = 0;
00417 
00418     nsresult rv = mIdent.Set(ident);
00419     if (NS_FAILED(rv)) {
00420         free(newRealm);
00421         return rv;
00422     }
00423 
00424     rv = AddPath(path);
00425     if (NS_FAILED(rv)) {
00426         free(newRealm);
00427         return rv;
00428     }
00429 
00430     // wait until the end to clear member vars in case input params
00431     // reference our members!
00432     if (mRealm)
00433         free(mRealm);
00434 
00435     mRealm = newRealm;
00436     mCreds = newCreds;
00437     mChallenge = newChall;
00438     mMetaData = metadata;
00439 
00440     return NS_OK;
00441 }
00442 
00443 //-----------------------------------------------------------------------------
00444 // nsHttpAuthNode
00445 //-----------------------------------------------------------------------------
00446 
00447 nsHttpAuthNode::nsHttpAuthNode()
00448 {
00449     LOG(("Creating nsHttpAuthNode @%x\n", this));
00450 }
00451 
00452 nsHttpAuthNode::~nsHttpAuthNode()
00453 {
00454     LOG(("Destroying nsHttpAuthNode @%x\n", this));
00455 
00456     PRInt32 i;
00457     for (i=0; i<mList.Count(); ++i)
00458         delete (nsHttpAuthEntry *) mList[i];
00459     mList.Clear();
00460 }
00461 
00462 nsHttpAuthEntry *
00463 nsHttpAuthNode::LookupEntryByPath(const char *path)
00464 {
00465     nsHttpAuthEntry *entry;
00466 
00467     // null path matches empty path
00468     if (!path)
00469         path = "";
00470 
00471     // look for an entry that either matches or contains this directory.
00472     // ie. we'll give out credentials if the given directory is a sub-
00473     // directory of an existing entry.
00474     for (PRInt32 i=0; i<mList.Count(); ++i) {
00475         entry = (nsHttpAuthEntry *) mList[i];
00476         nsHttpAuthPath *authPath = entry->RootPath();
00477         while (authPath) {
00478             const char *entryPath = authPath->mPath;
00479             // proxy auth entries have no path, so require exact match on
00480             // empty path string.
00481             if (entryPath[0] == '\0') {
00482                 if (path[0] == '\0')
00483                     return entry;
00484             }
00485             else if (strncmp(path, entryPath, nsCRT::strlen(entryPath)) == 0)
00486                 return entry;
00487 
00488             authPath = authPath->mNext;
00489         }
00490     }
00491     return nsnull;
00492 }
00493 
00494 nsHttpAuthEntry *
00495 nsHttpAuthNode::LookupEntryByRealm(const char *realm)
00496 {
00497     nsHttpAuthEntry *entry;
00498 
00499     // null realm matches empty realm
00500     if (!realm)
00501         realm = "";
00502 
00503     // look for an entry that matches this realm
00504     PRInt32 i;
00505     for (i=0; i<mList.Count(); ++i) {
00506         entry = (nsHttpAuthEntry *) mList[i];
00507         if (strcmp(realm, entry->Realm()) == 0)
00508             return entry;
00509     }
00510     return nsnull;
00511 }
00512 
00513 nsresult
00514 nsHttpAuthNode::SetAuthEntry(const char *path,
00515                              const char *realm,
00516                              const char *creds,
00517                              const char *challenge,
00518                              const nsHttpAuthIdentity &ident,
00519                              nsISupports *metadata)
00520 {
00521     // look for an entry with a matching realm
00522     nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
00523     if (!entry) {
00524         entry = new nsHttpAuthEntry(path, realm, creds, challenge, ident, metadata);
00525         if (!entry)
00526             return NS_ERROR_OUT_OF_MEMORY;
00527         mList.AppendElement(entry);
00528     }
00529     else {
00530         // update the entry...
00531         entry->Set(path, realm, creds, challenge, ident, metadata);
00532     }
00533 
00534     return NS_OK;
00535 }
00536 
00537 void
00538 nsHttpAuthNode::ClearAuthEntry(const char *realm)
00539 {
00540     nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
00541     if (entry) {
00542         mList.RemoveElement(entry); // double search OK
00543         delete entry;
00544     }
00545 }