1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@netscape.com> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include <stdlib.h>
40 : #include "nsHttp.h"
41 : #include "nsHttpAuthCache.h"
42 : #include "nsString.h"
43 : #include "nsCRT.h"
44 : #include "prprf.h"
45 :
46 : static inline void
47 3537 : GetAuthKey(const char *scheme, const char *host, PRInt32 port, nsCString &key)
48 : {
49 3537 : key.Assign(scheme);
50 3537 : key.AppendLiteral("://");
51 3537 : key.Append(host);
52 3537 : key.Append(':');
53 3537 : key.AppendInt(port);
54 3537 : }
55 :
56 : // return true if the two strings are equal or both empty. an empty string
57 : // is either null or zero length.
58 : static bool
59 21 : StrEquivalent(const PRUnichar *a, const PRUnichar *b)
60 : {
61 : static const PRUnichar emptyStr[] = {0};
62 :
63 21 : if (!a)
64 0 : a = emptyStr;
65 21 : if (!b)
66 0 : b = emptyStr;
67 :
68 21 : return nsCRT::strcmp(a, b) == 0;
69 : }
70 :
71 : //-----------------------------------------------------------------------------
72 : // nsHttpAuthCache <public>
73 : //-----------------------------------------------------------------------------
74 :
75 679 : nsHttpAuthCache::nsHttpAuthCache()
76 679 : : mDB(nsnull)
77 : {
78 679 : }
79 :
80 677 : nsHttpAuthCache::~nsHttpAuthCache()
81 : {
82 677 : if (mDB)
83 0 : ClearAll();
84 677 : }
85 :
86 : nsresult
87 693 : nsHttpAuthCache::Init()
88 : {
89 693 : NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED);
90 :
91 693 : LOG(("nsHttpAuthCache::Init\n"));
92 :
93 : mDB = PL_NewHashTable(128, (PLHashFunction) PL_HashString,
94 : (PLHashComparator) PL_CompareStrings,
95 693 : (PLHashComparator) 0, &gHashAllocOps, this);
96 693 : if (!mDB)
97 0 : return NS_ERROR_OUT_OF_MEMORY;
98 :
99 693 : return NS_OK;
100 : }
101 :
102 : nsresult
103 3494 : nsHttpAuthCache::GetAuthEntryForPath(const char *scheme,
104 : const char *host,
105 : PRInt32 port,
106 : const char *path,
107 : nsHttpAuthEntry **entry)
108 : {
109 3494 : LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n",
110 : scheme, host, port, path));
111 :
112 6988 : nsCAutoString key;
113 3494 : nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key);
114 3494 : if (!node)
115 3487 : return NS_ERROR_NOT_AVAILABLE;
116 :
117 7 : *entry = node->LookupEntryByPath(path);
118 7 : return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
119 : }
120 :
121 : nsresult
122 64 : nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme,
123 : const char *host,
124 : PRInt32 port,
125 : const char *realm,
126 : nsHttpAuthEntry **entry)
127 :
128 : {
129 64 : LOG(("nsHttpAuthCache::GetAuthEntryForDomain [key=%s://%s:%d realm=%s]\n",
130 : scheme, host, port, realm));
131 :
132 128 : nsCAutoString key;
133 64 : nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key);
134 64 : if (!node)
135 53 : return NS_ERROR_NOT_AVAILABLE;
136 :
137 11 : *entry = node->LookupEntryByRealm(realm);
138 11 : return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
139 : }
140 :
141 : nsresult
142 27 : nsHttpAuthCache::SetAuthEntry(const char *scheme,
143 : const char *host,
144 : PRInt32 port,
145 : const char *path,
146 : const char *realm,
147 : const char *creds,
148 : const char *challenge,
149 : const nsHttpAuthIdentity *ident,
150 : nsISupports *metadata)
151 : {
152 : nsresult rv;
153 :
154 27 : LOG(("nsHttpAuthCache::SetAuthEntry [key=%s://%s:%d realm=%s path=%s metadata=%x]\n",
155 : scheme, host, port, realm, path, metadata));
156 :
157 27 : if (!mDB) {
158 14 : rv = Init();
159 14 : if (NS_FAILED(rv)) return rv;
160 : }
161 :
162 54 : nsCAutoString key;
163 27 : nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key);
164 :
165 27 : if (!node) {
166 : // create a new entry node and set the given entry
167 27 : node = new nsHttpAuthNode();
168 27 : if (!node)
169 0 : return NS_ERROR_OUT_OF_MEMORY;
170 27 : rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
171 27 : if (NS_FAILED(rv))
172 0 : delete node;
173 : else
174 27 : PL_HashTableAdd(mDB, nsCRT::strdup(key.get()), node);
175 27 : return rv;
176 : }
177 :
178 0 : return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
179 : }
180 :
181 : void
182 7 : nsHttpAuthCache::ClearAuthEntry(const char *scheme,
183 : const char *host,
184 : PRInt32 port,
185 : const char *realm)
186 : {
187 7 : if (!mDB)
188 0 : return;
189 :
190 14 : nsCAutoString key;
191 7 : GetAuthKey(scheme, host, port, key);
192 7 : PL_HashTableRemove(mDB, key.get());
193 : }
194 :
195 : nsresult
196 1370 : nsHttpAuthCache::ClearAll()
197 : {
198 1370 : LOG(("nsHttpAuthCache::ClearAll\n"));
199 :
200 1370 : if (mDB) {
201 693 : PL_HashTableDestroy(mDB);
202 693 : mDB = 0;
203 : }
204 1370 : return NS_OK;
205 : }
206 :
207 : //-----------------------------------------------------------------------------
208 : // nsHttpAuthCache <private>
209 : //-----------------------------------------------------------------------------
210 :
211 : nsHttpAuthNode *
212 3585 : nsHttpAuthCache::LookupAuthNode(const char *scheme,
213 : const char *host,
214 : PRInt32 port,
215 : nsCString &key)
216 : {
217 3585 : if (!mDB)
218 55 : return nsnull;
219 :
220 3530 : GetAuthKey(scheme, host, port, key);
221 :
222 3530 : return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get());
223 : }
224 :
225 : void *
226 1393 : nsHttpAuthCache::AllocTable(void *self, PRSize size)
227 : {
228 1393 : return malloc(size);
229 : }
230 :
231 : void
232 1393 : nsHttpAuthCache::FreeTable(void *self, void *item)
233 : {
234 1393 : free(item);
235 1393 : }
236 :
237 : PLHashEntry *
238 27 : nsHttpAuthCache::AllocEntry(void *self, const void *key)
239 : {
240 27 : return (PLHashEntry *) malloc(sizeof(PLHashEntry));
241 : }
242 :
243 : void
244 27 : nsHttpAuthCache::FreeEntry(void *self, PLHashEntry *he, PRUintn flag)
245 : {
246 27 : if (flag == HT_FREE_VALUE) {
247 : // this would only happen if PL_HashTableAdd were to replace an
248 : // existing entry in the hash table, but we _always_ do a lookup
249 : // before adding a new entry to avoid this case.
250 0 : NS_NOTREACHED("should never happen");
251 : }
252 27 : else if (flag == HT_FREE_ENTRY) {
253 : // three wonderful flavors of freeing memory ;-)
254 27 : delete (nsHttpAuthNode *) he->value;
255 27 : nsCRT::free((char *) he->key);
256 27 : free(he);
257 : }
258 27 : }
259 :
260 : PLHashAllocOps nsHttpAuthCache::gHashAllocOps =
261 : {
262 : nsHttpAuthCache::AllocTable,
263 : nsHttpAuthCache::FreeTable,
264 : nsHttpAuthCache::AllocEntry,
265 : nsHttpAuthCache::FreeEntry
266 : };
267 :
268 : //-----------------------------------------------------------------------------
269 : // nsHttpAuthIdentity
270 : //-----------------------------------------------------------------------------
271 :
272 : nsresult
273 63 : nsHttpAuthIdentity::Set(const PRUnichar *domain,
274 : const PRUnichar *user,
275 : const PRUnichar *pass)
276 : {
277 : PRUnichar *newUser, *newPass, *newDomain;
278 :
279 63 : int domainLen = domain ? nsCRT::strlen(domain) : 0;
280 63 : int userLen = user ? nsCRT::strlen(user) : 0;
281 63 : int passLen = pass ? nsCRT::strlen(pass) : 0;
282 :
283 63 : int len = userLen + 1 + passLen + 1 + domainLen + 1;
284 63 : newUser = (PRUnichar *) malloc(len * sizeof(PRUnichar));
285 63 : if (!newUser)
286 0 : return NS_ERROR_OUT_OF_MEMORY;
287 :
288 63 : if (user)
289 63 : memcpy(newUser, user, userLen * sizeof(PRUnichar));
290 63 : newUser[userLen] = 0;
291 :
292 63 : newPass = &newUser[userLen + 1];
293 63 : if (pass)
294 63 : memcpy(newPass, pass, passLen * sizeof(PRUnichar));
295 63 : newPass[passLen] = 0;
296 :
297 63 : newDomain = &newPass[passLen + 1];
298 63 : if (domain)
299 61 : memcpy(newDomain, domain, domainLen * sizeof(PRUnichar));
300 63 : newDomain[domainLen] = 0;
301 :
302 : // wait until the end to clear member vars in case input params
303 : // reference our members!
304 63 : if (mUser)
305 0 : free(mUser);
306 63 : mUser = newUser;
307 63 : mPass = newPass;
308 63 : mDomain = newDomain;
309 63 : return NS_OK;
310 : }
311 :
312 : void
313 6552 : nsHttpAuthIdentity::Clear()
314 : {
315 6552 : if (mUser) {
316 63 : free(mUser);
317 63 : mUser = nsnull;
318 63 : mPass = nsnull;
319 63 : mDomain = nsnull;
320 : }
321 6552 : }
322 :
323 : bool
324 7 : nsHttpAuthIdentity::Equals(const nsHttpAuthIdentity &ident) const
325 : {
326 : // we could probably optimize this with a single loop, but why bother?
327 7 : return StrEquivalent(mUser, ident.mUser) &&
328 7 : StrEquivalent(mPass, ident.mPass) &&
329 14 : StrEquivalent(mDomain, ident.mDomain);
330 : }
331 :
332 : //-----------------------------------------------------------------------------
333 : // nsHttpAuthEntry
334 : //-----------------------------------------------------------------------------
335 :
336 54 : nsHttpAuthEntry::~nsHttpAuthEntry()
337 : {
338 27 : if (mRealm)
339 27 : free(mRealm);
340 :
341 81 : while (mRoot) {
342 27 : nsHttpAuthPath *ap = mRoot;
343 27 : mRoot = mRoot->mNext;
344 27 : free(ap);
345 : }
346 27 : }
347 :
348 : nsresult
349 27 : nsHttpAuthEntry::AddPath(const char *aPath)
350 : {
351 : // null path matches empty path
352 27 : if (!aPath)
353 0 : aPath = "";
354 :
355 27 : nsHttpAuthPath *tempPtr = mRoot;
356 54 : while (tempPtr) {
357 0 : const char *curpath = tempPtr->mPath;
358 0 : if (strncmp(aPath, curpath, nsCRT::strlen(curpath)) == 0)
359 0 : return NS_OK; // subpath already exists in the list
360 :
361 0 : tempPtr = tempPtr->mNext;
362 :
363 : }
364 :
365 : //Append the aPath
366 : nsHttpAuthPath *newAuthPath;
367 27 : int newpathLen = nsCRT::strlen(aPath);
368 27 : newAuthPath = (nsHttpAuthPath *) malloc(sizeof(nsHttpAuthPath) + newpathLen);
369 27 : if (!newAuthPath)
370 0 : return NS_ERROR_OUT_OF_MEMORY;
371 :
372 27 : memcpy(newAuthPath->mPath, aPath, newpathLen+1);
373 27 : newAuthPath->mNext = nsnull;
374 :
375 27 : if (!mRoot)
376 27 : mRoot = newAuthPath; //first entry
377 : else
378 0 : mTail->mNext = newAuthPath; // Append newAuthPath
379 :
380 : //update the tail pointer.
381 27 : mTail = newAuthPath;
382 27 : return NS_OK;
383 : }
384 :
385 : nsresult
386 27 : nsHttpAuthEntry::Set(const char *path,
387 : const char *realm,
388 : const char *creds,
389 : const char *chall,
390 : const nsHttpAuthIdentity *ident,
391 : nsISupports *metadata)
392 : {
393 : char *newRealm, *newCreds, *newChall;
394 :
395 27 : int realmLen = realm ? nsCRT::strlen(realm) : 0;
396 27 : int credsLen = creds ? nsCRT::strlen(creds) : 0;
397 27 : int challLen = chall ? nsCRT::strlen(chall) : 0;
398 :
399 27 : int len = realmLen + 1 + credsLen + 1 + challLen + 1;
400 27 : newRealm = (char *) malloc(len);
401 27 : if (!newRealm)
402 0 : return NS_ERROR_OUT_OF_MEMORY;
403 :
404 27 : if (realm)
405 27 : memcpy(newRealm, realm, realmLen);
406 27 : newRealm[realmLen] = 0;
407 :
408 27 : newCreds = &newRealm[realmLen + 1];
409 27 : if (creds)
410 22 : memcpy(newCreds, creds, credsLen);
411 27 : newCreds[credsLen] = 0;
412 :
413 27 : newChall = &newCreds[credsLen + 1];
414 27 : if (chall)
415 23 : memcpy(newChall, chall, challLen);
416 27 : newChall[challLen] = 0;
417 :
418 27 : nsresult rv = NS_OK;
419 27 : if (ident) {
420 27 : rv = mIdent.Set(*ident);
421 : }
422 0 : else if (mIdent.IsEmpty()) {
423 : // If we are not given an identity and our cached identity has not been
424 : // initialized yet (so is currently empty), initialize it now by
425 : // filling it with nulls. We need to do that because consumers expect
426 : // that mIdent is initialized after this function returns.
427 0 : rv = mIdent.Set(nsnull, nsnull, nsnull);
428 : }
429 27 : if (NS_FAILED(rv)) {
430 0 : free(newRealm);
431 0 : return rv;
432 : }
433 :
434 27 : rv = AddPath(path);
435 27 : if (NS_FAILED(rv)) {
436 0 : free(newRealm);
437 0 : return rv;
438 : }
439 :
440 : // wait until the end to clear member vars in case input params
441 : // reference our members!
442 27 : if (mRealm)
443 0 : free(mRealm);
444 :
445 27 : mRealm = newRealm;
446 27 : mCreds = newCreds;
447 27 : mChallenge = newChall;
448 27 : mMetaData = metadata;
449 :
450 27 : return NS_OK;
451 : }
452 :
453 : //-----------------------------------------------------------------------------
454 : // nsHttpAuthNode
455 : //-----------------------------------------------------------------------------
456 :
457 27 : nsHttpAuthNode::nsHttpAuthNode()
458 : {
459 27 : LOG(("Creating nsHttpAuthNode @%x\n", this));
460 27 : }
461 :
462 54 : nsHttpAuthNode::~nsHttpAuthNode()
463 : {
464 27 : LOG(("Destroying nsHttpAuthNode @%x\n", this));
465 :
466 27 : mList.Clear();
467 27 : }
468 :
469 : nsHttpAuthEntry *
470 7 : nsHttpAuthNode::LookupEntryByPath(const char *path)
471 : {
472 : nsHttpAuthEntry *entry;
473 :
474 : // null path matches empty path
475 7 : if (!path)
476 0 : path = "";
477 :
478 : // look for an entry that either matches or contains this directory.
479 : // ie. we'll give out credentials if the given directory is a sub-
480 : // directory of an existing entry.
481 7 : for (PRUint32 i=0; i<mList.Length(); ++i) {
482 7 : entry = mList[i];
483 7 : nsHttpAuthPath *authPath = entry->RootPath();
484 14 : while (authPath) {
485 7 : const char *entryPath = authPath->mPath;
486 : // proxy auth entries have no path, so require exact match on
487 : // empty path string.
488 7 : if (entryPath[0] == '\0') {
489 0 : if (path[0] == '\0')
490 0 : return entry;
491 : }
492 7 : else if (strncmp(path, entryPath, nsCRT::strlen(entryPath)) == 0)
493 7 : return entry;
494 :
495 0 : authPath = authPath->mNext;
496 : }
497 : }
498 0 : return nsnull;
499 : }
500 :
501 : nsHttpAuthEntry *
502 38 : nsHttpAuthNode::LookupEntryByRealm(const char *realm)
503 : {
504 : nsHttpAuthEntry *entry;
505 :
506 : // null realm matches empty realm
507 38 : if (!realm)
508 0 : realm = "";
509 :
510 : // look for an entry that matches this realm
511 : PRUint32 i;
512 38 : for (i=0; i<mList.Length(); ++i) {
513 11 : entry = mList[i];
514 11 : if (strcmp(realm, entry->Realm()) == 0)
515 11 : return entry;
516 : }
517 27 : return nsnull;
518 : }
519 :
520 : nsresult
521 27 : nsHttpAuthNode::SetAuthEntry(const char *path,
522 : const char *realm,
523 : const char *creds,
524 : const char *challenge,
525 : const nsHttpAuthIdentity *ident,
526 : nsISupports *metadata)
527 : {
528 : // look for an entry with a matching realm
529 27 : nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
530 27 : if (!entry) {
531 27 : entry = new nsHttpAuthEntry(path, realm, creds, challenge, ident, metadata);
532 27 : if (!entry)
533 0 : return NS_ERROR_OUT_OF_MEMORY;
534 27 : mList.AppendElement(entry);
535 : }
536 : else {
537 : // update the entry...
538 0 : entry->Set(path, realm, creds, challenge, ident, metadata);
539 : }
540 :
541 27 : return NS_OK;
542 : }
543 :
544 : void
545 0 : nsHttpAuthNode::ClearAuthEntry(const char *realm)
546 : {
547 0 : nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
548 0 : if (entry) {
549 0 : mList.RemoveElement(entry); // double search OK
550 : }
551 0 : }
|