1 /*
2 * Copyright 2010-2014 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Christophe Huriaux, c.huriaux@gmail.com
7 * Hamish Morrison, hamishm53@gmail.com
8 */
9
10
11 #include <new>
12 #include <stdio.h>
13
14 #include <HashMap.h>
15 #include <HashString.h>
16 #include <Message.h>
17 #include <NetworkCookieJar.h>
18
19 #include "NetworkCookieJarPrivate.h"
20
21 using namespace BPrivate::Network;
22
23
24 // #define TRACE_COOKIE
25 #ifdef TRACE_COOKIE
26 # define TRACE(x...) printf(x)
27 #else
28 # define TRACE(x...) ;
29 #endif
30
31
32 const char* kArchivedCookieMessageName = "be:cookie";
33
34
BNetworkCookieJar()35 BNetworkCookieJar::BNetworkCookieJar()
36 :
37 fCookieHashMap(new(std::nothrow) PrivateHashMap())
38 {
39 }
40
41
BNetworkCookieJar(const BNetworkCookieJar & other)42 BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieJar& other)
43 :
44 fCookieHashMap(new(std::nothrow) PrivateHashMap())
45 {
46 *this = other;
47 }
48
49
BNetworkCookieJar(const BNetworkCookieList & otherList)50 BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieList& otherList)
51 :
52 fCookieHashMap(new(std::nothrow) PrivateHashMap())
53 {
54 AddCookies(otherList);
55 }
56
57
BNetworkCookieJar(BMessage * archive)58 BNetworkCookieJar::BNetworkCookieJar(BMessage* archive)
59 :
60 fCookieHashMap(new(std::nothrow) PrivateHashMap())
61 {
62 BMessage extractedCookie;
63
64 for (int32 i = 0; archive->FindMessage(kArchivedCookieMessageName, i,
65 &extractedCookie) == B_OK; i++) {
66 BNetworkCookie* heapCookie
67 = new(std::nothrow) BNetworkCookie(&extractedCookie);
68
69 if (heapCookie == NULL)
70 break;
71
72 if (AddCookie(heapCookie) != B_OK) {
73 delete heapCookie;
74 continue;
75 }
76 }
77 }
78
79
~BNetworkCookieJar()80 BNetworkCookieJar::~BNetworkCookieJar()
81 {
82 for (Iterator it = GetIterator(); it.Next() != NULL;)
83 delete it.Remove();
84
85 fCookieHashMap->Lock();
86
87 PrivateHashMap::Iterator it = fCookieHashMap->GetIterator();
88 while (it.HasNext()) {
89 BNetworkCookieList* list = it.Next().value;
90 list->LockForWriting();
91 delete list;
92 }
93
94 delete fCookieHashMap;
95 }
96
97
98 // #pragma mark Add cookie to cookie jar
99
100
101 status_t
AddCookie(const BNetworkCookie & cookie)102 BNetworkCookieJar::AddCookie(const BNetworkCookie& cookie)
103 {
104 BNetworkCookie* heapCookie = new(std::nothrow) BNetworkCookie(cookie);
105 if (heapCookie == NULL)
106 return B_NO_MEMORY;
107
108 status_t result = AddCookie(heapCookie);
109 if (result != B_OK)
110 delete heapCookie;
111
112 return result;
113 }
114
115
116 status_t
AddCookie(const BString & cookie,const BUrl & referrer)117 BNetworkCookieJar::AddCookie(const BString& cookie, const BUrl& referrer)
118 {
119 BNetworkCookie* heapCookie = new(std::nothrow) BNetworkCookie(cookie,
120 referrer);
121
122 if (heapCookie == NULL)
123 return B_NO_MEMORY;
124
125 status_t result = AddCookie(heapCookie);
126
127 if (result != B_OK)
128 delete heapCookie;
129
130 return result;
131 }
132
133
134 status_t
AddCookie(BNetworkCookie * cookie)135 BNetworkCookieJar::AddCookie(BNetworkCookie* cookie)
136 {
137 if (fCookieHashMap == NULL)
138 return B_NO_MEMORY;
139
140 if (cookie == NULL || !cookie->IsValid())
141 return B_BAD_VALUE;
142
143 HashString key(cookie->Domain());
144
145 if (!fCookieHashMap->Lock())
146 return B_ERROR;
147
148 // Get the cookies for the requested domain, or create a new list if there
149 // isn't one yet.
150 BNetworkCookieList* list = fCookieHashMap->Get(key);
151 if (list == NULL) {
152 list = new(std::nothrow) BNetworkCookieList();
153
154 if (list == NULL) {
155 fCookieHashMap->Unlock();
156 return B_NO_MEMORY;
157 }
158
159 if (fCookieHashMap->Put(key, list) != B_OK) {
160 fCookieHashMap->Unlock();
161 delete list;
162 return B_NO_MEMORY;
163 }
164 }
165
166 if (list->LockForWriting() != B_OK) {
167 fCookieHashMap->Unlock();
168 return B_ERROR;
169 }
170
171 fCookieHashMap->Unlock();
172
173 // Remove any cookie with the same key as the one we're trying to add (it
174 // replaces/updates them)
175 for (int32 i = 0; i < list->CountItems(); i++) {
176 const BNetworkCookie* c = list->ItemAt(i);
177
178 if (c->Name() == cookie->Name() && c->Path() == cookie->Path()) {
179 list->RemoveItemAt(i);
180 break;
181 }
182 }
183
184 // If the cookie has an expiration date in the past, stop here: we
185 // effectively deleted a cookie.
186 if (cookie->ShouldDeleteNow()) {
187 TRACE("Remove cookie: %s\n", cookie->RawCookie(true).String());
188 delete cookie;
189 } else {
190 // Make sure the cookie has cached the raw string and expiration date
191 // string, so it is now actually immutable. This way we can safely
192 // read the cookie data from multiple threads without any locking.
193 const BString& raw = cookie->RawCookie(true);
194 (void)raw;
195
196 TRACE("Add cookie: %s\n", raw.String());
197
198 // Keep the list sorted by path length (longest first). This makes sure
199 // that cookies for most specific paths are returned first when
200 // iterating the cookie jar.
201 int32 i;
202 for (i = 0; i < list->CountItems(); i++) {
203 const BNetworkCookie* current = list->ItemAt(i);
204 if (current->Path().Length() < cookie->Path().Length())
205 break;
206 }
207 list->AddItem(cookie, i);
208 }
209
210 list->Unlock();
211
212 return B_OK;
213 }
214
215
216 status_t
AddCookies(const BNetworkCookieList & cookies)217 BNetworkCookieJar::AddCookies(const BNetworkCookieList& cookies)
218 {
219 for (int32 i = 0; i < cookies.CountItems(); i++) {
220 const BNetworkCookie* cookiePtr = cookies.ItemAt(i);
221
222 // Using AddCookie by reference in order to avoid multiple
223 // cookie jar share the same cookie pointers
224 status_t result = AddCookie(*cookiePtr);
225 if (result != B_OK)
226 return result;
227 }
228
229 return B_OK;
230 }
231
232
233 // #pragma mark Purge useless cookies
234
235
236 uint32
DeleteOutdatedCookies()237 BNetworkCookieJar::DeleteOutdatedCookies()
238 {
239 int32 deleteCount = 0;
240 const BNetworkCookie* cookiePtr;
241
242 for (Iterator it = GetIterator(); (cookiePtr = it.Next()) != NULL;) {
243 if (cookiePtr->ShouldDeleteNow()) {
244 delete it.Remove();
245 deleteCount++;
246 }
247 }
248
249 return deleteCount;
250 }
251
252
253 uint32
PurgeForExit()254 BNetworkCookieJar::PurgeForExit()
255 {
256 int32 deleteCount = 0;
257 const BNetworkCookie* cookiePtr;
258
259 for (Iterator it = GetIterator(); (cookiePtr = it.Next()) != NULL;) {
260 if (cookiePtr->ShouldDeleteAtExit()) {
261 delete it.Remove();
262 deleteCount++;
263 }
264 }
265
266 return deleteCount;
267 }
268
269
270 // #pragma mark BArchivable interface
271
272
273 status_t
Archive(BMessage * into,bool deep) const274 BNetworkCookieJar::Archive(BMessage* into, bool deep) const
275 {
276 status_t error = BArchivable::Archive(into, deep);
277
278 if (error == B_OK) {
279 const BNetworkCookie* cookiePtr;
280
281 for (Iterator it = GetIterator(); (cookiePtr = it.Next()) != NULL;) {
282 BMessage subArchive;
283
284 error = cookiePtr->Archive(&subArchive, deep);
285 if (error != B_OK)
286 return error;
287
288 error = into->AddMessage(kArchivedCookieMessageName, &subArchive);
289 if (error != B_OK)
290 return error;
291 }
292 }
293
294 return error;
295 }
296
297
298 BArchivable*
Instantiate(BMessage * archive)299 BNetworkCookieJar::Instantiate(BMessage* archive)
300 {
301 if (archive->HasMessage(kArchivedCookieMessageName))
302 return new(std::nothrow) BNetworkCookieJar(archive);
303
304 return NULL;
305 }
306
307
308 // #pragma mark BFlattenable interface
309
310
311 bool
IsFixedSize() const312 BNetworkCookieJar::IsFixedSize() const
313 {
314 // Flattened size vary
315 return false;
316 }
317
318
319 type_code
TypeCode() const320 BNetworkCookieJar::TypeCode() const
321 {
322 // TODO: Add a B_COOKIEJAR_TYPE
323 return B_ANY_TYPE;
324 }
325
326
327 ssize_t
FlattenedSize() const328 BNetworkCookieJar::FlattenedSize() const
329 {
330 _DoFlatten();
331 return fFlattened.Length() + 1;
332 }
333
334
335 status_t
Flatten(void * buffer,ssize_t size) const336 BNetworkCookieJar::Flatten(void* buffer, ssize_t size) const
337 {
338 if (FlattenedSize() > size)
339 return B_ERROR;
340
341 fFlattened.CopyInto(reinterpret_cast<char*>(buffer), 0,
342 fFlattened.Length());
343 reinterpret_cast<char*>(buffer)[fFlattened.Length()] = 0;
344
345 return B_OK;
346 }
347
348
349 bool
AllowsTypeCode(type_code) const350 BNetworkCookieJar::AllowsTypeCode(type_code) const
351 {
352 // TODO
353 return false;
354 }
355
356
357 status_t
Unflatten(type_code,const void * buffer,ssize_t size)358 BNetworkCookieJar::Unflatten(type_code, const void* buffer, ssize_t size)
359 {
360 BString flattenedCookies;
361 flattenedCookies.SetTo(reinterpret_cast<const char*>(buffer), size);
362
363 while (flattenedCookies.Length() > 0) {
364 BNetworkCookie tempCookie;
365 BString tempCookieLine;
366
367 int32 endOfLine = flattenedCookies.FindFirst('\n', 0);
368 if (endOfLine == -1)
369 tempCookieLine = flattenedCookies;
370 else {
371 flattenedCookies.MoveInto(tempCookieLine, 0, endOfLine);
372 flattenedCookies.Remove(0, 1);
373 }
374
375 if (tempCookieLine.Length() != 0 && tempCookieLine[0] != '#') {
376 for (int32 field = 0; field < 7; field++) {
377 BString tempString;
378
379 int32 endOfField = tempCookieLine.FindFirst('\t', 0);
380 if (endOfField == -1)
381 tempString = tempCookieLine;
382 else {
383 tempCookieLine.MoveInto(tempString, 0, endOfField);
384 tempCookieLine.Remove(0, 1);
385 }
386
387 switch (field) {
388 case 0:
389 tempCookie.SetDomain(tempString);
390 break;
391
392 case 1:
393 // TODO: Useless field ATM
394 break;
395
396 case 2:
397 tempCookie.SetPath(tempString);
398 break;
399
400 case 3:
401 tempCookie.SetSecure(tempString == "TRUE");
402 break;
403
404 case 4:
405 tempCookie.SetExpirationDate(atoi(tempString));
406 break;
407
408 case 5:
409 tempCookie.SetName(tempString);
410 break;
411
412 case 6:
413 tempCookie.SetValue(tempString);
414 break;
415 } // switch
416 } // for loop
417
418 AddCookie(tempCookie);
419 }
420 }
421
422 return B_OK;
423 }
424
425
426 BNetworkCookieJar&
operator =(const BNetworkCookieJar & other)427 BNetworkCookieJar::operator=(const BNetworkCookieJar& other)
428 {
429 if (&other == this)
430 return *this;
431
432 for (Iterator it = GetIterator(); it.Next() != NULL;)
433 delete it.Remove();
434
435 BArchivable::operator=(other);
436 BFlattenable::operator=(other);
437
438 fFlattened = other.fFlattened;
439
440 delete fCookieHashMap;
441 fCookieHashMap = new(std::nothrow) PrivateHashMap();
442
443 for (Iterator it = other.GetIterator(); it.HasNext();) {
444 const BNetworkCookie* cookie = it.Next();
445 AddCookie(*cookie); // Pass by reference so the cookie is copied.
446 }
447
448 return *this;
449 }
450
451
452 // #pragma mark Iterators
453
454
455 BNetworkCookieJar::Iterator
GetIterator() const456 BNetworkCookieJar::GetIterator() const
457 {
458 return BNetworkCookieJar::Iterator(this);
459 }
460
461
462 BNetworkCookieJar::UrlIterator
GetUrlIterator(const BUrl & url) const463 BNetworkCookieJar::GetUrlIterator(const BUrl& url) const
464 {
465 if (!url.HasPath()) {
466 BUrl copy(url);
467 copy.SetPath("/");
468 return BNetworkCookieJar::UrlIterator(this, copy);
469 }
470
471 return BNetworkCookieJar::UrlIterator(this, url);
472 }
473
474
475 void
_DoFlatten() const476 BNetworkCookieJar::_DoFlatten() const
477 {
478 fFlattened.Truncate(0);
479
480 const BNetworkCookie* cookiePtr;
481 for (Iterator it = GetIterator(); (cookiePtr = it.Next()) != NULL;) {
482 fFlattened << cookiePtr->Domain() << '\t' << "TRUE" << '\t'
483 << cookiePtr->Path() << '\t'
484 << (cookiePtr->Secure()?"TRUE":"FALSE") << '\t'
485 << (int32)cookiePtr->ExpirationDate() << '\t'
486 << cookiePtr->Name() << '\t' << cookiePtr->Value() << '\n';
487 }
488 }
489
490
491 // #pragma mark Iterator
492
493
Iterator(const Iterator & other)494 BNetworkCookieJar::Iterator::Iterator(const Iterator& other)
495 :
496 fCookieJar(other.fCookieJar),
497 fIterator(NULL),
498 fLastList(NULL),
499 fList(NULL),
500 fElement(NULL),
501 fLastElement(NULL),
502 fIndex(0)
503 {
504 fIterator = new(std::nothrow) PrivateIterator(
505 fCookieJar->fCookieHashMap->GetIterator());
506
507 _FindNext();
508 }
509
510
Iterator(const BNetworkCookieJar * cookieJar)511 BNetworkCookieJar::Iterator::Iterator(const BNetworkCookieJar* cookieJar)
512 :
513 fCookieJar(const_cast<BNetworkCookieJar*>(cookieJar)),
514 fIterator(NULL),
515 fLastList(NULL),
516 fList(NULL),
517 fElement(NULL),
518 fLastElement(NULL),
519 fIndex(0)
520 {
521 fIterator = new(std::nothrow) PrivateIterator(
522 fCookieJar->fCookieHashMap->GetIterator());
523
524 // Locate first cookie
525 _FindNext();
526 }
527
528
~Iterator()529 BNetworkCookieJar::Iterator::~Iterator()
530 {
531 if (fList != NULL)
532 fList->Unlock();
533 if (fLastList != NULL)
534 fLastList->Unlock();
535
536 delete fIterator;
537 }
538
539
540 BNetworkCookieJar::Iterator&
operator =(const Iterator & other)541 BNetworkCookieJar::Iterator::operator=(const Iterator& other)
542 {
543 if (this == &other)
544 return *this;
545
546 delete fIterator;
547 if (fList != NULL)
548 fList->Unlock();
549
550 fCookieJar = other.fCookieJar;
551 fIterator = NULL;
552 fLastList = NULL;
553 fList = NULL;
554 fElement = NULL;
555 fLastElement = NULL;
556 fIndex = 0;
557
558 fIterator = new(std::nothrow) PrivateIterator(
559 fCookieJar->fCookieHashMap->GetIterator());
560
561 _FindNext();
562
563 return *this;
564 }
565
566
567 bool
HasNext() const568 BNetworkCookieJar::Iterator::HasNext() const
569 {
570 return fElement;
571 }
572
573
574 const BNetworkCookie*
Next()575 BNetworkCookieJar::Iterator::Next()
576 {
577 if (!fElement)
578 return NULL;
579
580 const BNetworkCookie* result = fElement;
581 _FindNext();
582 return result;
583 }
584
585
586 const BNetworkCookie*
NextDomain()587 BNetworkCookieJar::Iterator::NextDomain()
588 {
589 if (!fElement)
590 return NULL;
591
592 const BNetworkCookie* result = fElement;
593
594 if (!fIterator->fCookieMapIterator.HasNext()) {
595 fElement = NULL;
596 return result;
597 }
598
599 if (fList != NULL)
600 fList->Unlock();
601
602 if (fCookieJar->fCookieHashMap->Lock()) {
603 fList = fIterator->fCookieMapIterator.Next().value;
604 fList->LockForReading();
605
606 while (fList->CountItems() == 0
607 && fIterator->fCookieMapIterator.HasNext()) {
608 // Empty list. Skip it
609 fList->Unlock();
610 fList = fIterator->fCookieMapIterator.Next().value;
611 fList->LockForReading();
612 }
613
614 fCookieJar->fCookieHashMap->Unlock();
615 }
616
617 fIndex = 0;
618 fElement = fList->ItemAt(fIndex);
619 return result;
620 }
621
622
623 const BNetworkCookie*
Remove()624 BNetworkCookieJar::Iterator::Remove()
625 {
626 if (!fLastElement)
627 return NULL;
628
629 const BNetworkCookie* result = fLastElement;
630
631 if (fIndex == 0) {
632 if (fLastList && fCookieJar->fCookieHashMap->Lock()) {
633 // We are on the first item of fList, so we need to remove the
634 // last of fLastList
635 fLastList->Unlock();
636 if (fLastList->LockForWriting() == B_OK) {
637 fLastList->RemoveItemAt(fLastList->CountItems() - 1);
638 // TODO if the list became empty, we could remove it from the
639 // map, but this can be a problem if other iterators are still
640 // referencing it. Is there a safe place and locking pattern
641 // where we can do that?
642 fLastList->Unlock();
643 fLastList->LockForReading();
644 }
645 fCookieJar->fCookieHashMap->Unlock();
646 }
647 } else {
648 fIndex--;
649
650 if (fCookieJar->fCookieHashMap->Lock()) {
651 // Switch to a write lock
652 fList->Unlock();
653 if (fList->LockForWriting() == B_OK) {
654 fList->RemoveItemAt(fIndex);
655 fList->Unlock();
656 }
657 fList->LockForReading();
658 fCookieJar->fCookieHashMap->Unlock();
659 }
660 }
661
662 fLastElement = NULL;
663 return result;
664 }
665
666
667 void
_FindNext()668 BNetworkCookieJar::Iterator::_FindNext()
669 {
670 fLastElement = fElement;
671
672 fIndex++;
673 if (fList && fIndex < fList->CountItems()) {
674 // Get an element from the current list
675 fElement = fList->ItemAt(fIndex);
676 return;
677 }
678
679 if (fIterator == NULL || !fIterator->fCookieMapIterator.HasNext()) {
680 // We are done iterating
681 fElement = NULL;
682 return;
683 }
684
685 // Get an element from the next list
686 if (fLastList != NULL) {
687 fLastList->Unlock();
688 }
689 fLastList = fList;
690
691 if (fCookieJar->fCookieHashMap->Lock()) {
692 fList = (fIterator->fCookieMapIterator.Next().value);
693 fList->LockForReading();
694
695 while (fList->CountItems() == 0
696 && fIterator->fCookieMapIterator.HasNext()) {
697 // Empty list. Skip it
698 fList->Unlock();
699 fList = fIterator->fCookieMapIterator.Next().value;
700 fList->LockForReading();
701 }
702
703 fCookieJar->fCookieHashMap->Unlock();
704 }
705
706 fIndex = 0;
707 fElement = fList->ItemAt(fIndex);
708 }
709
710
711 // #pragma mark URL Iterator
712
713
UrlIterator(const UrlIterator & other)714 BNetworkCookieJar::UrlIterator::UrlIterator(const UrlIterator& other)
715 :
716 fCookieJar(other.fCookieJar),
717 fIterator(NULL),
718 fList(NULL),
719 fLastList(NULL),
720 fElement(NULL),
721 fLastElement(NULL),
722 fIndex(0),
723 fLastIndex(0),
724 fUrl(other.fUrl)
725 {
726 _Initialize();
727 }
728
729
UrlIterator(const BNetworkCookieJar * cookieJar,const BUrl & url)730 BNetworkCookieJar::UrlIterator::UrlIterator(const BNetworkCookieJar* cookieJar,
731 const BUrl& url)
732 :
733 fCookieJar(const_cast<BNetworkCookieJar*>(cookieJar)),
734 fIterator(NULL),
735 fList(NULL),
736 fLastList(NULL),
737 fElement(NULL),
738 fLastElement(NULL),
739 fIndex(0),
740 fLastIndex(0),
741 fUrl(url)
742 {
743 _Initialize();
744 }
745
746
~UrlIterator()747 BNetworkCookieJar::UrlIterator::~UrlIterator()
748 {
749 if (fList != NULL)
750 fList->Unlock();
751 if (fLastList != NULL)
752 fLastList->Unlock();
753
754 delete fIterator;
755 }
756
757
758 bool
HasNext() const759 BNetworkCookieJar::UrlIterator::HasNext() const
760 {
761 return fElement;
762 }
763
764
765 const BNetworkCookie*
Next()766 BNetworkCookieJar::UrlIterator::Next()
767 {
768 if (!fElement)
769 return NULL;
770
771 const BNetworkCookie* result = fElement;
772 _FindNext();
773 return result;
774 }
775
776
777 const BNetworkCookie*
Remove()778 BNetworkCookieJar::UrlIterator::Remove()
779 {
780 if (!fLastElement)
781 return NULL;
782
783 const BNetworkCookie* result = fLastElement;
784
785 if (fCookieJar->fCookieHashMap->Lock()) {
786 fLastList->Unlock();
787 if (fLastList->LockForWriting() == B_OK) {
788 fLastList->RemoveItemAt(fLastIndex);
789
790 if (fLastList->CountItems() == 0) {
791 fCookieJar->fCookieHashMap->Remove(fIterator->fCookieMapIterator);
792 delete fLastList;
793 fLastList = NULL;
794 } else {
795 fLastList->Unlock();
796 fLastList->LockForReading();
797 }
798 }
799 fCookieJar->fCookieHashMap->Unlock();
800 }
801
802 fLastElement = NULL;
803 return result;
804 }
805
806
807 BNetworkCookieJar::UrlIterator&
operator =(const BNetworkCookieJar::UrlIterator & other)808 BNetworkCookieJar::UrlIterator::operator=(
809 const BNetworkCookieJar::UrlIterator& other)
810 {
811 if (this == &other)
812 return *this;
813
814 // Teardown
815 if (fList)
816 fList->Unlock();
817
818 delete fIterator;
819
820 // Init
821 fCookieJar = other.fCookieJar;
822 fIterator = NULL;
823 fList = NULL;
824 fLastList = NULL;
825 fElement = NULL;
826 fLastElement = NULL;
827 fIndex = 0;
828 fLastIndex = 0;
829 fUrl = other.fUrl;
830
831 _Initialize();
832
833 return *this;
834 }
835
836
837 void
_Initialize()838 BNetworkCookieJar::UrlIterator::_Initialize()
839 {
840 BString domain = fUrl.Host();
841
842 if (!domain.Length()) {
843 if (fUrl.Protocol() == "file")
844 domain = "localhost";
845 else
846 return;
847 }
848
849 fIterator = new(std::nothrow) PrivateIterator(
850 fCookieJar->fCookieHashMap->GetIterator());
851
852 if (fIterator != NULL) {
853 // Prepending a dot since _FindNext is going to call _SupDomain()
854 domain.Prepend(".");
855 fIterator->fKey.SetTo(domain, domain.Length());
856 _FindNext();
857 }
858 }
859
860
861 bool
_SuperDomain()862 BNetworkCookieJar::UrlIterator::_SuperDomain()
863 {
864 BString domain(fIterator->fKey.GetString());
865 // Makes a copy of the characters from the key. This is important,
866 // because HashString doesn't like SetTo to be called with a substring
867 // of its original string (use-after-free + memcpy overwrite).
868 int32 firstDot = domain.FindFirst('.');
869 if (firstDot < 0)
870 return false;
871
872 const char* nextDot = domain.String() + firstDot;
873
874 fIterator->fKey.SetTo(nextDot + 1);
875 return true;
876 }
877
878
879 void
_FindNext()880 BNetworkCookieJar::UrlIterator::_FindNext()
881 {
882 fLastIndex = fIndex;
883 fLastElement = fElement;
884 if (fLastList != NULL)
885 fLastList->Unlock();
886
887 fLastList = fList;
888 if (fCookieJar->fCookieHashMap->Lock()) {
889 if (fLastList)
890 fLastList->LockForReading();
891
892 while (!_FindPath()) {
893 if (!_SuperDomain()) {
894 fElement = NULL;
895 fCookieJar->fCookieHashMap->Unlock();
896 return;
897 }
898
899 _FindDomain();
900 }
901 fCookieJar->fCookieHashMap->Unlock();
902 }
903 }
904
905
906 void
_FindDomain()907 BNetworkCookieJar::UrlIterator::_FindDomain()
908 {
909 if (fList != NULL)
910 fList->Unlock();
911
912 if (fCookieJar->fCookieHashMap->Lock()) {
913 fList = fCookieJar->fCookieHashMap->Get(fIterator->fKey);
914
915 if (fList == NULL)
916 fElement = NULL;
917 else {
918 fList->LockForReading();
919 }
920 fCookieJar->fCookieHashMap->Unlock();
921 }
922
923 fIndex = -1;
924 }
925
926
927 bool
_FindPath()928 BNetworkCookieJar::UrlIterator::_FindPath()
929 {
930 fIndex++;
931 while (fList && fIndex < fList->CountItems()) {
932 fElement = fList->ItemAt(fIndex);
933
934 if (fElement->IsValidForPath(fUrl.Path()))
935 return true;
936
937 fIndex++;
938 }
939
940 return false;
941 }
942
943
944 // #pragma mark - BNetworkCookieList
945
946
BNetworkCookieList()947 BNetworkCookieList::BNetworkCookieList()
948 {
949 pthread_rwlock_init(&fLock, NULL);
950 }
951
952
~BNetworkCookieList()953 BNetworkCookieList::~BNetworkCookieList()
954 {
955 // Note: this is expected to be called with the write lock held.
956 pthread_rwlock_destroy(&fLock);
957 }
958
959
960 status_t
LockForReading()961 BNetworkCookieList::LockForReading()
962 {
963 return pthread_rwlock_rdlock(&fLock);
964 }
965
966
967 status_t
LockForWriting()968 BNetworkCookieList::LockForWriting()
969 {
970 return pthread_rwlock_wrlock(&fLock);
971 }
972
973
974 status_t
Unlock()975 BNetworkCookieList::Unlock()
976 {
977 return pthread_rwlock_unlock(&fLock);
978 }
979
980