Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // HttpLog.h should generally be included first
7 #include "HttpLog.h"
9 #include "nsHttpAuthCache.h"
11 #include <stdlib.h>
13 #include "mozilla/Attributes.h"
14 #include "nsString.h"
15 #include "nsCRT.h"
16 #include "mozIApplicationClearPrivateDataParams.h"
17 #include "nsIObserverService.h"
18 #include "mozilla/Services.h"
19 #include "nsNetUtil.h"
21 namespace mozilla {
22 namespace net {
24 static inline void
25 GetAuthKey(const char *scheme, const char *host, int32_t port, uint32_t appId, bool inBrowserElement, nsCString &key)
26 {
27 key.Truncate();
28 key.AppendInt(appId);
29 key.Append(':');
30 key.AppendInt(inBrowserElement);
31 key.Append(':');
32 key.Append(scheme);
33 key.AppendLiteral("://");
34 key.Append(host);
35 key.Append(':');
36 key.AppendInt(port);
37 }
39 // return true if the two strings are equal or both empty. an empty string
40 // is either null or zero length.
41 static bool
42 StrEquivalent(const char16_t *a, const char16_t *b)
43 {
44 static const char16_t emptyStr[] = {0};
46 if (!a)
47 a = emptyStr;
48 if (!b)
49 b = emptyStr;
51 return nsCRT::strcmp(a, b) == 0;
52 }
54 //-----------------------------------------------------------------------------
55 // nsHttpAuthCache <public>
56 //-----------------------------------------------------------------------------
58 nsHttpAuthCache::nsHttpAuthCache()
59 : mDB(nullptr)
60 , mObserver(new AppDataClearObserver(MOZ_THIS_IN_INITIALIZER_LIST()))
61 {
62 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
63 if (obsSvc) {
64 obsSvc->AddObserver(mObserver, "webapps-clear-data", false);
65 }
66 }
68 nsHttpAuthCache::~nsHttpAuthCache()
69 {
70 if (mDB)
71 ClearAll();
72 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
73 if (obsSvc) {
74 obsSvc->RemoveObserver(mObserver, "webapps-clear-data");
75 mObserver->mOwner = nullptr;
76 }
77 }
79 nsresult
80 nsHttpAuthCache::Init()
81 {
82 NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED);
84 LOG(("nsHttpAuthCache::Init\n"));
86 mDB = PL_NewHashTable(128, (PLHashFunction) PL_HashString,
87 (PLHashComparator) PL_CompareStrings,
88 (PLHashComparator) 0, &gHashAllocOps, this);
89 if (!mDB)
90 return NS_ERROR_OUT_OF_MEMORY;
92 return NS_OK;
93 }
95 nsresult
96 nsHttpAuthCache::GetAuthEntryForPath(const char *scheme,
97 const char *host,
98 int32_t port,
99 const char *path,
100 uint32_t appId,
101 bool inBrowserElement,
102 nsHttpAuthEntry **entry)
103 {
104 LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n",
105 scheme, host, port, path));
107 nsAutoCString key;
108 nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key);
109 if (!node)
110 return NS_ERROR_NOT_AVAILABLE;
112 *entry = node->LookupEntryByPath(path);
113 return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
114 }
116 nsresult
117 nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme,
118 const char *host,
119 int32_t port,
120 const char *realm,
121 uint32_t appId,
122 bool inBrowserElement,
123 nsHttpAuthEntry **entry)
125 {
126 LOG(("nsHttpAuthCache::GetAuthEntryForDomain [key=%s://%s:%d realm=%s]\n",
127 scheme, host, port, realm));
129 nsAutoCString key;
130 nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key);
131 if (!node)
132 return NS_ERROR_NOT_AVAILABLE;
134 *entry = node->LookupEntryByRealm(realm);
135 return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
136 }
138 nsresult
139 nsHttpAuthCache::SetAuthEntry(const char *scheme,
140 const char *host,
141 int32_t port,
142 const char *path,
143 const char *realm,
144 const char *creds,
145 const char *challenge,
146 uint32_t appId,
147 bool inBrowserElement,
148 const nsHttpAuthIdentity *ident,
149 nsISupports *metadata)
150 {
151 nsresult rv;
153 LOG(("nsHttpAuthCache::SetAuthEntry [key=%s://%s:%d realm=%s path=%s metadata=%x]\n",
154 scheme, host, port, realm, path, metadata));
156 if (!mDB) {
157 rv = Init();
158 if (NS_FAILED(rv)) return rv;
159 }
161 nsAutoCString key;
162 nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key);
164 if (!node) {
165 // create a new entry node and set the given entry
166 node = new nsHttpAuthNode();
167 if (!node)
168 return NS_ERROR_OUT_OF_MEMORY;
169 rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
170 if (NS_FAILED(rv))
171 delete node;
172 else
173 PL_HashTableAdd(mDB, strdup(key.get()), node);
174 return rv;
175 }
177 return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
178 }
180 void
181 nsHttpAuthCache::ClearAuthEntry(const char *scheme,
182 const char *host,
183 int32_t port,
184 const char *realm,
185 uint32_t appId,
186 bool inBrowserElement)
187 {
188 if (!mDB)
189 return;
191 nsAutoCString key;
192 GetAuthKey(scheme, host, port, appId, inBrowserElement, key);
193 PL_HashTableRemove(mDB, key.get());
194 }
196 nsresult
197 nsHttpAuthCache::ClearAll()
198 {
199 LOG(("nsHttpAuthCache::ClearAll\n"));
201 if (mDB) {
202 PL_HashTableDestroy(mDB);
203 mDB = 0;
204 }
205 return NS_OK;
206 }
208 //-----------------------------------------------------------------------------
209 // nsHttpAuthCache <private>
210 //-----------------------------------------------------------------------------
212 nsHttpAuthNode *
213 nsHttpAuthCache::LookupAuthNode(const char *scheme,
214 const char *host,
215 int32_t port,
216 uint32_t appId,
217 bool inBrowserElement,
218 nsCString &key)
219 {
220 if (!mDB)
221 return nullptr;
223 GetAuthKey(scheme, host, port, appId, inBrowserElement, key);
225 return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get());
226 }
228 void *
229 nsHttpAuthCache::AllocTable(void *self, size_t size)
230 {
231 return malloc(size);
232 }
234 void
235 nsHttpAuthCache::FreeTable(void *self, void *item)
236 {
237 free(item);
238 }
240 PLHashEntry *
241 nsHttpAuthCache::AllocEntry(void *self, const void *key)
242 {
243 return (PLHashEntry *) malloc(sizeof(PLHashEntry));
244 }
246 void
247 nsHttpAuthCache::FreeEntry(void *self, PLHashEntry *he, unsigned flag)
248 {
249 if (flag == HT_FREE_VALUE) {
250 // this would only happen if PL_HashTableAdd were to replace an
251 // existing entry in the hash table, but we _always_ do a lookup
252 // before adding a new entry to avoid this case.
253 NS_NOTREACHED("should never happen");
254 }
255 else if (flag == HT_FREE_ENTRY) {
256 // three wonderful flavors of freeing memory ;-)
257 delete (nsHttpAuthNode *) he->value;
258 free((char *) he->key);
259 free(he);
260 }
261 }
263 PLHashAllocOps nsHttpAuthCache::gHashAllocOps =
264 {
265 nsHttpAuthCache::AllocTable,
266 nsHttpAuthCache::FreeTable,
267 nsHttpAuthCache::AllocEntry,
268 nsHttpAuthCache::FreeEntry
269 };
271 NS_IMPL_ISUPPORTS(nsHttpAuthCache::AppDataClearObserver, nsIObserver)
273 NS_IMETHODIMP
274 nsHttpAuthCache::AppDataClearObserver::Observe(nsISupports *subject,
275 const char * topic,
276 const char16_t * data_unicode)
277 {
278 NS_ENSURE_TRUE(mOwner, NS_ERROR_NOT_AVAILABLE);
280 nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
281 do_QueryInterface(subject);
282 if (!params) {
283 NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
284 return NS_ERROR_UNEXPECTED;
285 }
287 uint32_t appId;
288 bool browserOnly;
290 nsresult rv = params->GetAppId(&appId);
291 NS_ENSURE_SUCCESS(rv, rv);
292 rv = params->GetBrowserOnly(&browserOnly);
293 NS_ENSURE_SUCCESS(rv, rv);
295 MOZ_ASSERT(appId != NECKO_UNKNOWN_APP_ID);
296 mOwner->ClearAppData(appId, browserOnly);
297 return NS_OK;
298 }
300 static int
301 RemoveEntriesForApp(PLHashEntry *entry, int32_t number, void *arg)
302 {
303 nsDependentCString key(static_cast<const char*>(entry->key));
304 nsAutoCString* prefix = static_cast<nsAutoCString*>(arg);
305 if (StringBeginsWith(key, *prefix)) {
306 return HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE;
307 }
308 return HT_ENUMERATE_NEXT;
309 }
311 void
312 nsHttpAuthCache::ClearAppData(uint32_t appId, bool browserOnly)
313 {
314 if (!mDB) {
315 return;
316 }
317 nsAutoCString keyPrefix;
318 keyPrefix.AppendInt(appId);
319 keyPrefix.Append(':');
320 if (browserOnly) {
321 keyPrefix.AppendInt(browserOnly);
322 keyPrefix.Append(':');
323 }
324 PL_HashTableEnumerateEntries(mDB, RemoveEntriesForApp, &keyPrefix);
325 }
327 //-----------------------------------------------------------------------------
328 // nsHttpAuthIdentity
329 //-----------------------------------------------------------------------------
331 nsresult
332 nsHttpAuthIdentity::Set(const char16_t *domain,
333 const char16_t *user,
334 const char16_t *pass)
335 {
336 char16_t *newUser, *newPass, *newDomain;
338 int domainLen = domain ? NS_strlen(domain) : 0;
339 int userLen = user ? NS_strlen(user) : 0;
340 int passLen = pass ? NS_strlen(pass) : 0;
342 int len = userLen + 1 + passLen + 1 + domainLen + 1;
343 newUser = (char16_t *) malloc(len * sizeof(char16_t));
344 if (!newUser)
345 return NS_ERROR_OUT_OF_MEMORY;
347 if (user)
348 memcpy(newUser, user, userLen * sizeof(char16_t));
349 newUser[userLen] = 0;
351 newPass = &newUser[userLen + 1];
352 if (pass)
353 memcpy(newPass, pass, passLen * sizeof(char16_t));
354 newPass[passLen] = 0;
356 newDomain = &newPass[passLen + 1];
357 if (domain)
358 memcpy(newDomain, domain, domainLen * sizeof(char16_t));
359 newDomain[domainLen] = 0;
361 // wait until the end to clear member vars in case input params
362 // reference our members!
363 if (mUser)
364 free(mUser);
365 mUser = newUser;
366 mPass = newPass;
367 mDomain = newDomain;
368 return NS_OK;
369 }
371 void
372 nsHttpAuthIdentity::Clear()
373 {
374 if (mUser) {
375 free(mUser);
376 mUser = nullptr;
377 mPass = nullptr;
378 mDomain = nullptr;
379 }
380 }
382 bool
383 nsHttpAuthIdentity::Equals(const nsHttpAuthIdentity &ident) const
384 {
385 // we could probably optimize this with a single loop, but why bother?
386 return StrEquivalent(mUser, ident.mUser) &&
387 StrEquivalent(mPass, ident.mPass) &&
388 StrEquivalent(mDomain, ident.mDomain);
389 }
391 //-----------------------------------------------------------------------------
392 // nsHttpAuthEntry
393 //-----------------------------------------------------------------------------
395 nsHttpAuthEntry::~nsHttpAuthEntry()
396 {
397 if (mRealm)
398 free(mRealm);
400 while (mRoot) {
401 nsHttpAuthPath *ap = mRoot;
402 mRoot = mRoot->mNext;
403 free(ap);
404 }
405 }
407 nsresult
408 nsHttpAuthEntry::AddPath(const char *aPath)
409 {
410 // null path matches empty path
411 if (!aPath)
412 aPath = "";
414 nsHttpAuthPath *tempPtr = mRoot;
415 while (tempPtr) {
416 const char *curpath = tempPtr->mPath;
417 if (strncmp(aPath, curpath, strlen(curpath)) == 0)
418 return NS_OK; // subpath already exists in the list
420 tempPtr = tempPtr->mNext;
422 }
424 //Append the aPath
425 nsHttpAuthPath *newAuthPath;
426 int newpathLen = strlen(aPath);
427 newAuthPath = (nsHttpAuthPath *) malloc(sizeof(nsHttpAuthPath) + newpathLen);
428 if (!newAuthPath)
429 return NS_ERROR_OUT_OF_MEMORY;
431 memcpy(newAuthPath->mPath, aPath, newpathLen+1);
432 newAuthPath->mNext = nullptr;
434 if (!mRoot)
435 mRoot = newAuthPath; //first entry
436 else
437 mTail->mNext = newAuthPath; // Append newAuthPath
439 //update the tail pointer.
440 mTail = newAuthPath;
441 return NS_OK;
442 }
444 nsresult
445 nsHttpAuthEntry::Set(const char *path,
446 const char *realm,
447 const char *creds,
448 const char *chall,
449 const nsHttpAuthIdentity *ident,
450 nsISupports *metadata)
451 {
452 char *newRealm, *newCreds, *newChall;
454 int realmLen = realm ? strlen(realm) : 0;
455 int credsLen = creds ? strlen(creds) : 0;
456 int challLen = chall ? strlen(chall) : 0;
458 int len = realmLen + 1 + credsLen + 1 + challLen + 1;
459 newRealm = (char *) malloc(len);
460 if (!newRealm)
461 return NS_ERROR_OUT_OF_MEMORY;
463 if (realm)
464 memcpy(newRealm, realm, realmLen);
465 newRealm[realmLen] = 0;
467 newCreds = &newRealm[realmLen + 1];
468 if (creds)
469 memcpy(newCreds, creds, credsLen);
470 newCreds[credsLen] = 0;
472 newChall = &newCreds[credsLen + 1];
473 if (chall)
474 memcpy(newChall, chall, challLen);
475 newChall[challLen] = 0;
477 nsresult rv = NS_OK;
478 if (ident) {
479 rv = mIdent.Set(*ident);
480 }
481 else if (mIdent.IsEmpty()) {
482 // If we are not given an identity and our cached identity has not been
483 // initialized yet (so is currently empty), initialize it now by
484 // filling it with nulls. We need to do that because consumers expect
485 // that mIdent is initialized after this function returns.
486 rv = mIdent.Set(nullptr, nullptr, nullptr);
487 }
488 if (NS_FAILED(rv)) {
489 free(newRealm);
490 return rv;
491 }
493 rv = AddPath(path);
494 if (NS_FAILED(rv)) {
495 free(newRealm);
496 return rv;
497 }
499 // wait until the end to clear member vars in case input params
500 // reference our members!
501 if (mRealm)
502 free(mRealm);
504 mRealm = newRealm;
505 mCreds = newCreds;
506 mChallenge = newChall;
507 mMetaData = metadata;
509 return NS_OK;
510 }
512 //-----------------------------------------------------------------------------
513 // nsHttpAuthNode
514 //-----------------------------------------------------------------------------
516 nsHttpAuthNode::nsHttpAuthNode()
517 {
518 LOG(("Creating nsHttpAuthNode @%x\n", this));
519 }
521 nsHttpAuthNode::~nsHttpAuthNode()
522 {
523 LOG(("Destroying nsHttpAuthNode @%x\n", this));
525 mList.Clear();
526 }
528 nsHttpAuthEntry *
529 nsHttpAuthNode::LookupEntryByPath(const char *path)
530 {
531 nsHttpAuthEntry *entry;
533 // null path matches empty path
534 if (!path)
535 path = "";
537 // look for an entry that either matches or contains this directory.
538 // ie. we'll give out credentials if the given directory is a sub-
539 // directory of an existing entry.
540 for (uint32_t i=0; i<mList.Length(); ++i) {
541 entry = mList[i];
542 nsHttpAuthPath *authPath = entry->RootPath();
543 while (authPath) {
544 const char *entryPath = authPath->mPath;
545 // proxy auth entries have no path, so require exact match on
546 // empty path string.
547 if (entryPath[0] == '\0') {
548 if (path[0] == '\0')
549 return entry;
550 }
551 else if (strncmp(path, entryPath, strlen(entryPath)) == 0)
552 return entry;
554 authPath = authPath->mNext;
555 }
556 }
557 return nullptr;
558 }
560 nsHttpAuthEntry *
561 nsHttpAuthNode::LookupEntryByRealm(const char *realm)
562 {
563 nsHttpAuthEntry *entry;
565 // null realm matches empty realm
566 if (!realm)
567 realm = "";
569 // look for an entry that matches this realm
570 uint32_t i;
571 for (i=0; i<mList.Length(); ++i) {
572 entry = mList[i];
573 if (strcmp(realm, entry->Realm()) == 0)
574 return entry;
575 }
576 return nullptr;
577 }
579 nsresult
580 nsHttpAuthNode::SetAuthEntry(const char *path,
581 const char *realm,
582 const char *creds,
583 const char *challenge,
584 const nsHttpAuthIdentity *ident,
585 nsISupports *metadata)
586 {
587 // look for an entry with a matching realm
588 nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
589 if (!entry) {
590 entry = new nsHttpAuthEntry(path, realm, creds, challenge, ident, metadata);
591 if (!entry)
592 return NS_ERROR_OUT_OF_MEMORY;
593 mList.AppendElement(entry);
594 }
595 else {
596 // update the entry...
597 entry->Set(path, realm, creds, challenge, ident, metadata);
598 }
600 return NS_OK;
601 }
603 void
604 nsHttpAuthNode::ClearAuthEntry(const char *realm)
605 {
606 nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
607 if (entry) {
608 mList.RemoveElement(entry); // double search OK
609 }
610 }
612 } // namespace mozilla::net
613 } // namespace mozilla