1186c96d5SAxel Dörfler /* 2*4fe2002bSAxel Dörfler * Copyright 2013-2015, Axel Dörfler, axeld@pinc-software.de. 3186c96d5SAxel Dörfler * Distributed under the terms of the MIT License. 4186c96d5SAxel Dörfler */ 5186c96d5SAxel Dörfler 6186c96d5SAxel Dörfler 7186c96d5SAxel Dörfler #include "IMAPProtocol.h" 8186c96d5SAxel Dörfler 9186c96d5SAxel Dörfler #include <Directory.h> 10186c96d5SAxel Dörfler 11186c96d5SAxel Dörfler #include "IMAPConnectionWorker.h" 12a4bdd26dSAxel Dörfler #include "IMAPFolder.h" 13a4bdd26dSAxel Dörfler #include "Utilities.h" 14186c96d5SAxel Dörfler 15186c96d5SAxel Dörfler 16186c96d5SAxel Dörfler IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings) 17186c96d5SAxel Dörfler : 18186c96d5SAxel Dörfler BInboundMailProtocol(settings), 1928ee6c28SAxel Dörfler fSettings(settings.Name(), settings.InboundSettings()), 207993ddfaSAxel Dörfler fWorkers(5, false) 21186c96d5SAxel Dörfler { 22186c96d5SAxel Dörfler BPath destination = fSettings.Destination(); 23186c96d5SAxel Dörfler 24186c96d5SAxel Dörfler status_t status = create_directory(destination.Path(), 0755); 25186c96d5SAxel Dörfler if (status != B_OK) { 2628ee6c28SAxel Dörfler fprintf(stderr, "IMAP: Could not create destination directory %s: %s\n", 27186c96d5SAxel Dörfler destination.Path(), strerror(status)); 28186c96d5SAxel Dörfler } 29186c96d5SAxel Dörfler 30186c96d5SAxel Dörfler PostMessage(B_READY_TO_RUN); 31186c96d5SAxel Dörfler } 32186c96d5SAxel Dörfler 33186c96d5SAxel Dörfler 34186c96d5SAxel Dörfler IMAPProtocol::~IMAPProtocol() 35186c96d5SAxel Dörfler { 36186c96d5SAxel Dörfler } 37186c96d5SAxel Dörfler 38186c96d5SAxel Dörfler 39186c96d5SAxel Dörfler status_t 407993ddfaSAxel Dörfler IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle) 41adbe8fc9SAxel Dörfler { 42adbe8fc9SAxel Dörfler // Get list of subscribed folders 43adbe8fc9SAxel Dörfler 44a4bdd26dSAxel Dörfler StringList newFolders; 45a4bdd26dSAxel Dörfler BString separator; 46a4bdd26dSAxel Dörfler status_t status = protocol.GetSubscribedFolders(newFolders, separator); 47adbe8fc9SAxel Dörfler if (status != B_OK) 48adbe8fc9SAxel Dörfler return status; 49adbe8fc9SAxel Dörfler 50adbe8fc9SAxel Dörfler // Determine how many new mailboxes we have 51adbe8fc9SAxel Dörfler 52a4bdd26dSAxel Dörfler StringList::iterator folderIterator = newFolders.begin(); 53a4bdd26dSAxel Dörfler while (folderIterator != newFolders.end()) { 54a4bdd26dSAxel Dörfler if (fFolders.find(*folderIterator) != fFolders.end()) 55a4bdd26dSAxel Dörfler folderIterator = newFolders.erase(folderIterator); 56a4bdd26dSAxel Dörfler else 57a4bdd26dSAxel Dörfler folderIterator++; 58adbe8fc9SAxel Dörfler } 59adbe8fc9SAxel Dörfler 60a4bdd26dSAxel Dörfler int32 totalMailboxes = fFolders.size() + newFolders.size(); 61a4bdd26dSAxel Dörfler int32 workersWanted = 1; 627993ddfaSAxel Dörfler if (idle) 63a4bdd26dSAxel Dörfler workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes); 64adbe8fc9SAxel Dörfler 65a4bdd26dSAxel Dörfler if (newFolders.empty() && fWorkers.CountItems() == workersWanted) { 66a4bdd26dSAxel Dörfler // Nothing to do - we've already distributed everything 67a4bdd26dSAxel Dörfler return B_OK; 68a4bdd26dSAxel Dörfler } 69adbe8fc9SAxel Dörfler 70a4bdd26dSAxel Dörfler // Remove mailboxes from workers 71a4bdd26dSAxel Dörfler for (int32 i = 0; i < fWorkers.CountItems(); i++) { 72a4bdd26dSAxel Dörfler fWorkers.ItemAt(i)->RemoveAllMailboxes(); 73a4bdd26dSAxel Dörfler } 74a4bdd26dSAxel Dörfler 75a4bdd26dSAxel Dörfler // Create/remove connection workers as allowed and needed 76a4bdd26dSAxel Dörfler while (fWorkers.CountItems() < workersWanted) { 77adbe8fc9SAxel Dörfler IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this, 78adbe8fc9SAxel Dörfler fSettings); 79adbe8fc9SAxel Dörfler if (!fWorkers.AddItem(worker)) { 80adbe8fc9SAxel Dörfler delete worker; 81adbe8fc9SAxel Dörfler break; 82adbe8fc9SAxel Dörfler } 83adbe8fc9SAxel Dörfler 847993ddfaSAxel Dörfler status = worker->Run(); 857993ddfaSAxel Dörfler if (status != B_OK) { 867993ddfaSAxel Dörfler fWorkers.RemoveItem(worker); 877993ddfaSAxel Dörfler delete worker; 88adbe8fc9SAxel Dörfler } 897993ddfaSAxel Dörfler } 907993ddfaSAxel Dörfler 91a4bdd26dSAxel Dörfler while (fWorkers.CountItems() > workersWanted) { 92a4bdd26dSAxel Dörfler IMAPConnectionWorker* worker 93a4bdd26dSAxel Dörfler = fWorkers.RemoveItemAt(fWorkers.CountItems() - 1); 94a4bdd26dSAxel Dörfler worker->Quit(); 95adbe8fc9SAxel Dörfler } 96adbe8fc9SAxel Dörfler 97a4bdd26dSAxel Dörfler // Update known mailboxes 98a4bdd26dSAxel Dörfler folderIterator = newFolders.begin(); 99a4bdd26dSAxel Dörfler for (; folderIterator != newFolders.end(); folderIterator++) { 100a4bdd26dSAxel Dörfler const BString& mailbox = *folderIterator; 101a4bdd26dSAxel Dörfler fFolders.insert(std::make_pair(mailbox, 102a4bdd26dSAxel Dörfler _CreateFolder(mailbox, separator))); 103a4bdd26dSAxel Dörfler } 104adbe8fc9SAxel Dörfler 105a4bdd26dSAxel Dörfler // Distribute the mailboxes evenly to the workers 106a4bdd26dSAxel Dörfler FolderMap::iterator iterator = fFolders.begin(); 107adbe8fc9SAxel Dörfler int32 index = 0; 108a4bdd26dSAxel Dörfler for (; iterator != fFolders.end(); iterator++) { 109a4bdd26dSAxel Dörfler fWorkers.ItemAt(index)->AddMailbox(iterator->second); 110adbe8fc9SAxel Dörfler index = (index + 1) % fWorkers.CountItems(); 111adbe8fc9SAxel Dörfler } 112adbe8fc9SAxel Dörfler 1137993ddfaSAxel Dörfler // Start waiting workers 1144b2c5571SAxel Dörfler return _EnqueueCheckMailboxes(); 115adbe8fc9SAxel Dörfler } 116adbe8fc9SAxel Dörfler 117adbe8fc9SAxel Dörfler 118229c7773SAxel Dörfler void 119229c7773SAxel Dörfler IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker) 120229c7773SAxel Dörfler { 121229c7773SAxel Dörfler fWorkers.RemoveItem(worker); 122229c7773SAxel Dörfler } 123229c7773SAxel Dörfler 124229c7773SAxel Dörfler 125eba458b9SAxel Dörfler void 1261052525dSAxel Dörfler IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream, 127d33e4744SAxel Dörfler uint32 fetchFlags, BMessage& attributes) 128eba458b9SAxel Dörfler { 129d33e4744SAxel Dörfler if ((fetchFlags & (IMAP::kFetchHeader | IMAP::kFetchBody)) 130*4fe2002bSAxel Dörfler == (IMAP::kFetchHeader | IMAP::kFetchBody)) { 131d33e4744SAxel Dörfler ProcessMessageFetched(ref, stream, attributes); 132d33e4744SAxel Dörfler } else if ((fetchFlags & IMAP::kFetchHeader) != 0) { 133d33e4744SAxel Dörfler ProcessHeaderFetched(ref, stream, attributes); 134d33e4744SAxel Dörfler } else if ((fetchFlags & IMAP::kFetchBody) != 0) { 135d33e4744SAxel Dörfler NotifyBodyFetched(ref, stream, attributes); 136549949b2SAxel Dörfler } 137eba458b9SAxel Dörfler } 138eba458b9SAxel Dörfler 139eba458b9SAxel Dörfler 140adbe8fc9SAxel Dörfler status_t 141186c96d5SAxel Dörfler IMAPProtocol::SyncMessages() 142186c96d5SAxel Dörfler { 143186c96d5SAxel Dörfler puts("IMAP: sync"); 144adbe8fc9SAxel Dörfler 145adbe8fc9SAxel Dörfler if (fWorkers.IsEmpty()) { 146adbe8fc9SAxel Dörfler // Create main (and possibly initial) connection worker 147adbe8fc9SAxel Dörfler IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this, 148adbe8fc9SAxel Dörfler fSettings, true); 149adbe8fc9SAxel Dörfler if (!fWorkers.AddItem(worker)) { 150adbe8fc9SAxel Dörfler delete worker; 151adbe8fc9SAxel Dörfler return B_NO_MEMORY; 152adbe8fc9SAxel Dörfler } 153adbe8fc9SAxel Dörfler 154229c7773SAxel Dörfler worker->EnqueueCheckSubscribedFolders(); 155a4bdd26dSAxel Dörfler return worker->Run(); 156adbe8fc9SAxel Dörfler } 157adbe8fc9SAxel Dörfler 1584b2c5571SAxel Dörfler return _EnqueueCheckMailboxes(); 159186c96d5SAxel Dörfler } 160186c96d5SAxel Dörfler 161186c96d5SAxel Dörfler 162186c96d5SAxel Dörfler status_t 163186c96d5SAxel Dörfler IMAPProtocol::FetchBody(const entry_ref& ref) 164186c96d5SAxel Dörfler { 165186c96d5SAxel Dörfler printf("IMAP: fetch body %s\n", ref.name); 166186c96d5SAxel Dörfler return B_ERROR; 167186c96d5SAxel Dörfler } 168186c96d5SAxel Dörfler 169186c96d5SAxel Dörfler 170186c96d5SAxel Dörfler status_t 171186c96d5SAxel Dörfler IMAPProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flags) 172186c96d5SAxel Dörfler { 173186c96d5SAxel Dörfler printf("IMAP: mark as read %s: %d\n", ref.name, flags); 174186c96d5SAxel Dörfler return B_ERROR; 175186c96d5SAxel Dörfler } 176186c96d5SAxel Dörfler 177186c96d5SAxel Dörfler 178186c96d5SAxel Dörfler status_t 179186c96d5SAxel Dörfler IMAPProtocol::DeleteMessage(const entry_ref& ref) 180186c96d5SAxel Dörfler { 181186c96d5SAxel Dörfler printf("IMAP: delete message %s\n", ref.name); 182186c96d5SAxel Dörfler return B_ERROR; 183186c96d5SAxel Dörfler } 184186c96d5SAxel Dörfler 185186c96d5SAxel Dörfler 186186c96d5SAxel Dörfler status_t 187186c96d5SAxel Dörfler IMAPProtocol::AppendMessage(const entry_ref& ref) 188186c96d5SAxel Dörfler { 189186c96d5SAxel Dörfler printf("IMAP: append message %s\n", ref.name); 190186c96d5SAxel Dörfler return B_ERROR; 191186c96d5SAxel Dörfler } 192186c96d5SAxel Dörfler 193186c96d5SAxel Dörfler 194186c96d5SAxel Dörfler void 195186c96d5SAxel Dörfler IMAPProtocol::MessageReceived(BMessage* message) 196186c96d5SAxel Dörfler { 197186c96d5SAxel Dörfler switch (message->what) { 198186c96d5SAxel Dörfler case B_READY_TO_RUN: 199186c96d5SAxel Dörfler ReadyToRun(); 200186c96d5SAxel Dörfler break; 201adbe8fc9SAxel Dörfler 202adbe8fc9SAxel Dörfler default: 203adbe8fc9SAxel Dörfler BInboundMailProtocol::MessageReceived(message); 204adbe8fc9SAxel Dörfler break; 205186c96d5SAxel Dörfler } 206186c96d5SAxel Dörfler } 207186c96d5SAxel Dörfler 208186c96d5SAxel Dörfler 209186c96d5SAxel Dörfler void 210186c96d5SAxel Dörfler IMAPProtocol::ReadyToRun() 211186c96d5SAxel Dörfler { 212adbe8fc9SAxel Dörfler puts("IMAP: ready to run!"); 213adbe8fc9SAxel Dörfler if (fSettings.IdleMode()) 214adbe8fc9SAxel Dörfler SyncMessages(); 215186c96d5SAxel Dörfler } 216186c96d5SAxel Dörfler 217186c96d5SAxel Dörfler 218a4bdd26dSAxel Dörfler IMAPFolder* 219a4bdd26dSAxel Dörfler IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator) 220a4bdd26dSAxel Dörfler { 221a4bdd26dSAxel Dörfler BString name = MailboxToFolderName(mailbox, separator); 222a4bdd26dSAxel Dörfler 223a4bdd26dSAxel Dörfler BPath path(fSettings.Destination()); 224a4bdd26dSAxel Dörfler if (path.Append(name.String()) != B_OK) { 225a4bdd26dSAxel Dörfler fprintf(stderr, "Could not append path: %s\n", name.String()); 226a4bdd26dSAxel Dörfler return NULL; 227a4bdd26dSAxel Dörfler } 228a4bdd26dSAxel Dörfler 229a4bdd26dSAxel Dörfler status_t status = create_directory(path.Path(), 0755); 230a4bdd26dSAxel Dörfler if (status != B_OK) { 231a4bdd26dSAxel Dörfler fprintf(stderr, "Could not create path %s: %s\n", path.Path(), 232a4bdd26dSAxel Dörfler strerror(status)); 233a4bdd26dSAxel Dörfler return NULL; 234a4bdd26dSAxel Dörfler } 235a4bdd26dSAxel Dörfler 236a4bdd26dSAxel Dörfler entry_ref ref; 237a4bdd26dSAxel Dörfler status = get_ref_for_path(path.Path(), &ref); 238a4bdd26dSAxel Dörfler if (status != B_OK) { 239a4bdd26dSAxel Dörfler fprintf(stderr, "Could not get ref for %s: %s\n", path.Path(), 240a4bdd26dSAxel Dörfler strerror(status)); 241a4bdd26dSAxel Dörfler return NULL; 242a4bdd26dSAxel Dörfler } 243a4bdd26dSAxel Dörfler 244eba458b9SAxel Dörfler return new IMAPFolder(*this, mailbox, ref); 245a4bdd26dSAxel Dörfler } 246a4bdd26dSAxel Dörfler 247a4bdd26dSAxel Dörfler 2484b2c5571SAxel Dörfler status_t 2494b2c5571SAxel Dörfler IMAPProtocol::_EnqueueCheckMailboxes() 2504b2c5571SAxel Dörfler { 2514b2c5571SAxel Dörfler for (int32 i = 0; i < fWorkers.CountItems(); i++) { 2524b2c5571SAxel Dörfler fWorkers.ItemAt(i)->EnqueueCheckMailboxes(); 2534b2c5571SAxel Dörfler } 2544b2c5571SAxel Dörfler 2554b2c5571SAxel Dörfler return B_OK; 2564b2c5571SAxel Dörfler } 2574b2c5571SAxel Dörfler 2584b2c5571SAxel Dörfler 259186c96d5SAxel Dörfler // #pragma mark - 260186c96d5SAxel Dörfler 261186c96d5SAxel Dörfler 262186c96d5SAxel Dörfler extern "C" BInboundMailProtocol* 263186c96d5SAxel Dörfler instantiate_inbound_protocol(const BMailAccountSettings& settings) 264186c96d5SAxel Dörfler { 265186c96d5SAxel Dörfler return new IMAPProtocol(settings); 266186c96d5SAxel Dörfler } 267