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*>(Get()) != 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 qlocker(fQueueLocker); 622 while (!fPendingCommands.IsEmpty()) 623 delete(fPendingCommands.RemoveItemAt(0)); 624 _EnqueueCommand(new QuitCommand()); 625 } 626 627 628 status_t 629 IMAPConnectionWorker::EnqueueCheckSubscribedFolders() 630 { 631 printf("IMAP: worker %p: enqueue check subscribed folders\n", this); 632 return _EnqueueCommand(new CheckSubscribedFoldersCommand()); 633 } 634 635 636 status_t 637 IMAPConnectionWorker::EnqueueCheckMailboxes() 638 { 639 // Do not schedule checking mailboxes again if we're still working on 640 // those. 641 if (fSyncPending > 0) 642 return B_OK; 643 644 printf("IMAP: worker %p: enqueue check mailboxes\n", this); 645 return _EnqueueCommand(new CheckMailboxesCommand(*this)); 646 } 647 648 649 status_t 650 IMAPConnectionWorker::EnqueueFetchBody(IMAPFolder& folder, uint32 uid, 651 const BMessenger& replyTo) 652 { 653 IMAPMailbox* mailbox = _MailboxFor(folder); 654 if (mailbox == NULL) 655 return B_ENTRY_NOT_FOUND; 656 657 std::vector<uint32> uids; 658 uids.push_back(uid); 659 660 return _EnqueueCommand(new FetchBodiesCommand(folder, *mailbox, uids, 661 &replyTo)); 662 } 663 664 665 status_t 666 IMAPConnectionWorker::EnqueueUpdateFlags(IMAPFolder& folder, uint32 uid, 667 uint32 flags) 668 { 669 IMAPMailbox* mailbox = _MailboxFor(folder); 670 if (mailbox == NULL) 671 return B_ENTRY_NOT_FOUND; 672 673 std::vector<uint32> uids; 674 uids.push_back(uid); 675 676 return _EnqueueCommand(new UpdateFlagsCommand(folder, *mailbox, uids, 677 flags)); 678 } 679 680 681 // #pragma mark - Handler listener 682 683 684 void 685 IMAPConnectionWorker::MessageExistsReceived(uint32 count) 686 { 687 printf("Message exists: %" B_PRIu32 "\n", count); 688 fMessagesExist = count; 689 690 // TODO: We might want to trigger another check even during sync 691 // (but only one), if this isn't the result of a SELECT 692 EnqueueCheckMailboxes(); 693 } 694 695 696 void 697 IMAPConnectionWorker::MessageExpungeReceived(uint32 index) 698 { 699 printf("Message expunge: %" B_PRIu32 "\n", index); 700 if (fSelectedBox == NULL) 701 return; 702 703 BAutolock locker(fLocker); 704 705 IMAPMailbox* mailbox = _MailboxFor(*fSelectedBox); 706 if (mailbox != NULL) { 707 mailbox->RemoveMessageEntry(index); 708 // TODO: remove message from folder 709 } 710 } 711 712 713 // #pragma mark - private 714 715 716 status_t 717 IMAPConnectionWorker::_Worker() 718 { 719 status_t status = B_OK; 720 721 while (!fStopped) { 722 BAutolock qlocker(fQueueLocker); 723 724 if (fPendingCommands.IsEmpty()) { 725 if (!fIdle) 726 _Disconnect(); 727 qlocker.Unlock(); 728 729 // TODO: in idle mode, we'd need to parse any incoming message here 730 _WaitForCommands(); 731 continue; 732 } 733 734 WorkerCommand* command = fPendingCommands.RemoveItemAt(0); 735 if (command == NULL) 736 continue; 737 738 qlocker.Unlock(); 739 BAutolock locker(fLocker); 740 CommandDeleter deleter(*this, command); 741 742 if (dynamic_cast<QuitCommand*>(command) == NULL) { // do not connect on QuitCommand 743 status = _Connect(); 744 if (status != B_OK) 745 break; 746 } 747 748 status = command->Process(*this); 749 if (status != B_OK) 750 break; 751 752 if (!command->IsDone()) { 753 deleter.Detach(); 754 command->SetContinuation(); 755 locker.Unlock(); 756 _EnqueueCommand(command); 757 } 758 } 759 760 fOwner.WorkerQuit(this); 761 return status; 762 } 763 764 765 /*! Enqueues the given command to the worker queue. This method will take 766 over ownership of the given command even in the error case. 767 */ 768 status_t 769 IMAPConnectionWorker::_EnqueueCommand(WorkerCommand* command) 770 { 771 BAutolock qlocker(fQueueLocker); 772 773 if (!fPendingCommands.AddItem(command)) { 774 delete command; 775 return B_NO_MEMORY; 776 } 777 778 if (dynamic_cast<SyncCommand*>(command) != NULL 779 && !command->IsContinuation()) 780 fSyncPending++; 781 782 qlocker.Unlock(); 783 release_sem(fPendingCommandsSemaphore); 784 return B_OK; 785 } 786 787 788 void 789 IMAPConnectionWorker::_WaitForCommands() 790 { 791 int32 count = 1; 792 get_sem_count(fPendingCommandsSemaphore, &count); 793 if (count < 1) 794 count = 1; 795 796 while (acquire_sem_etc(fPendingCommandsSemaphore, count, 0, 797 B_INFINITE_TIMEOUT) == B_INTERRUPTED); 798 } 799 800 801 status_t 802 IMAPConnectionWorker::_SelectMailbox(IMAPFolder& folder, uint32* _nextUID) 803 { 804 if (fSelectedBox == &folder && _nextUID == NULL) 805 return B_OK; 806 807 IMAP::SelectCommand select(folder.MailboxName().String()); 808 809 status_t status = fProtocol.ProcessCommand(select); 810 if (status == B_OK) { 811 folder.SetUIDValidity(select.UIDValidity()); 812 if (_nextUID != NULL) 813 *_nextUID = select.NextUID(); 814 fSelectedBox = &folder; 815 } 816 817 return status; 818 } 819 820 821 IMAPMailbox* 822 IMAPConnectionWorker::_MailboxFor(IMAPFolder& folder) 823 { 824 MailboxMap::iterator found = fMailboxes.find(&folder); 825 if (found == fMailboxes.end()) 826 return NULL; 827 828 IMAPMailbox* mailbox = found->second; 829 if (mailbox == NULL) { 830 mailbox = new IMAPMailbox(fProtocol, folder.MailboxName()); 831 folder.SetListener(mailbox); 832 found->second = mailbox; 833 } 834 return mailbox; 835 } 836 837 838 void 839 IMAPConnectionWorker::_SyncCommandDone() 840 { 841 fSyncPending--; 842 } 843 844 845 bool 846 IMAPConnectionWorker::_IsQuitPending() 847 { 848 BAutolock locker(fQueueLocker); 849 WorkerCommand* nextCommand = fPendingCommands.ItemAt(0); 850 return dynamic_cast<QuitCommand*>(nextCommand) != NULL; 851 } 852 853 854 status_t 855 IMAPConnectionWorker::_Connect() 856 { 857 if (fProtocol.IsConnected()) 858 return B_OK; 859 860 status_t status = B_INTERRUPTED; 861 int tries = 10; 862 while (tries-- > 0) { 863 if (_IsQuitPending()) 864 break; 865 status = fProtocol.Connect(fSettings.ServerAddress(), 866 fSettings.Username(), fSettings.Password(), fSettings.UseSSL()); 867 if (status == B_OK) 868 break; 869 870 // Wait for 1 second, and try again 871 snooze(1000000); 872 } 873 // TODO: if other workers are connected, but it fails for us, we need to 874 // remove this worker, and reduce the number of concurrent connections 875 if (status != B_OK) 876 return status; 877 878 //fIdle = fSettings.IdleMode() && fProtocol.Capabilities().Contains("IDLE"); 879 // TODO: Idle mode is not yet implemented! 880 fIdle = false; 881 return B_OK; 882 } 883 884 885 void 886 IMAPConnectionWorker::_Disconnect() 887 { 888 fProtocol.Disconnect(); 889 fSelectedBox = NULL; 890 } 891 892 893 /*static*/ status_t 894 IMAPConnectionWorker::_Worker(void* _self) 895 { 896 IMAPConnectionWorker* self = (IMAPConnectionWorker*)_self; 897 status_t status = self->_Worker(); 898 899 delete self; 900 return status; 901 } 902