1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/nsHttpAuthCache.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,613 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// HttpLog.h should generally be included first 1.10 +#include "HttpLog.h" 1.11 + 1.12 +#include "nsHttpAuthCache.h" 1.13 + 1.14 +#include <stdlib.h> 1.15 + 1.16 +#include "mozilla/Attributes.h" 1.17 +#include "nsString.h" 1.18 +#include "nsCRT.h" 1.19 +#include "mozIApplicationClearPrivateDataParams.h" 1.20 +#include "nsIObserverService.h" 1.21 +#include "mozilla/Services.h" 1.22 +#include "nsNetUtil.h" 1.23 + 1.24 +namespace mozilla { 1.25 +namespace net { 1.26 + 1.27 +static inline void 1.28 +GetAuthKey(const char *scheme, const char *host, int32_t port, uint32_t appId, bool inBrowserElement, nsCString &key) 1.29 +{ 1.30 + key.Truncate(); 1.31 + key.AppendInt(appId); 1.32 + key.Append(':'); 1.33 + key.AppendInt(inBrowserElement); 1.34 + key.Append(':'); 1.35 + key.Append(scheme); 1.36 + key.AppendLiteral("://"); 1.37 + key.Append(host); 1.38 + key.Append(':'); 1.39 + key.AppendInt(port); 1.40 +} 1.41 + 1.42 +// return true if the two strings are equal or both empty. an empty string 1.43 +// is either null or zero length. 1.44 +static bool 1.45 +StrEquivalent(const char16_t *a, const char16_t *b) 1.46 +{ 1.47 + static const char16_t emptyStr[] = {0}; 1.48 + 1.49 + if (!a) 1.50 + a = emptyStr; 1.51 + if (!b) 1.52 + b = emptyStr; 1.53 + 1.54 + return nsCRT::strcmp(a, b) == 0; 1.55 +} 1.56 + 1.57 +//----------------------------------------------------------------------------- 1.58 +// nsHttpAuthCache <public> 1.59 +//----------------------------------------------------------------------------- 1.60 + 1.61 +nsHttpAuthCache::nsHttpAuthCache() 1.62 + : mDB(nullptr) 1.63 + , mObserver(new AppDataClearObserver(MOZ_THIS_IN_INITIALIZER_LIST())) 1.64 +{ 1.65 + nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService(); 1.66 + if (obsSvc) { 1.67 + obsSvc->AddObserver(mObserver, "webapps-clear-data", false); 1.68 + } 1.69 +} 1.70 + 1.71 +nsHttpAuthCache::~nsHttpAuthCache() 1.72 +{ 1.73 + if (mDB) 1.74 + ClearAll(); 1.75 + nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService(); 1.76 + if (obsSvc) { 1.77 + obsSvc->RemoveObserver(mObserver, "webapps-clear-data"); 1.78 + mObserver->mOwner = nullptr; 1.79 + } 1.80 +} 1.81 + 1.82 +nsresult 1.83 +nsHttpAuthCache::Init() 1.84 +{ 1.85 + NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED); 1.86 + 1.87 + LOG(("nsHttpAuthCache::Init\n")); 1.88 + 1.89 + mDB = PL_NewHashTable(128, (PLHashFunction) PL_HashString, 1.90 + (PLHashComparator) PL_CompareStrings, 1.91 + (PLHashComparator) 0, &gHashAllocOps, this); 1.92 + if (!mDB) 1.93 + return NS_ERROR_OUT_OF_MEMORY; 1.94 + 1.95 + return NS_OK; 1.96 +} 1.97 + 1.98 +nsresult 1.99 +nsHttpAuthCache::GetAuthEntryForPath(const char *scheme, 1.100 + const char *host, 1.101 + int32_t port, 1.102 + const char *path, 1.103 + uint32_t appId, 1.104 + bool inBrowserElement, 1.105 + nsHttpAuthEntry **entry) 1.106 +{ 1.107 + LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n", 1.108 + scheme, host, port, path)); 1.109 + 1.110 + nsAutoCString key; 1.111 + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); 1.112 + if (!node) 1.113 + return NS_ERROR_NOT_AVAILABLE; 1.114 + 1.115 + *entry = node->LookupEntryByPath(path); 1.116 + return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE; 1.117 +} 1.118 + 1.119 +nsresult 1.120 +nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme, 1.121 + const char *host, 1.122 + int32_t port, 1.123 + const char *realm, 1.124 + uint32_t appId, 1.125 + bool inBrowserElement, 1.126 + nsHttpAuthEntry **entry) 1.127 + 1.128 +{ 1.129 + LOG(("nsHttpAuthCache::GetAuthEntryForDomain [key=%s://%s:%d realm=%s]\n", 1.130 + scheme, host, port, realm)); 1.131 + 1.132 + nsAutoCString key; 1.133 + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); 1.134 + if (!node) 1.135 + return NS_ERROR_NOT_AVAILABLE; 1.136 + 1.137 + *entry = node->LookupEntryByRealm(realm); 1.138 + return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE; 1.139 +} 1.140 + 1.141 +nsresult 1.142 +nsHttpAuthCache::SetAuthEntry(const char *scheme, 1.143 + const char *host, 1.144 + int32_t port, 1.145 + const char *path, 1.146 + const char *realm, 1.147 + const char *creds, 1.148 + const char *challenge, 1.149 + uint32_t appId, 1.150 + bool inBrowserElement, 1.151 + const nsHttpAuthIdentity *ident, 1.152 + nsISupports *metadata) 1.153 +{ 1.154 + nsresult rv; 1.155 + 1.156 + LOG(("nsHttpAuthCache::SetAuthEntry [key=%s://%s:%d realm=%s path=%s metadata=%x]\n", 1.157 + scheme, host, port, realm, path, metadata)); 1.158 + 1.159 + if (!mDB) { 1.160 + rv = Init(); 1.161 + if (NS_FAILED(rv)) return rv; 1.162 + } 1.163 + 1.164 + nsAutoCString key; 1.165 + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); 1.166 + 1.167 + if (!node) { 1.168 + // create a new entry node and set the given entry 1.169 + node = new nsHttpAuthNode(); 1.170 + if (!node) 1.171 + return NS_ERROR_OUT_OF_MEMORY; 1.172 + rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata); 1.173 + if (NS_FAILED(rv)) 1.174 + delete node; 1.175 + else 1.176 + PL_HashTableAdd(mDB, strdup(key.get()), node); 1.177 + return rv; 1.178 + } 1.179 + 1.180 + return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata); 1.181 +} 1.182 + 1.183 +void 1.184 +nsHttpAuthCache::ClearAuthEntry(const char *scheme, 1.185 + const char *host, 1.186 + int32_t port, 1.187 + const char *realm, 1.188 + uint32_t appId, 1.189 + bool inBrowserElement) 1.190 +{ 1.191 + if (!mDB) 1.192 + return; 1.193 + 1.194 + nsAutoCString key; 1.195 + GetAuthKey(scheme, host, port, appId, inBrowserElement, key); 1.196 + PL_HashTableRemove(mDB, key.get()); 1.197 +} 1.198 + 1.199 +nsresult 1.200 +nsHttpAuthCache::ClearAll() 1.201 +{ 1.202 + LOG(("nsHttpAuthCache::ClearAll\n")); 1.203 + 1.204 + if (mDB) { 1.205 + PL_HashTableDestroy(mDB); 1.206 + mDB = 0; 1.207 + } 1.208 + return NS_OK; 1.209 +} 1.210 + 1.211 +//----------------------------------------------------------------------------- 1.212 +// nsHttpAuthCache <private> 1.213 +//----------------------------------------------------------------------------- 1.214 + 1.215 +nsHttpAuthNode * 1.216 +nsHttpAuthCache::LookupAuthNode(const char *scheme, 1.217 + const char *host, 1.218 + int32_t port, 1.219 + uint32_t appId, 1.220 + bool inBrowserElement, 1.221 + nsCString &key) 1.222 +{ 1.223 + if (!mDB) 1.224 + return nullptr; 1.225 + 1.226 + GetAuthKey(scheme, host, port, appId, inBrowserElement, key); 1.227 + 1.228 + return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get()); 1.229 +} 1.230 + 1.231 +void * 1.232 +nsHttpAuthCache::AllocTable(void *self, size_t size) 1.233 +{ 1.234 + return malloc(size); 1.235 +} 1.236 + 1.237 +void 1.238 +nsHttpAuthCache::FreeTable(void *self, void *item) 1.239 +{ 1.240 + free(item); 1.241 +} 1.242 + 1.243 +PLHashEntry * 1.244 +nsHttpAuthCache::AllocEntry(void *self, const void *key) 1.245 +{ 1.246 + return (PLHashEntry *) malloc(sizeof(PLHashEntry)); 1.247 +} 1.248 + 1.249 +void 1.250 +nsHttpAuthCache::FreeEntry(void *self, PLHashEntry *he, unsigned flag) 1.251 +{ 1.252 + if (flag == HT_FREE_VALUE) { 1.253 + // this would only happen if PL_HashTableAdd were to replace an 1.254 + // existing entry in the hash table, but we _always_ do a lookup 1.255 + // before adding a new entry to avoid this case. 1.256 + NS_NOTREACHED("should never happen"); 1.257 + } 1.258 + else if (flag == HT_FREE_ENTRY) { 1.259 + // three wonderful flavors of freeing memory ;-) 1.260 + delete (nsHttpAuthNode *) he->value; 1.261 + free((char *) he->key); 1.262 + free(he); 1.263 + } 1.264 +} 1.265 + 1.266 +PLHashAllocOps nsHttpAuthCache::gHashAllocOps = 1.267 +{ 1.268 + nsHttpAuthCache::AllocTable, 1.269 + nsHttpAuthCache::FreeTable, 1.270 + nsHttpAuthCache::AllocEntry, 1.271 + nsHttpAuthCache::FreeEntry 1.272 +}; 1.273 + 1.274 +NS_IMPL_ISUPPORTS(nsHttpAuthCache::AppDataClearObserver, nsIObserver) 1.275 + 1.276 +NS_IMETHODIMP 1.277 +nsHttpAuthCache::AppDataClearObserver::Observe(nsISupports *subject, 1.278 + const char * topic, 1.279 + const char16_t * data_unicode) 1.280 +{ 1.281 + NS_ENSURE_TRUE(mOwner, NS_ERROR_NOT_AVAILABLE); 1.282 + 1.283 + nsCOMPtr<mozIApplicationClearPrivateDataParams> params = 1.284 + do_QueryInterface(subject); 1.285 + if (!params) { 1.286 + NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); 1.287 + return NS_ERROR_UNEXPECTED; 1.288 + } 1.289 + 1.290 + uint32_t appId; 1.291 + bool browserOnly; 1.292 + 1.293 + nsresult rv = params->GetAppId(&appId); 1.294 + NS_ENSURE_SUCCESS(rv, rv); 1.295 + rv = params->GetBrowserOnly(&browserOnly); 1.296 + NS_ENSURE_SUCCESS(rv, rv); 1.297 + 1.298 + MOZ_ASSERT(appId != NECKO_UNKNOWN_APP_ID); 1.299 + mOwner->ClearAppData(appId, browserOnly); 1.300 + return NS_OK; 1.301 +} 1.302 + 1.303 +static int 1.304 +RemoveEntriesForApp(PLHashEntry *entry, int32_t number, void *arg) 1.305 +{ 1.306 + nsDependentCString key(static_cast<const char*>(entry->key)); 1.307 + nsAutoCString* prefix = static_cast<nsAutoCString*>(arg); 1.308 + if (StringBeginsWith(key, *prefix)) { 1.309 + return HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE; 1.310 + } 1.311 + return HT_ENUMERATE_NEXT; 1.312 +} 1.313 + 1.314 +void 1.315 +nsHttpAuthCache::ClearAppData(uint32_t appId, bool browserOnly) 1.316 +{ 1.317 + if (!mDB) { 1.318 + return; 1.319 + } 1.320 + nsAutoCString keyPrefix; 1.321 + keyPrefix.AppendInt(appId); 1.322 + keyPrefix.Append(':'); 1.323 + if (browserOnly) { 1.324 + keyPrefix.AppendInt(browserOnly); 1.325 + keyPrefix.Append(':'); 1.326 + } 1.327 + PL_HashTableEnumerateEntries(mDB, RemoveEntriesForApp, &keyPrefix); 1.328 +} 1.329 + 1.330 +//----------------------------------------------------------------------------- 1.331 +// nsHttpAuthIdentity 1.332 +//----------------------------------------------------------------------------- 1.333 + 1.334 +nsresult 1.335 +nsHttpAuthIdentity::Set(const char16_t *domain, 1.336 + const char16_t *user, 1.337 + const char16_t *pass) 1.338 +{ 1.339 + char16_t *newUser, *newPass, *newDomain; 1.340 + 1.341 + int domainLen = domain ? NS_strlen(domain) : 0; 1.342 + int userLen = user ? NS_strlen(user) : 0; 1.343 + int passLen = pass ? NS_strlen(pass) : 0; 1.344 + 1.345 + int len = userLen + 1 + passLen + 1 + domainLen + 1; 1.346 + newUser = (char16_t *) malloc(len * sizeof(char16_t)); 1.347 + if (!newUser) 1.348 + return NS_ERROR_OUT_OF_MEMORY; 1.349 + 1.350 + if (user) 1.351 + memcpy(newUser, user, userLen * sizeof(char16_t)); 1.352 + newUser[userLen] = 0; 1.353 + 1.354 + newPass = &newUser[userLen + 1]; 1.355 + if (pass) 1.356 + memcpy(newPass, pass, passLen * sizeof(char16_t)); 1.357 + newPass[passLen] = 0; 1.358 + 1.359 + newDomain = &newPass[passLen + 1]; 1.360 + if (domain) 1.361 + memcpy(newDomain, domain, domainLen * sizeof(char16_t)); 1.362 + newDomain[domainLen] = 0; 1.363 + 1.364 + // wait until the end to clear member vars in case input params 1.365 + // reference our members! 1.366 + if (mUser) 1.367 + free(mUser); 1.368 + mUser = newUser; 1.369 + mPass = newPass; 1.370 + mDomain = newDomain; 1.371 + return NS_OK; 1.372 +} 1.373 + 1.374 +void 1.375 +nsHttpAuthIdentity::Clear() 1.376 +{ 1.377 + if (mUser) { 1.378 + free(mUser); 1.379 + mUser = nullptr; 1.380 + mPass = nullptr; 1.381 + mDomain = nullptr; 1.382 + } 1.383 +} 1.384 + 1.385 +bool 1.386 +nsHttpAuthIdentity::Equals(const nsHttpAuthIdentity &ident) const 1.387 +{ 1.388 + // we could probably optimize this with a single loop, but why bother? 1.389 + return StrEquivalent(mUser, ident.mUser) && 1.390 + StrEquivalent(mPass, ident.mPass) && 1.391 + StrEquivalent(mDomain, ident.mDomain); 1.392 +} 1.393 + 1.394 +//----------------------------------------------------------------------------- 1.395 +// nsHttpAuthEntry 1.396 +//----------------------------------------------------------------------------- 1.397 + 1.398 +nsHttpAuthEntry::~nsHttpAuthEntry() 1.399 +{ 1.400 + if (mRealm) 1.401 + free(mRealm); 1.402 + 1.403 + while (mRoot) { 1.404 + nsHttpAuthPath *ap = mRoot; 1.405 + mRoot = mRoot->mNext; 1.406 + free(ap); 1.407 + } 1.408 +} 1.409 + 1.410 +nsresult 1.411 +nsHttpAuthEntry::AddPath(const char *aPath) 1.412 +{ 1.413 + // null path matches empty path 1.414 + if (!aPath) 1.415 + aPath = ""; 1.416 + 1.417 + nsHttpAuthPath *tempPtr = mRoot; 1.418 + while (tempPtr) { 1.419 + const char *curpath = tempPtr->mPath; 1.420 + if (strncmp(aPath, curpath, strlen(curpath)) == 0) 1.421 + return NS_OK; // subpath already exists in the list 1.422 + 1.423 + tempPtr = tempPtr->mNext; 1.424 + 1.425 + } 1.426 + 1.427 + //Append the aPath 1.428 + nsHttpAuthPath *newAuthPath; 1.429 + int newpathLen = strlen(aPath); 1.430 + newAuthPath = (nsHttpAuthPath *) malloc(sizeof(nsHttpAuthPath) + newpathLen); 1.431 + if (!newAuthPath) 1.432 + return NS_ERROR_OUT_OF_MEMORY; 1.433 + 1.434 + memcpy(newAuthPath->mPath, aPath, newpathLen+1); 1.435 + newAuthPath->mNext = nullptr; 1.436 + 1.437 + if (!mRoot) 1.438 + mRoot = newAuthPath; //first entry 1.439 + else 1.440 + mTail->mNext = newAuthPath; // Append newAuthPath 1.441 + 1.442 + //update the tail pointer. 1.443 + mTail = newAuthPath; 1.444 + return NS_OK; 1.445 +} 1.446 + 1.447 +nsresult 1.448 +nsHttpAuthEntry::Set(const char *path, 1.449 + const char *realm, 1.450 + const char *creds, 1.451 + const char *chall, 1.452 + const nsHttpAuthIdentity *ident, 1.453 + nsISupports *metadata) 1.454 +{ 1.455 + char *newRealm, *newCreds, *newChall; 1.456 + 1.457 + int realmLen = realm ? strlen(realm) : 0; 1.458 + int credsLen = creds ? strlen(creds) : 0; 1.459 + int challLen = chall ? strlen(chall) : 0; 1.460 + 1.461 + int len = realmLen + 1 + credsLen + 1 + challLen + 1; 1.462 + newRealm = (char *) malloc(len); 1.463 + if (!newRealm) 1.464 + return NS_ERROR_OUT_OF_MEMORY; 1.465 + 1.466 + if (realm) 1.467 + memcpy(newRealm, realm, realmLen); 1.468 + newRealm[realmLen] = 0; 1.469 + 1.470 + newCreds = &newRealm[realmLen + 1]; 1.471 + if (creds) 1.472 + memcpy(newCreds, creds, credsLen); 1.473 + newCreds[credsLen] = 0; 1.474 + 1.475 + newChall = &newCreds[credsLen + 1]; 1.476 + if (chall) 1.477 + memcpy(newChall, chall, challLen); 1.478 + newChall[challLen] = 0; 1.479 + 1.480 + nsresult rv = NS_OK; 1.481 + if (ident) { 1.482 + rv = mIdent.Set(*ident); 1.483 + } 1.484 + else if (mIdent.IsEmpty()) { 1.485 + // If we are not given an identity and our cached identity has not been 1.486 + // initialized yet (so is currently empty), initialize it now by 1.487 + // filling it with nulls. We need to do that because consumers expect 1.488 + // that mIdent is initialized after this function returns. 1.489 + rv = mIdent.Set(nullptr, nullptr, nullptr); 1.490 + } 1.491 + if (NS_FAILED(rv)) { 1.492 + free(newRealm); 1.493 + return rv; 1.494 + } 1.495 + 1.496 + rv = AddPath(path); 1.497 + if (NS_FAILED(rv)) { 1.498 + free(newRealm); 1.499 + return rv; 1.500 + } 1.501 + 1.502 + // wait until the end to clear member vars in case input params 1.503 + // reference our members! 1.504 + if (mRealm) 1.505 + free(mRealm); 1.506 + 1.507 + mRealm = newRealm; 1.508 + mCreds = newCreds; 1.509 + mChallenge = newChall; 1.510 + mMetaData = metadata; 1.511 + 1.512 + return NS_OK; 1.513 +} 1.514 + 1.515 +//----------------------------------------------------------------------------- 1.516 +// nsHttpAuthNode 1.517 +//----------------------------------------------------------------------------- 1.518 + 1.519 +nsHttpAuthNode::nsHttpAuthNode() 1.520 +{ 1.521 + LOG(("Creating nsHttpAuthNode @%x\n", this)); 1.522 +} 1.523 + 1.524 +nsHttpAuthNode::~nsHttpAuthNode() 1.525 +{ 1.526 + LOG(("Destroying nsHttpAuthNode @%x\n", this)); 1.527 + 1.528 + mList.Clear(); 1.529 +} 1.530 + 1.531 +nsHttpAuthEntry * 1.532 +nsHttpAuthNode::LookupEntryByPath(const char *path) 1.533 +{ 1.534 + nsHttpAuthEntry *entry; 1.535 + 1.536 + // null path matches empty path 1.537 + if (!path) 1.538 + path = ""; 1.539 + 1.540 + // look for an entry that either matches or contains this directory. 1.541 + // ie. we'll give out credentials if the given directory is a sub- 1.542 + // directory of an existing entry. 1.543 + for (uint32_t i=0; i<mList.Length(); ++i) { 1.544 + entry = mList[i]; 1.545 + nsHttpAuthPath *authPath = entry->RootPath(); 1.546 + while (authPath) { 1.547 + const char *entryPath = authPath->mPath; 1.548 + // proxy auth entries have no path, so require exact match on 1.549 + // empty path string. 1.550 + if (entryPath[0] == '\0') { 1.551 + if (path[0] == '\0') 1.552 + return entry; 1.553 + } 1.554 + else if (strncmp(path, entryPath, strlen(entryPath)) == 0) 1.555 + return entry; 1.556 + 1.557 + authPath = authPath->mNext; 1.558 + } 1.559 + } 1.560 + return nullptr; 1.561 +} 1.562 + 1.563 +nsHttpAuthEntry * 1.564 +nsHttpAuthNode::LookupEntryByRealm(const char *realm) 1.565 +{ 1.566 + nsHttpAuthEntry *entry; 1.567 + 1.568 + // null realm matches empty realm 1.569 + if (!realm) 1.570 + realm = ""; 1.571 + 1.572 + // look for an entry that matches this realm 1.573 + uint32_t i; 1.574 + for (i=0; i<mList.Length(); ++i) { 1.575 + entry = mList[i]; 1.576 + if (strcmp(realm, entry->Realm()) == 0) 1.577 + return entry; 1.578 + } 1.579 + return nullptr; 1.580 +} 1.581 + 1.582 +nsresult 1.583 +nsHttpAuthNode::SetAuthEntry(const char *path, 1.584 + const char *realm, 1.585 + const char *creds, 1.586 + const char *challenge, 1.587 + const nsHttpAuthIdentity *ident, 1.588 + nsISupports *metadata) 1.589 +{ 1.590 + // look for an entry with a matching realm 1.591 + nsHttpAuthEntry *entry = LookupEntryByRealm(realm); 1.592 + if (!entry) { 1.593 + entry = new nsHttpAuthEntry(path, realm, creds, challenge, ident, metadata); 1.594 + if (!entry) 1.595 + return NS_ERROR_OUT_OF_MEMORY; 1.596 + mList.AppendElement(entry); 1.597 + } 1.598 + else { 1.599 + // update the entry... 1.600 + entry->Set(path, realm, creds, challenge, ident, metadata); 1.601 + } 1.602 + 1.603 + return NS_OK; 1.604 +} 1.605 + 1.606 +void 1.607 +nsHttpAuthNode::ClearAuthEntry(const char *realm) 1.608 +{ 1.609 + nsHttpAuthEntry *entry = LookupEntryByRealm(realm); 1.610 + if (entry) { 1.611 + mList.RemoveElement(entry); // double search OK 1.612 + } 1.613 +} 1.614 + 1.615 +} // namespace mozilla::net 1.616 +} // namespace mozilla