1/* 2 * Copyright 2010 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 */ 8 9 10#include <new> 11 12#include <Debug.h> 13#include <HashMap.h> 14#include <HashString.h> 15#include <Message.h> 16#include <NetworkCookieJar.h> 17#include "NetworkCookieJarPrivate.h" 18 19const char* kArchivedCookieMessageName = "be:cookie"; 20 21 22BNetworkCookieJar::BNetworkCookieJar() 23 : 24 fCookieHashMap(new PrivateHashMap) 25{ 26} 27 28 29BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieJar&) 30 : 31 BArchivable(), 32 fCookieHashMap(new PrivateHashMap) 33{ 34 // TODO 35} 36 37 38BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieList& otherList) 39 : 40 fCookieHashMap(new PrivateHashMap) 41{ 42 AddCookies(otherList); 43} 44 45 46BNetworkCookieJar::BNetworkCookieJar(BMessage* archive) 47 : 48 fCookieHashMap(new PrivateHashMap) 49{ 50 BMessage extractedCookie; 51 52 for (int32 i = 0; 53 archive->FindMessage(kArchivedCookieMessageName, i, &extractedCookie) 54 == B_OK; 55 i++) { 56 BNetworkCookie* heapCookie 57 = new(std::nothrow) BNetworkCookie(&extractedCookie); 58 59 if (heapCookie == NULL || !AddCookie(heapCookie)) 60 break; 61 } 62} 63 64 65BNetworkCookieJar::~BNetworkCookieJar() 66{ 67 BNetworkCookie* cookiePtr; 68 69 for (Iterator it(GetIterator()); (cookiePtr = it.Next()); ) 70 delete it.Remove(); 71} 72 73 74// #pragma mark Add cookie to cookie jar 75 76 77bool 78BNetworkCookieJar::AddCookie(const BNetworkCookie& cookie) 79{ 80 BNetworkCookie* heapCookie = new(std::nothrow) BNetworkCookie(cookie); 81 82 if (!AddCookie(heapCookie)) { 83 delete heapCookie; 84 return false; 85 } 86 87 return true; 88} 89 90 91bool 92BNetworkCookieJar::AddCookie(BNetworkCookie* cookie) 93{ 94 if (cookie != NULL) { 95 HashString key(cookie->Domain()); 96 97 if (!fCookieHashMap->fHashMap.ContainsKey(key)) 98 fCookieHashMap->fHashMap.Put(key, new BList); 99 100 BNetworkCookieList* list = fCookieHashMap->fHashMap.Get(key); 101 102 for (int32 i = 0; i < list->CountItems(); i++) { 103 BNetworkCookie* c 104 = reinterpret_cast<BNetworkCookie*>(list->ItemAt(i)); 105 106 if (c->Name() == cookie->Name()) { 107 list->RemoveItem(i); 108 break; 109 } 110 } 111 112 // Discard the cookie if it's to be deleted 113 if (!cookie->ShouldDeleteNow()) 114 list->AddItem(cookie); 115 } 116 117 return true; 118} 119 120 121bool 122BNetworkCookieJar::AddCookies(const BNetworkCookieList& cookies) 123{ 124 for (int32 i = 0; i < cookies.CountItems(); i++) { 125 BNetworkCookie* cookiePtr 126 = reinterpret_cast<BNetworkCookie*>(cookies.ItemAt(i)); 127 128 // Using AddCookie by reference in order to avoid multiple 129 // cookie jar share the same cookie pointers 130 if (!AddCookie(*cookiePtr)) 131 return false; 132 } 133 134 return true; 135} 136 137 138// #pragma mark Purge useless cookies 139 140 141uint32 142BNetworkCookieJar::DeleteOutdatedCookies() 143{ 144 int32 deleteCount = 0; 145 BNetworkCookie* cookiePtr; 146 147 for (Iterator it(GetIterator()); (cookiePtr = it.Next()); ) { 148 if (cookiePtr->ShouldDeleteNow()) { 149 delete it.Remove(); 150 deleteCount++; 151 } 152 } 153 154 return deleteCount; 155} 156 157 158uint32 159BNetworkCookieJar::PurgeForExit() 160{ 161 int32 deleteCount = 0; 162 BNetworkCookie* cookiePtr; 163 164 for (Iterator it(GetIterator()); (cookiePtr = it.Next()); ) { 165 if (cookiePtr->ShouldDeleteAtExit()) { 166 delete it.Remove(); 167 deleteCount++; 168 } 169 } 170 171 return deleteCount; 172} 173 174 175// #pragma mark BArchivable interface 176 177 178status_t 179BNetworkCookieJar::Archive(BMessage* into, bool deep) const 180{ 181 status_t error = BArchivable::Archive(into, deep); 182 183 if (error == B_OK) { 184 BNetworkCookie* cookiePtr; 185 186 for (Iterator it(GetIterator()); (cookiePtr = it.Next()); ) { 187 BMessage subArchive; 188 189 error = cookiePtr->Archive(&subArchive, deep); 190 if (error != B_OK) 191 return error; 192 193 error = into->AddMessage(kArchivedCookieMessageName, &subArchive); 194 if (error != B_OK) 195 return error; 196 } 197 } 198 199 return error; 200} 201 202 203BArchivable* 204BNetworkCookieJar::Instantiate(BMessage* archive) 205{ 206 if (archive->HasMessage(kArchivedCookieMessageName)) 207 return new(std::nothrow) BNetworkCookieJar(archive); 208 209 return NULL; 210} 211 212 213// #pragma mark BFlattenable interface 214 215 216bool 217BNetworkCookieJar::IsFixedSize() const 218{ 219 // Flattened size vary 220 return false; 221} 222 223 224type_code 225BNetworkCookieJar::TypeCode() const 226{ 227 // TODO: Add a B_COOKIEJAR_TYPE 228 return B_ANY_TYPE; 229} 230 231 232ssize_t 233BNetworkCookieJar::FlattenedSize() const 234{ 235 _DoFlatten(); 236 return fFlattened.Length() + 1; 237} 238 239 240status_t 241BNetworkCookieJar::Flatten(void* buffer, ssize_t size) const 242{ 243 if (FlattenedSize() > size) 244 return B_ERROR; 245 246 fFlattened.CopyInto(reinterpret_cast<char*>(buffer), 0, 247 fFlattened.Length()); 248 reinterpret_cast<char*>(buffer)[fFlattened.Length()] = 0; 249 250 return B_OK; 251} 252 253 254bool 255BNetworkCookieJar::AllowsTypeCode(type_code) const 256{ 257 // TODO 258 return false; 259} 260 261 262status_t 263BNetworkCookieJar::Unflatten(type_code, const void* buffer, ssize_t size) 264{ 265 BString flattenedCookies; 266 flattenedCookies.SetTo(reinterpret_cast<const char*>(buffer), size); 267 268 while (flattenedCookies.Length() > 0) { 269 BNetworkCookie tempCookie; 270 BString tempCookieLine; 271 272 int32 endOfLine = flattenedCookies.FindFirst('\n', 0); 273 if (endOfLine == -1) 274 tempCookieLine = flattenedCookies; 275 else { 276 flattenedCookies.MoveInto(tempCookieLine, 0, endOfLine); 277 flattenedCookies.Remove(0, 1); 278 } 279 280 if (tempCookieLine.Length() != 0 && tempCookieLine[0] != '#') { 281 for (int32 field = 0; field < 7; field++) { 282 BString tempString; 283 284 int32 endOfField = tempCookieLine.FindFirst('\t', 0); 285 if (endOfField == -1) 286 tempString = tempCookieLine; 287 else { 288 tempCookieLine.MoveInto(tempString, 0, endOfField); 289 tempCookieLine.Remove(0, 1); 290 } 291 292 switch (field) { 293 case 0: 294 tempCookie.SetDomain(tempString); 295 break; 296 297 case 1: 298 // TODO: Useless field ATM 299 break; 300 301 case 2: 302 tempCookie.SetPath(tempString); 303 break; 304 305 case 3: 306 tempCookie.SetSecure(tempString == "TRUE"); 307 break; 308 309 case 4: 310 tempCookie.SetExpirationDate(atoi(tempString)); 311 break; 312 313 case 5: 314 tempCookie.SetName(tempString); 315 break; 316 317 case 6: 318 tempCookie.SetValue(tempString); 319 break; 320 } // switch 321 } // for loop 322 323 AddCookie(tempCookie); 324 } 325 } 326 327 return B_OK; 328} 329 330 331// #pragma mark Iterators 332 333 334BNetworkCookieJar::Iterator 335BNetworkCookieJar::GetIterator() const 336{ 337 return BNetworkCookieJar::Iterator(this); 338} 339 340 341BNetworkCookieJar::UrlIterator 342BNetworkCookieJar::GetUrlIterator(const BUrl& url) const 343{ 344 if (!url.HasPath()) { 345 BUrl copy(url); 346 copy.SetPath("/"); 347 return BNetworkCookieJar::UrlIterator(this, copy); 348 } 349 350 return BNetworkCookieJar::UrlIterator(this, url); 351} 352 353 354void 355BNetworkCookieJar::_DoFlatten() const 356{ 357 fFlattened.Truncate(0); 358 359 BNetworkCookie* cookiePtr; 360 for (Iterator it(GetIterator()); (cookiePtr = it.Next()); ) { 361 fFlattened << cookiePtr->Domain() << '\t' << "TRUE" << '\t' 362 << cookiePtr->Path() << '\t' 363 << (cookiePtr->Secure()?"TRUE":"FALSE") << '\t' 364 << (int32)cookiePtr->ExpirationDate() << '\t' 365 << cookiePtr->Name() << '\t' << cookiePtr->Value() << '\n'; 366 } 367} 368 369 370// #pragma mark Iterator 371 372 373BNetworkCookieJar::Iterator::Iterator(const Iterator& other) 374 : 375 fCookieJar(other.fCookieJar), 376 fIterator(other.fIterator), 377 fLastList(other.fLastList), 378 fList(other.fList), 379 fElement(other.fElement), 380 fLastElement(other.fLastElement), 381 fIndex(other.fIndex) 382{ 383} 384 385 386BNetworkCookieJar::Iterator::Iterator(const BNetworkCookieJar* cookieJar) 387 : 388 fCookieJar(const_cast<BNetworkCookieJar*>(cookieJar)), 389 fIterator(NULL), 390 fLastList(NULL), 391 fList(NULL), 392 fElement(NULL), 393 fLastElement(NULL), 394 fIndex(0) 395{ 396 fIterator = new(std::nothrow) PrivateIterator( 397 fCookieJar->fCookieHashMap->fHashMap.GetIterator()); 398 399 // Locate first cookie 400 _FindNext(); 401} 402 403 404BNetworkCookieJar::Iterator::~Iterator() 405{ 406 delete fIterator; 407} 408 409 410bool 411BNetworkCookieJar::Iterator::HasNext() const 412{ 413 return fElement; 414} 415 416 417BNetworkCookie* 418BNetworkCookieJar::Iterator::Next() 419{ 420 if (!fElement) 421 return NULL; 422 423 BNetworkCookie* result = fElement; 424 _FindNext(); 425 return result; 426} 427 428 429BNetworkCookie* 430BNetworkCookieJar::Iterator::NextDomain() 431{ 432 if (!fElement) 433 return NULL; 434 435 BNetworkCookie* result = fElement; 436 437 if (!fIterator->fCookieMapIterator.HasNext()) { 438 fElement = NULL; 439 return NULL; 440 } 441 442 fList = *(fIterator->fCookieMapIterator.NextValue()); 443 fIndex = 0; 444 fElement = reinterpret_cast<BNetworkCookie*>(fList->ItemAt(fIndex)); 445 446 return result; 447} 448 449 450BNetworkCookie* 451BNetworkCookieJar::Iterator::Remove() 452{ 453 if (!fLastElement) 454 return NULL; 455 456 BNetworkCookie* result = fLastElement; 457 458 if (fIndex == 0) { 459 if (fLastList->CountItems() == 1) { 460 fIterator->fCookieMapIterator.Remove(); 461 delete fLastList; 462 } 463 else 464 fLastList->RemoveItem(fLastList->CountItems() - 1); 465 } else { 466 fList->RemoveItem(fIndex-1); 467 fIndex--; 468 } 469 470 fLastElement = NULL; 471 return result; 472} 473 474 475BNetworkCookieJar::Iterator& 476BNetworkCookieJar::Iterator::operator=(const BNetworkCookieJar::Iterator& other) 477{ 478 fCookieJar = other.fCookieJar; 479 fIterator = other.fIterator; 480 fLastList = other.fLastList; 481 fList = other.fList; 482 fElement = other.fElement; 483 fLastElement = other.fLastElement; 484 fIndex = other.fIndex; 485 return *this; 486} 487 488 489void 490BNetworkCookieJar::Iterator::_FindNext() 491{ 492 fLastElement = fElement; 493 494 fIndex++; 495 if (fList && fIndex < fList->CountItems()) { 496 fElement = reinterpret_cast<BNetworkCookie*>(fList->ItemAt(fIndex)); 497 return; 498 } 499 500 if (!fIterator->fCookieMapIterator.HasNext()) { 501 fElement = NULL; 502 return; 503 } 504 505 fLastList = fList; 506 fList = *(fIterator->fCookieMapIterator.NextValue()); 507 fIndex = 0; 508 fElement = reinterpret_cast<BNetworkCookie*>(fList->ItemAt(fIndex)); 509} 510 511 512// #pragma mark URL Iterator 513 514 515BNetworkCookieJar::UrlIterator::UrlIterator(const UrlIterator& other) 516{ 517 *this = other; 518} 519 520 521BNetworkCookieJar::UrlIterator::UrlIterator(const BNetworkCookieJar* cookieJar, 522 const BUrl& url) 523 : 524 fCookieJar(const_cast<BNetworkCookieJar*>(cookieJar)), 525 fIterator(NULL), 526 fList(NULL), 527 fLastList(NULL), 528 fElement(NULL), 529 fLastElement(NULL), 530 fIndex(0), 531 fLastIndex(0), 532 fUrl(const_cast<BUrl&>(url)) 533{ 534 BString domain(url.Host()); 535 536 if (!domain.Length()) 537 return; 538 539 if (domain[0] != '.') 540 domain.Prepend("."); 541 542 // Prepending another dot since _FindNext is going to 543 // call _SupDomain() 544 domain.Prepend("."); 545 546 fIterator = new(std::nothrow) PrivateIterator( 547 fCookieJar->fCookieHashMap->fHashMap.GetIterator()); 548 fIterator->fKey.SetTo(domain, domain.Length()); 549 550 _FindNext(); 551} 552 553 554BNetworkCookieJar::UrlIterator::~UrlIterator() 555{ 556 delete fIterator; 557} 558 559 560bool 561BNetworkCookieJar::UrlIterator::HasNext() const 562{ 563 return fElement; 564} 565 566 567BNetworkCookie* 568BNetworkCookieJar::UrlIterator::Next() 569{ 570 if (!fElement) 571 return NULL; 572 573 BNetworkCookie* result = fElement; 574 _FindNext(); 575 return result; 576} 577 578 579BNetworkCookie* 580BNetworkCookieJar::UrlIterator::Remove() 581{ 582 if (!fLastElement) 583 return NULL; 584 585 BNetworkCookie* result = fLastElement; 586 587 fLastList->RemoveItem(fLastIndex); 588 589 if (fLastList->CountItems() == 0) { 590 HashString lastKey(fLastElement->Domain(), 591 fLastElement->Domain().Length()); 592 593 delete fCookieJar->fCookieHashMap->fHashMap.Remove(lastKey); 594 } 595 596 fLastElement = NULL; 597 return result; 598} 599 600 601BNetworkCookieJar::UrlIterator& 602BNetworkCookieJar::UrlIterator::operator=( 603 const BNetworkCookieJar::UrlIterator& other) 604{ 605 fCookieJar = other.fCookieJar; 606 fList = other.fList; 607 fLastList = other.fLastList; 608 fElement = other.fElement; 609 fLastElement = other.fLastElement; 610 fIndex = other.fIndex; 611 fLastIndex = other.fLastIndex; 612 fUrl = other.fUrl; 613 fIterator = other.fIterator; 614 return *this; 615} 616 617 618bool 619BNetworkCookieJar::UrlIterator::_SupDomain() 620{ 621 BString domain(fIterator->fKey.GetString()); 622 int32 nextDot = domain.FindFirst('.', 1); 623 624 if (nextDot == -1) 625 return false; 626 627 domain.Remove(0, nextDot); 628 fIterator->fKey.SetTo(domain.String(), domain.Length()); 629 return true; 630} 631 632 633void 634BNetworkCookieJar::UrlIterator::_FindNext() 635{ 636 fLastIndex = fIndex; 637 fLastElement = fElement; 638 639 if (_FindPath()) 640 return; 641 642 fLastList = fList; 643 do { 644 if (!_SupDomain()) { 645 fElement = NULL; 646 return; 647 } 648 649 _FindDomain(); 650 } while (!_FindPath()); 651} 652 653 654void 655BNetworkCookieJar::UrlIterator::_FindDomain() 656{ 657 fList = fCookieJar->fCookieHashMap->fHashMap.Get(fIterator->fKey); 658 659 if (fList == NULL) 660 fElement = NULL; 661 662 fIndex = -1; 663} 664 665 666bool 667BNetworkCookieJar::UrlIterator::_FindPath() 668{ 669 fIndex++; 670 if (fList && fIndex < fList->CountItems()) { 671 do { 672 fElement 673 = reinterpret_cast<BNetworkCookie*>(fList->ItemAt(fIndex)); 674 675 if (fElement->IsValidForPath(fUrl.Path())) 676 return true; 677 678 fIndex++; 679 } while (fList && fIndex < fList->CountItems()); 680 } 681 682 return false; 683} 684