1 /* 2 * Copyright 2011-2016, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "IMAPConnectionWorker.h" 8 9 #include <Autolock.h> 10 #include <Messenger.h> 11 12 #include <AutoDeleter.h> 13 14 #include "IMAPFolder.h" 15 #include "IMAPMailbox.h" 16 #include "IMAPProtocol.h" 17 18 19 using IMAP::MessageUIDList; 20 21 22 static const uint32 kMaxFetchEntries = 500; 23 static const uint32 kMaxDirectDownloadSize = 4096; 24 25 26 class WorkerPrivate { 27 public: 28 WorkerPrivate(IMAPConnectionWorker& worker) 29 : 30 fWorker(worker) 31 { 32 } 33 34 IMAP::Protocol& Protocol() 35 { 36 return fWorker.fProtocol; 37 } 38 39 status_t AddFolders(BObjectList<IMAPFolder>& folders) 40 { 41 IMAPConnectionWorker::MailboxMap::iterator iterator 42 = fWorker.fMailboxes.begin(); 43 for (; iterator != fWorker.fMailboxes.end(); iterator++) { 44 IMAPFolder* folder = iterator->first; 45 if (!folders.AddItem(folder)) 46 return B_NO_MEMORY; 47 } 48 return B_OK; 49 } 50 51 status_t SelectMailbox(IMAPFolder& folder) 52 { 53 return fWorker._SelectMailbox(folder, NULL); 54 } 55 56 status_t SelectMailbox(IMAPFolder& folder, uint32& nextUID) 57 { 58 return fWorker._SelectMailbox(folder, &nextUID); 59 } 60 61 IMAPMailbox* MailboxFor(IMAPFolder& folder) 62 { 63 return fWorker._MailboxFor(folder); 64 } 65 66 int32 BodyFetchLimit() const 67 { 68 return fWorker.fSettings.BodyFetchLimit(); 69 } 70 71 uint32 MessagesExist() const 72 { 73 return fWorker._MessagesExist(); 74 } 75 76 status_t EnqueueCommand(WorkerCommand* command) 77 { 78 return fWorker._EnqueueCommand(command); 79 } 80 81 void SyncCommandDone() 82 { 83 fWorker._SyncCommandDone(); 84 } 85 86 void Quit() 87 { 88 fWorker.fStopped = true; 89 } 90 91 private: 92 IMAPConnectionWorker& fWorker; 93 }; 94 95 96 class WorkerCommand { 97 public: 98 WorkerCommand() 99 : 100 fContinuation(false) 101 { 102 } 103 104 virtual ~WorkerCommand() 105 { 106 } 107 108 virtual status_t Process(IMAPConnectionWorker& worker) = 0; 109 110 virtual bool IsDone() const 111 { 112 return true; 113 } 114 115 bool IsContinuation() const 116 { 117 return fContinuation; 118 } 119 120 void SetContinuation() 121 { 122 fContinuation = true; 123 } 124 125 private: 126 bool fContinuation; 127 }; 128 129 130 /*! All commands that inherit from this class will automatically maintain the 131 worker's fSyncPending member, and will thus prevent syncing more than once 132 concurrently. 133 */ 134 class SyncCommand : public WorkerCommand { 135 }; 136 137 138 class QuitCommand : public WorkerCommand { 139 public: 140 QuitCommand() 141 { 142 } 143 144 virtual status_t Process(IMAPConnectionWorker& worker) 145 { 146 WorkerPrivate(worker).Quit(); 147 return B_OK; 148 } 149 }; 150 151 152 class CheckSubscribedFoldersCommand : public WorkerCommand { 153 public: 154 virtual status_t Process(IMAPConnectionWorker& worker) 155 { 156 IMAP::Protocol& protocol = WorkerPrivate(worker).Protocol(); 157 158 // The main worker checks the subscribed folders, and creates 159 // other workers as needed 160 return worker.Owner().CheckSubscribedFolders(protocol, 161 worker.UsesIdle()); 162 } 163 }; 164 165 166 class FetchBodiesCommand : public SyncCommand, public IMAP::FetchListener { 167 public: 168 FetchBodiesCommand(IMAPFolder& folder, IMAPMailbox& mailbox, 169 MessageUIDList& entries, const BMessenger* replyTo = NULL) 170 : 171 fFolder(folder), 172 fMailbox(mailbox), 173 fEntries(entries) 174 { 175 folder.RegisterPendingBodies(entries, replyTo); 176 } 177 178 virtual status_t Process(IMAPConnectionWorker& worker) 179 { 180 IMAP::Protocol& protocol = WorkerPrivate(worker).Protocol(); 181 182 if (fEntries.empty()) 183 return B_OK; 184 185 fUID = *fEntries.begin(); 186 fEntries.erase(fEntries.begin()); 187 188 status_t status = WorkerPrivate(worker).SelectMailbox(fFolder); 189 if (status == B_OK) { 190 printf("IMAP: fetch body for %" B_PRIu32 "\n", fUID); 191 // Since RFC3501 does not specify whether the FETCH response may 192 // alter the order of the message data items we request, we cannot 193 // request more than a single UID at a time, or else we may not be 194 // able to assign the data to the correct message beforehand. 195 IMAP::FetchCommand fetch(fUID, fUID, IMAP::kFetchBody); 196 fetch.SetListener(this); 197 198 status = protocol.ProcessCommand(fetch); 199 } 200 if (status == B_OK) 201 status = fFetchStatus; 202 if (status != B_OK) 203 fFolder.StoringBodyFailed(fRef, fUID, status); 204 205 return status; 206 } 207 208 virtual bool IsDone() const 209 { 210 return fEntries.empty(); 211 } 212 213 virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t& length) 214 { 215 fFetchStatus = fFolder.StoreBody(fUID, stream, length, fRef, fFile); 216 return true; 217 } 218 219 virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags) 220 { 221 if (fFetchStatus == B_OK) 222 fFolder.BodyStored(fRef, fFile, uid); 223 } 224 225 private: 226 IMAPFolder& fFolder; 227 IMAPMailbox& fMailbox; 228 MessageUIDList fEntries; 229 uint32 fUID; 230 entry_ref fRef; 231 BFile fFile; 232 status_t fFetchStatus; 233 }; 234 235 236 class FetchHeadersCommand : public SyncCommand, public IMAP::FetchListener { 237 public: 238 FetchHeadersCommand(IMAPFolder& folder, IMAPMailbox& mailbox, 239 MessageUIDList& uids, int32 bodyFetchLimit) 240 : 241 fFolder(folder), 242 fMailbox(mailbox), 243 fUIDs(uids), 244 fBodyFetchLimit(bodyFetchLimit) 245 { 246 } 247 248 virtual status_t Process(IMAPConnectionWorker& worker) 249 { 250 IMAP::Protocol& protocol = WorkerPrivate(worker).Protocol(); 251 252 status_t status = WorkerPrivate(worker).SelectMailbox(fFolder); 253 if (status != B_OK) 254 return status; 255 256 printf("IMAP: fetch %" B_PRIuSIZE "u headers\n", fUIDs.size()); 257 258 IMAP::FetchCommand fetch(fUIDs, kMaxFetchEntries, 259 IMAP::kFetchHeader | IMAP::kFetchFlags); 260 fetch.SetListener(this); 261 262 status = protocol.ProcessCommand(fetch); 263 if (status != B_OK) 264 return status; 265 266 if (IsDone() && !fFetchBodies.empty()) { 267 // Enqueue command to fetch the message bodies 268 WorkerPrivate(worker).EnqueueCommand(new FetchBodiesCommand(fFolder, 269 fMailbox, fFetchBodies)); 270 } 271 272 return B_OK; 273 } 274 275 virtual bool IsDone() const 276 { 277 return fUIDs.empty(); 278 } 279 280 virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t& length) 281 { 282 fFetchStatus = fFolder.StoreMessage(fetchFlags, stream, length, 283 fRef, fFile); 284 return true; 285 } 286 287 virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags) 288 { 289 if (fFetchStatus == B_OK) { 290 fFolder.MessageStored(fRef, fFile, fetchFlags, uid, flags); 291 292 uint32 size = fMailbox.MessageSize(uid); 293 if (fBodyFetchLimit < 0 || size < fBodyFetchLimit) 294 fFetchBodies.push_back(uid); 295 } 296 } 297 298 private: 299 IMAPFolder& fFolder; 300 IMAPMailbox& fMailbox; 301 MessageUIDList fUIDs; 302 MessageUIDList fFetchBodies; 303 uint32 fBodyFetchLimit; 304 entry_ref fRef; 305 BFile fFile; 306 status_t fFetchStatus; 307 }; 308 309 310 class CheckMailboxesCommand : public SyncCommand { 311 public: 312 CheckMailboxesCommand(IMAPConnectionWorker& worker) 313 : 314 fWorker(worker), 315 fFolders(5, false), 316 fState(INIT), 317 fFolder(NULL), 318 fMailbox(NULL) 319 { 320 } 321 322 virtual status_t Process(IMAPConnectionWorker& worker) 323 { 324 IMAP::Protocol& protocol = WorkerPrivate(worker).Protocol(); 325 326 if (fState == INIT) { 327 // Collect folders 328 status_t status = WorkerPrivate(worker).AddFolders(fFolders); 329 if (status != B_OK || fFolders.IsEmpty()) { 330 fState = DONE; 331 return status; 332 } 333 334 fState = SELECT; 335 } 336 337 if (fState == SELECT) { 338 // Get next mailbox from list, and select it 339 fFolder = fFolders.RemoveItemAt(fFolders.CountItems() - 1); 340 if (fFolder == NULL) { 341 for (int32 i = 0; i < fFetchCommands.CountItems(); i++) { 342 WorkerPrivate(worker).EnqueueCommand( 343 fFetchCommands.ItemAt(i)); 344 } 345 346 fState = DONE; 347 return B_OK; 348 } 349 350 fMailbox = WorkerPrivate(worker).MailboxFor(*fFolder); 351 352 status_t status = WorkerPrivate(worker).SelectMailbox(*fFolder); 353 if (status != B_OK) 354 return status; 355 356 fLastIndex = WorkerPrivate(worker).MessagesExist(); 357 fFirstIndex = fMailbox->CountMessages() + 1; 358 if (fLastIndex > 0) 359 fState = FETCH_ENTRIES; 360 } 361 362 if (fState == FETCH_ENTRIES) { 363 status_t status = WorkerPrivate(worker).SelectMailbox(*fFolder); 364 if (status != B_OK) 365 return status; 366 367 uint32 to = fLastIndex; 368 uint32 from = fFirstIndex + kMaxFetchEntries < to 369 ? fLastIndex - kMaxFetchEntries : fFirstIndex; 370 371 printf("IMAP: get entries from %" B_PRIu32 " to %" B_PRIu32 "\n", 372 from, to); 373 374 IMAP::MessageEntryList entries; 375 IMAP::FetchMessageEntriesCommand fetch(entries, from, to, false); 376 status = protocol.ProcessCommand(fetch); 377 if (status != B_OK) 378 return status; 379 380 // Determine how much we need to download 381 // TODO: also retrieve the header size, and only take the body 382 // size into account if it's below the limit -- that does not 383 // seem to be possible, though 384 for (size_t i = 0; i < entries.size(); i++) { 385 printf("%10" B_PRIu32 " %8" B_PRIu32 " bytes, flags: %#" 386 B_PRIx32 "\n", entries[i].uid, entries[i].size, 387 entries[i].flags); 388 fMailbox->AddMessageEntry(from + i, entries[i].uid, 389 entries[i].flags, entries[i].size); 390 391 if (entries[i].uid > fFolder->LastUID()) { 392 fTotalBytes += entries[i].size; 393 fUIDsToFetch.push_back(entries[i].uid); 394 } else { 395 fFolder->SyncMessageFlags(entries[i].uid, entries[i].flags); 396 } 397 } 398 399 fTotalEntries += fUIDsToFetch.size(); 400 fLastIndex = from - 1; 401 402 if (from == 1) { 403 fFolder->MessageEntriesFetched(); 404 405 if (fUIDsToFetch.size() > 0) { 406 // Add pending command to fetch the message headers 407 WorkerCommand* command = new FetchHeadersCommand(*fFolder, 408 *fMailbox, fUIDsToFetch, 409 WorkerPrivate(worker).BodyFetchLimit()); 410 if (!fFetchCommands.AddItem(command)) 411 delete command; 412 413 fUIDsToFetch.clear(); 414 } 415 fState = SELECT; 416 } 417 } 418 419 return B_OK; 420 } 421 422 virtual bool IsDone() const 423 { 424 return fState == DONE; 425 } 426 427 private: 428 enum State { 429 INIT, 430 SELECT, 431 FETCH_ENTRIES, 432 DONE 433 }; 434 435 IMAPConnectionWorker& fWorker; 436 BObjectList<IMAPFolder> fFolders; 437 State fState; 438 IMAPFolder* fFolder; 439 IMAPMailbox* fMailbox; 440 uint32 fFirstIndex; 441 uint32 fLastIndex; 442 uint64 fTotalEntries; 443 uint64 fTotalBytes; 444 WorkerCommandList fFetchCommands; 445 MessageUIDList fUIDsToFetch; 446 }; 447 448 449 class UpdateFlagsCommand : public WorkerCommand { 450 public: 451 UpdateFlagsCommand(IMAPFolder& folder, IMAPMailbox& mailbox, 452 MessageUIDList& entries, uint32 flags) 453 : 454 fFolder(folder), 455 fMailbox(mailbox), 456 fEntries(entries), 457 fFlags(flags) 458 { 459 } 460 461 virtual status_t Process(IMAPConnectionWorker& worker) 462 { 463 if (fEntries.empty()) 464 return B_OK; 465 466 fUID = *fEntries.begin(); 467 fEntries.erase(fEntries.begin()); 468 469 status_t status = WorkerPrivate(worker).SelectMailbox(fFolder); 470 if (status == B_OK) { 471 IMAP::Protocol& protocol = WorkerPrivate(worker).Protocol(); 472 IMAP::SetFlagsCommand set(fUID, fFlags); 473 status = protocol.ProcessCommand(set); 474 } 475 476 return status; 477 } 478 479 virtual bool IsDone() const 480 { 481 return fEntries.empty(); 482 } 483 484 private: 485 IMAPFolder& fFolder; 486 IMAPMailbox& fMailbox; 487 MessageUIDList fEntries; 488 uint32 fUID; 489 uint32 fFlags; 490 }; 491 492 493 struct CommandDelete 494 { 495 inline void operator()(WorkerCommand* command) 496 { 497 delete command; 498 } 499 }; 500 501 502 /*! An auto deleter similar to ObjectDeleter that calls SyncCommandDone() 503 for all SyncCommands. 504 */ 505 struct CommandDeleter : BPrivate::AutoDeleter<WorkerCommand, CommandDelete> 506 { 507 CommandDeleter(IMAPConnectionWorker& worker, WorkerCommand* command) 508 : 509 BPrivate::AutoDeleter<WorkerCommand, CommandDelete>(command), 510 fWorker(worker) 511 { 512 } 513 514 ~CommandDeleter() 515 { 516 if (dynamic_cast<SyncCommand*>(fObject) != NULL) 517 WorkerPrivate(fWorker).SyncCommandDone(); 518 } 519 520 private: 521 IMAPConnectionWorker& fWorker; 522 }; 523 524 525 // #pragma mark - 526 527 528 IMAPConnectionWorker::IMAPConnectionWorker(IMAPProtocol& owner, 529 const Settings& settings, bool main) 530 : 531 fOwner(owner), 532 fSettings(settings), 533 fPendingCommandsSemaphore(-1), 534 fIdleBox(NULL), 535 fMain(main), 536 fStopped(false) 537 { 538 fExistsHandler.SetListener(this); 539 fProtocol.AddHandler(fExistsHandler); 540 541 fExpungeHandler.SetListener(this); 542 fProtocol.AddHandler(fExpungeHandler); 543 } 544 545 546 IMAPConnectionWorker::~IMAPConnectionWorker() 547 { 548 puts("worker quit"); 549 delete_sem(fPendingCommandsSemaphore); 550 _Disconnect(); 551 } 552 553 554 bool 555 IMAPConnectionWorker::HasMailboxes() const 556 { 557 BAutolock locker(const_cast<IMAPConnectionWorker*>(this)->fLocker); 558 return !fMailboxes.empty(); 559 } 560 561 562 uint32 563 IMAPConnectionWorker::CountMailboxes() const 564 { 565 BAutolock locker(const_cast<IMAPConnectionWorker*>(this)->fLocker); 566 return fMailboxes.size(); 567 } 568 569 570 void 571 IMAPConnectionWorker::AddMailbox(IMAPFolder* folder) 572 { 573 BAutolock locker(fLocker); 574 575 fMailboxes.insert(std::make_pair(folder, (IMAPMailbox*)NULL)); 576 577 // Prefer to have the INBOX in idle mode over other mail boxes 578 if (fIdleBox == NULL || folder->MailboxName().ICompare("INBOX") == 0) 579 fIdleBox = folder; 580 } 581 582 583 void 584 IMAPConnectionWorker::RemoveAllMailboxes() 585 { 586 BAutolock locker(fLocker); 587 588 // Reset listeners, and delete the mailboxes 589 MailboxMap::iterator iterator = fMailboxes.begin(); 590 for (; iterator != fMailboxes.end(); iterator++) { 591 iterator->first->SetListener(NULL); 592 delete iterator->second; 593 } 594 595 fIdleBox = NULL; 596 fMailboxes.clear(); 597 } 598 599 600 status_t 601 IMAPConnectionWorker::Run() 602 { 603 fPendingCommandsSemaphore = create_sem(0, "imap pending commands"); 604 if (fPendingCommandsSemaphore < 0) 605 return fPendingCommandsSemaphore; 606 607 fThread = spawn_thread(&_Worker, "imap connection worker", 608 B_NORMAL_PRIORITY, this); 609 if (fThread < 0) 610 return fThread; 611 612 resume_thread(fThread); 613 return B_OK; 614 } 615 616 617 void 618 IMAPConnectionWorker::Quit() 619 { 620 printf("IMAP: worker %p: enqueue quit\n", this); 621 BAutolock locker(fLocker); 622 while (!fPendingCommands.IsEmpty()) 623 delete(fPendingCommands.RemoveItemAt(0)); 624 locker.Unlock(); 625 _EnqueueCommand(new QuitCommand()); 626 } 627 628 629 status_t 630 IMAPConnectionWorker::EnqueueCheckSubscribedFolders() 631 { 632 printf("IMAP: worker %p: enqueue check subscribed folders\n", this); 633 return _EnqueueCommand(new CheckSubscribedFoldersCommand()); 634 } 635 636 637 status_t 638 IMAPConnectionWorker::EnqueueCheckMailboxes() 639 { 640 // Do not schedule checking mailboxes again if we're still working on 641 // those. 642 if (fSyncPending > 0) 643 return B_OK; 644 645 printf("IMAP: worker %p: enqueue check mailboxes\n", this); 646 return _EnqueueCommand(new CheckMailboxesCommand(*this)); 647 } 648 649 650 status_t 651 IMAPConnectionWorker::EnqueueFetchBody(IMAPFolder& folder, uint32 uid, 652 const BMessenger& replyTo) 653 { 654 IMAPMailbox* mailbox = _MailboxFor(folder); 655 if (mailbox == NULL) 656 return B_ENTRY_NOT_FOUND; 657 658 std::vector<uint32> uids; 659 uids.push_back(uid); 660 661 return _EnqueueCommand(new FetchBodiesCommand(folder, *mailbox, uids, 662 &replyTo)); 663 } 664 665 666 status_t 667 IMAPConnectionWorker::EnqueueUpdateFlags(IMAPFolder& folder, uint32 uid, 668 uint32 flags) 669 { 670 IMAPMailbox* mailbox = _MailboxFor(folder); 671 if (mailbox == NULL) 672 return B_ENTRY_NOT_FOUND; 673 674 std::vector<uint32> uids; 675 uids.push_back(uid); 676 677 return _EnqueueCommand(new UpdateFlagsCommand(folder, *mailbox, uids, 678 flags)); 679 } 680 681 682 // #pragma mark - Handler listener 683 684 685 void 686 IMAPConnectionWorker::MessageExistsReceived(uint32 count) 687 { 688 printf("Message exists: %" B_PRIu32 "\n", count); 689 fMessagesExist = count; 690 691 // TODO: We might want to trigger another check even during sync 692 // (but only one), if this isn't the result of a SELECT 693 EnqueueCheckMailboxes(); 694 } 695 696 697 void 698 IMAPConnectionWorker::MessageExpungeReceived(uint32 index) 699 { 700 printf("Message expunge: %" B_PRIu32 "\n", index); 701 if (fSelectedBox == NULL) 702 return; 703 704 BAutolock locker(fLocker); 705 706 IMAPMailbox* mailbox = _MailboxFor(*fSelectedBox); 707 if (mailbox != NULL) { 708 mailbox->RemoveMessageEntry(index); 709 // TODO: remove message from folder 710 } 711 } 712 713 714 // #pragma mark - private 715 716 717 status_t 718 IMAPConnectionWorker::_Worker() 719 { 720 status_t status = B_OK; 721 722 while (!fStopped) { 723 BAutolock locker(fLocker); 724 725 if (fPendingCommands.IsEmpty()) { 726 if (!fIdle) 727 _Disconnect(); 728 locker.Unlock(); 729 730 // TODO: in idle mode, we'd need to parse any incoming message here 731 _WaitForCommands(); 732 continue; 733 } 734 735 WorkerCommand* command = fPendingCommands.RemoveItemAt(0); 736 if (command == NULL) 737 continue; 738 739 CommandDeleter deleter(*this, command); 740 741 if (dynamic_cast<QuitCommand*>(command) == NULL) { // do not connect on QuitCommand 742 status = _Connect(); 743 if (status != B_OK) 744 break; 745 } 746 747 status = command->Process(*this); 748 if (status != B_OK) 749 break; 750 751 if (!command->IsDone()) { 752 deleter.Detach(); 753 command->SetContinuation(); 754 _EnqueueCommand(command); 755 } 756 } 757 758 fOwner.WorkerQuit(this); 759 return status; 760 } 761 762 763 /*! Enqueues the given command to the worker queue. This method will take 764 over ownership of the given command even in the error case. 765 */ 766 status_t 767 IMAPConnectionWorker::_EnqueueCommand(WorkerCommand* command) 768 { 769 BAutolock locker(fLocker); 770 771 if (!fPendingCommands.AddItem(command)) { 772 delete command; 773 return B_NO_MEMORY; 774 } 775 776 if (dynamic_cast<SyncCommand*>(command) != NULL 777 && !command->IsContinuation()) 778 fSyncPending++; 779 780 locker.Unlock(); 781 release_sem(fPendingCommandsSemaphore); 782 return B_OK; 783 } 784 785 786 void 787 IMAPConnectionWorker::_WaitForCommands() 788 { 789 int32 count = 1; 790 get_sem_count(fPendingCommandsSemaphore, &count); 791 if (count < 1) 792 count = 1; 793 794 while (acquire_sem_etc(fPendingCommandsSemaphore, count, 0, 795 B_INFINITE_TIMEOUT) == B_INTERRUPTED); 796 } 797 798 799 status_t 800 IMAPConnectionWorker::_SelectMailbox(IMAPFolder& folder, uint32* _nextUID) 801 { 802 if (fSelectedBox == &folder && _nextUID == NULL) 803 return B_OK; 804 805 IMAP::SelectCommand select(folder.MailboxName().String()); 806 807 status_t status = fProtocol.ProcessCommand(select); 808 if (status == B_OK) { 809 folder.SetUIDValidity(select.UIDValidity()); 810 if (_nextUID != NULL) 811 *_nextUID = select.NextUID(); 812 fSelectedBox = &folder; 813 } 814 815 return status; 816 } 817 818 819 IMAPMailbox* 820 IMAPConnectionWorker::_MailboxFor(IMAPFolder& folder) 821 { 822 MailboxMap::iterator found = fMailboxes.find(&folder); 823 if (found == fMailboxes.end()) 824 return NULL; 825 826 IMAPMailbox* mailbox = found->second; 827 if (mailbox == NULL) { 828 mailbox = new IMAPMailbox(fProtocol, folder.MailboxName()); 829 folder.SetListener(mailbox); 830 found->second = mailbox; 831 } 832 return mailbox; 833 } 834 835 836 void 837 IMAPConnectionWorker::_SyncCommandDone() 838 { 839 fSyncPending--; 840 } 841 842 843 status_t 844 IMAPConnectionWorker::_Connect() 845 { 846 if (fProtocol.IsConnected()) 847 return B_OK; 848 849 status_t status; 850 int tries = 6; 851 while (tries-- > 0) { 852 status = fProtocol.Connect(fSettings.ServerAddress(), 853 fSettings.Username(), fSettings.Password(), fSettings.UseSSL()); 854 if (status == B_OK) 855 break; 856 857 // Wait for 10 seconds, and try again 858 snooze(10000000); 859 } 860 // TODO: if other workers are connected, but it fails for us, we need to 861 // remove this worker, and reduce the number of concurrent connections 862 if (status != B_OK) 863 return status; 864 865 //fIdle = fSettings.IdleMode() && fProtocol.Capabilities().Contains("IDLE"); 866 // TODO: Idle mode is not yet implemented! 867 fIdle = false; 868 return B_OK; 869 } 870 871 872 void 873 IMAPConnectionWorker::_Disconnect() 874 { 875 fProtocol.Disconnect(); 876 fSelectedBox = NULL; 877 } 878 879 880 /*static*/ status_t 881 IMAPConnectionWorker::_Worker(void* _self) 882 { 883 IMAPConnectionWorker* self = (IMAPConnectionWorker*)_self; 884 status_t status = self->_Worker(); 885 886 delete self; 887 return status; 888 } 889