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 std::vector<uint32> uidsToFetch; 381 382 // Determine how much we need to download 383 // TODO: also retrieve the header size, and only take the body 384 // size into account if it's below the limit -- that does not 385 // seem to be possible, though 386 for (size_t i = 0; i < entries.size(); i++) { 387 printf("%10" B_PRIu32 " %8" B_PRIu32 " bytes, flags: %#" 388 B_PRIx32 "\n", entries[i].uid, entries[i].size, 389 entries[i].flags); 390 fMailbox->AddMessageEntry(from + i, entries[i].uid, 391 entries[i].flags, entries[i].size); 392 393 if (entries[i].uid > fFolder->LastUID()) { 394 fTotalBytes += entries[i].size; 395 fUIDsToFetch.push_back(entries[i].uid); 396 } else { 397 fFolder->SyncMessageFlags(entries[i].uid, entries[i].flags); 398 } 399 } 400 401 fTotalEntries += fUIDsToFetch.size(); 402 fLastIndex = from - 1; 403 404 if (from == 1) { 405 fFolder->MessageEntriesFetched(); 406 407 if (fUIDsToFetch.size() > 0) { 408 // Add pending command to fetch the message headers 409 WorkerCommand* command = new FetchHeadersCommand(*fFolder, 410 *fMailbox, fUIDsToFetch, 411 WorkerPrivate(worker).BodyFetchLimit()); 412 if (!fFetchCommands.AddItem(command)) 413 delete command; 414 415 fUIDsToFetch.clear(); 416 } 417 fState = SELECT; 418 } 419 } 420 421 return B_OK; 422 } 423 424 virtual bool IsDone() const 425 { 426 return fState == DONE; 427 } 428 429 private: 430 enum State { 431 INIT, 432 SELECT, 433 FETCH_ENTRIES, 434 DONE 435 }; 436 437 IMAPConnectionWorker& fWorker; 438 BObjectList<IMAPFolder> fFolders; 439 State fState; 440 IMAPFolder* fFolder; 441 IMAPMailbox* fMailbox; 442 uint32 fFirstIndex; 443 uint32 fLastIndex; 444 uint64 fTotalEntries; 445 uint64 fTotalBytes; 446 WorkerCommandList fFetchCommands; 447 MessageUIDList fUIDsToFetch; 448 }; 449 450 451 class UpdateFlagsCommand : public WorkerCommand { 452 public: 453 UpdateFlagsCommand(IMAPFolder& folder, IMAPMailbox& mailbox, 454 MessageUIDList& entries, uint32 flags) 455 : 456 fFolder(folder), 457 fMailbox(mailbox), 458 fEntries(entries), 459 fFlags(flags) 460 { 461 } 462 463 virtual status_t Process(IMAPConnectionWorker& worker) 464 { 465 if (fEntries.empty()) 466 return B_OK; 467 468 fUID = *fEntries.begin(); 469 fEntries.erase(fEntries.begin()); 470 471 status_t status = WorkerPrivate(worker).SelectMailbox(fFolder); 472 if (status == B_OK) { 473 IMAP::Protocol& protocol = WorkerPrivate(worker).Protocol(); 474 IMAP::SetFlagsCommand set(fUID, fFlags); 475 status = protocol.ProcessCommand(set); 476 } 477 478 return status; 479 } 480 481 virtual bool IsDone() const 482 { 483 return fEntries.empty(); 484 } 485 486 private: 487 IMAPFolder& fFolder; 488 IMAPMailbox& fMailbox; 489 MessageUIDList fEntries; 490 uint32 fUID; 491 uint32 fFlags; 492 }; 493 494 495 struct CommandDelete 496 { 497 inline void operator()(WorkerCommand* command) 498 { 499 delete command; 500 } 501 }; 502 503 504 /*! An auto deleter similar to ObjectDeleter that calls SyncCommandDone() 505 for all SyncCommands. 506 */ 507 struct CommandDeleter : BPrivate::AutoDeleter<WorkerCommand, CommandDelete> 508 { 509 CommandDeleter(IMAPConnectionWorker& worker, WorkerCommand* command) 510 : 511 BPrivate::AutoDeleter<WorkerCommand, CommandDelete>(command), 512 fWorker(worker) 513 { 514 } 515 516 ~CommandDeleter() 517 { 518 if (dynamic_cast<SyncCommand*>(fObject) != NULL) 519 WorkerPrivate(fWorker).SyncCommandDone(); 520 } 521 522 private: 523 IMAPConnectionWorker& fWorker; 524 }; 525 526 527 // #pragma mark - 528 529 530 IMAPConnectionWorker::IMAPConnectionWorker(IMAPProtocol& owner, 531 const Settings& settings, bool main) 532 : 533 fOwner(owner), 534 fSettings(settings), 535 fPendingCommandsSemaphore(-1), 536 fIdleBox(NULL), 537 fMain(main), 538 fStopped(false) 539 { 540 fExistsHandler.SetListener(this); 541 fProtocol.AddHandler(fExistsHandler); 542 543 fExpungeHandler.SetListener(this); 544 fProtocol.AddHandler(fExpungeHandler); 545 } 546 547 548 IMAPConnectionWorker::~IMAPConnectionWorker() 549 { 550 puts("worker quit"); 551 delete_sem(fPendingCommandsSemaphore); 552 _Disconnect(); 553 } 554 555 556 bool 557 IMAPConnectionWorker::HasMailboxes() const 558 { 559 BAutolock locker(const_cast<IMAPConnectionWorker*>(this)->fLocker); 560 return !fMailboxes.empty(); 561 } 562 563 564 uint32 565 IMAPConnectionWorker::CountMailboxes() const 566 { 567 BAutolock locker(const_cast<IMAPConnectionWorker*>(this)->fLocker); 568 return fMailboxes.size(); 569 } 570 571 572 void 573 IMAPConnectionWorker::AddMailbox(IMAPFolder* folder) 574 { 575 BAutolock locker(fLocker); 576 577 fMailboxes.insert(std::make_pair(folder, (IMAPMailbox*)NULL)); 578 579 // Prefer to have the INBOX in idle mode over other mail boxes 580 if (fIdleBox == NULL || folder->MailboxName().ICompare("INBOX") == 0) 581 fIdleBox = folder; 582 } 583 584 585 void 586 IMAPConnectionWorker::RemoveAllMailboxes() 587 { 588 BAutolock locker(fLocker); 589 590 // Reset listeners, and delete the mailboxes 591 MailboxMap::iterator iterator = fMailboxes.begin(); 592 for (; iterator != fMailboxes.end(); iterator++) { 593 iterator->first->SetListener(NULL); 594 delete iterator->second; 595 } 596 597 fIdleBox = NULL; 598 fMailboxes.clear(); 599 } 600 601 602 status_t 603 IMAPConnectionWorker::Run() 604 { 605 fPendingCommandsSemaphore = create_sem(0, "imap pending commands"); 606 if (fPendingCommandsSemaphore < 0) 607 return fPendingCommandsSemaphore; 608 609 fThread = spawn_thread(&_Worker, "imap connection worker", 610 B_NORMAL_PRIORITY, this); 611 if (fThread < 0) 612 return fThread; 613 614 resume_thread(fThread); 615 return B_OK; 616 } 617 618 619 void 620 IMAPConnectionWorker::Quit() 621 { 622 printf("IMAP: worker %p: enqueue quit\n", this); 623 _EnqueueCommand(new QuitCommand()); 624 } 625 626 627 status_t 628 IMAPConnectionWorker::EnqueueCheckSubscribedFolders() 629 { 630 printf("IMAP: worker %p: enqueue check subscribed folders\n", this); 631 return _EnqueueCommand(new CheckSubscribedFoldersCommand()); 632 } 633 634 635 status_t 636 IMAPConnectionWorker::EnqueueCheckMailboxes() 637 { 638 // Do not schedule checking mailboxes again if we're still working on 639 // those. 640 if (fSyncPending > 0) 641 return B_OK; 642 643 printf("IMAP: worker %p: enqueue check mailboxes\n", this); 644 return _EnqueueCommand(new CheckMailboxesCommand(*this)); 645 } 646 647 648 status_t 649 IMAPConnectionWorker::EnqueueFetchBody(IMAPFolder& folder, uint32 uid, 650 const BMessenger& replyTo) 651 { 652 IMAPMailbox* mailbox = _MailboxFor(folder); 653 if (mailbox == NULL) 654 return B_ENTRY_NOT_FOUND; 655 656 std::vector<uint32> uids; 657 uids.push_back(uid); 658 659 return _EnqueueCommand(new FetchBodiesCommand(folder, *mailbox, uids, 660 &replyTo)); 661 } 662 663 664 status_t 665 IMAPConnectionWorker::EnqueueUpdateFlags(IMAPFolder& folder, uint32 uid, 666 uint32 flags) 667 { 668 IMAPMailbox* mailbox = _MailboxFor(folder); 669 if (mailbox == NULL) 670 return B_ENTRY_NOT_FOUND; 671 672 std::vector<uint32> uids; 673 uids.push_back(uid); 674 675 return _EnqueueCommand(new UpdateFlagsCommand(folder, *mailbox, uids, 676 flags)); 677 } 678 679 680 // #pragma mark - Handler listener 681 682 683 void 684 IMAPConnectionWorker::MessageExistsReceived(uint32 count) 685 { 686 printf("Message exists: %" B_PRIu32 "\n", count); 687 fMessagesExist = count; 688 689 // TODO: We might want to trigger another check even during sync 690 // (but only one), if this isn't the result of a SELECT 691 EnqueueCheckMailboxes(); 692 } 693 694 695 void 696 IMAPConnectionWorker::MessageExpungeReceived(uint32 index) 697 { 698 printf("Message expunge: %" B_PRIu32 "\n", index); 699 if (fSelectedBox == NULL) 700 return; 701 702 BAutolock locker(fLocker); 703 704 IMAPMailbox* mailbox = _MailboxFor(*fSelectedBox); 705 if (mailbox != NULL) { 706 mailbox->RemoveMessageEntry(index); 707 // TODO: remove message from folder 708 } 709 } 710 711 712 // #pragma mark - private 713 714 715 status_t 716 IMAPConnectionWorker::_Worker() 717 { 718 while (!fStopped) { 719 BAutolock locker(fLocker); 720 721 if (fPendingCommands.IsEmpty()) { 722 if (!fIdle) 723 _Disconnect(); 724 locker.Unlock(); 725 726 // TODO: in idle mode, we'd need to parse any incoming message here 727 _WaitForCommands(); 728 continue; 729 } 730 731 WorkerCommand* command = fPendingCommands.RemoveItemAt(0); 732 if (command == NULL) 733 continue; 734 735 CommandDeleter deleter(*this, command); 736 737 status_t status = _Connect(); 738 if (status != B_OK) 739 return status; 740 741 status = command->Process(*this); 742 if (status != B_OK) 743 return status; 744 745 if (!command->IsDone()) { 746 deleter.Detach(); 747 command->SetContinuation(); 748 _EnqueueCommand(command); 749 } 750 } 751 752 fOwner.WorkerQuit(this); 753 return B_OK; 754 } 755 756 757 /*! Enqueues the given command to the worker queue. This method will take 758 over ownership of the given command even in the error case. 759 */ 760 status_t 761 IMAPConnectionWorker::_EnqueueCommand(WorkerCommand* command) 762 { 763 BAutolock locker(fLocker); 764 765 if (!fPendingCommands.AddItem(command)) { 766 delete command; 767 return B_NO_MEMORY; 768 } 769 770 if (dynamic_cast<SyncCommand*>(command) != NULL 771 && !command->IsContinuation()) 772 fSyncPending++; 773 774 locker.Unlock(); 775 release_sem(fPendingCommandsSemaphore); 776 return B_OK; 777 } 778 779 780 void 781 IMAPConnectionWorker::_WaitForCommands() 782 { 783 int32 count = 1; 784 get_sem_count(fPendingCommandsSemaphore, &count); 785 if (count < 1) 786 count = 1; 787 788 while (acquire_sem_etc(fPendingCommandsSemaphore, count, 0, 789 B_INFINITE_TIMEOUT) == B_INTERRUPTED); 790 } 791 792 793 status_t 794 IMAPConnectionWorker::_SelectMailbox(IMAPFolder& folder, uint32* _nextUID) 795 { 796 if (fSelectedBox == &folder && _nextUID == NULL) 797 return B_OK; 798 799 IMAP::SelectCommand select(folder.MailboxName().String()); 800 801 status_t status = fProtocol.ProcessCommand(select); 802 if (status == B_OK) { 803 folder.SetUIDValidity(select.UIDValidity()); 804 if (_nextUID != NULL) 805 *_nextUID = select.NextUID(); 806 fSelectedBox = &folder; 807 } 808 809 return status; 810 } 811 812 813 IMAPMailbox* 814 IMAPConnectionWorker::_MailboxFor(IMAPFolder& folder) 815 { 816 MailboxMap::iterator found = fMailboxes.find(&folder); 817 if (found == fMailboxes.end()) 818 return NULL; 819 820 IMAPMailbox* mailbox = found->second; 821 if (mailbox == NULL) { 822 mailbox = new IMAPMailbox(fProtocol, folder.MailboxName()); 823 folder.SetListener(mailbox); 824 found->second = mailbox; 825 } 826 return mailbox; 827 } 828 829 830 void 831 IMAPConnectionWorker::_SyncCommandDone() 832 { 833 fSyncPending--; 834 } 835 836 837 status_t 838 IMAPConnectionWorker::_Connect() 839 { 840 if (fProtocol.IsConnected()) 841 return B_OK; 842 843 status_t status; 844 int tries = 6; 845 while (tries-- > 0) { 846 status = fProtocol.Connect(fSettings.ServerAddress(), 847 fSettings.Username(), fSettings.Password(), fSettings.UseSSL()); 848 if (status == B_OK) 849 break; 850 851 // Wait for 10 seconds, and try again 852 snooze(10000000); 853 } 854 // TODO: if other workers are connected, but it fails for us, we need to 855 // remove this worker, and reduce the number of concurrent connections 856 if (status != B_OK) 857 return status; 858 859 //fIdle = fSettings.IdleMode() && fProtocol.Capabilities().Contains("IDLE"); 860 // TODO: Idle mode is not yet implemented! 861 fIdle = false; 862 return B_OK; 863 } 864 865 866 void 867 IMAPConnectionWorker::_Disconnect() 868 { 869 fProtocol.Disconnect(); 870 } 871 872 873 /*static*/ status_t 874 IMAPConnectionWorker::_Worker(void* _self) 875 { 876 IMAPConnectionWorker* self = (IMAPConnectionWorker*)_self; 877 status_t status = self->_Worker(); 878 879 delete self; 880 return status; 881 } 882