1 /* 2 * Copyright 2013, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "IMAPProtocol.h" 8 9 #include <Directory.h> 10 11 #include "IMAPConnectionWorker.h" 12 #include "IMAPFolder.h" 13 #include "Utilities.h" 14 15 16 IMAPProtocol::IMAPProtocol(const BMailAccountSettings& settings) 17 : 18 BInboundMailProtocol(settings), 19 fSettings(settings.Name(), settings.InboundSettings()), 20 fWorkers(5, false) 21 { 22 BPath destination = fSettings.Destination(); 23 24 status_t status = create_directory(destination.Path(), 0755); 25 if (status != B_OK) { 26 fprintf(stderr, "IMAP: Could not create destination directory %s: %s\n", 27 destination.Path(), strerror(status)); 28 } 29 30 PostMessage(B_READY_TO_RUN); 31 } 32 33 34 IMAPProtocol::~IMAPProtocol() 35 { 36 } 37 38 39 status_t 40 IMAPProtocol::CheckSubscribedFolders(IMAP::Protocol& protocol, bool idle) 41 { 42 // Get list of subscribed folders 43 44 StringList newFolders; 45 BString separator; 46 status_t status = protocol.GetSubscribedFolders(newFolders, separator); 47 if (status != B_OK) 48 return status; 49 50 // Determine how many new mailboxes we have 51 52 StringList::iterator folderIterator = newFolders.begin(); 53 while (folderIterator != newFolders.end()) { 54 if (fFolders.find(*folderIterator) != fFolders.end()) 55 folderIterator = newFolders.erase(folderIterator); 56 else 57 folderIterator++; 58 } 59 60 int32 totalMailboxes = fFolders.size() + newFolders.size(); 61 int32 workersWanted = 1; 62 if (idle) 63 workersWanted = std::min(fSettings.MaxConnections(), totalMailboxes); 64 65 if (newFolders.empty() && fWorkers.CountItems() == workersWanted) { 66 // Nothing to do - we've already distributed everything 67 return B_OK; 68 } 69 70 // Remove mailboxes from workers 71 for (int32 i = 0; i < fWorkers.CountItems(); i++) { 72 fWorkers.ItemAt(i)->RemoveAllMailboxes(); 73 } 74 75 // Create/remove connection workers as allowed and needed 76 while (fWorkers.CountItems() < workersWanted) { 77 IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this, 78 fSettings); 79 if (!fWorkers.AddItem(worker)) { 80 delete worker; 81 break; 82 } 83 84 status = worker->Run(); 85 if (status != B_OK) { 86 fWorkers.RemoveItem(worker); 87 delete worker; 88 } 89 } 90 91 while (fWorkers.CountItems() > workersWanted) { 92 IMAPConnectionWorker* worker 93 = fWorkers.RemoveItemAt(fWorkers.CountItems() - 1); 94 worker->Quit(); 95 } 96 97 // Update known mailboxes 98 folderIterator = newFolders.begin(); 99 for (; folderIterator != newFolders.end(); folderIterator++) { 100 const BString& mailbox = *folderIterator; 101 fFolders.insert(std::make_pair(mailbox, 102 _CreateFolder(mailbox, separator))); 103 } 104 105 // Distribute the mailboxes evenly to the workers 106 FolderMap::iterator iterator = fFolders.begin(); 107 int32 index = 0; 108 for (; iterator != fFolders.end(); iterator++) { 109 fWorkers.ItemAt(index)->AddMailbox(iterator->second); 110 index = (index + 1) % fWorkers.CountItems(); 111 } 112 113 // Start waiting workers 114 return _EnqueueCheckMailboxes(); 115 } 116 117 118 void 119 IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker) 120 { 121 fWorkers.RemoveItem(worker); 122 } 123 124 125 void 126 IMAPProtocol::MessageStored(IMAPFolder& folder, entry_ref& ref, BFile& stream, 127 uint32 fetchFlags, BMessage& attributes) 128 { 129 if ((fetchFlags & (IMAP::kFetchHeader | IMAP::kFetchBody)) 130 == IMAP::kFetchHeader | IMAP::kFetchBody) { 131 ProcessMessageFetched(ref, stream, attributes); 132 } else if ((fetchFlags & IMAP::kFetchHeader) != 0) { 133 ProcessHeaderFetched(ref, stream, attributes); 134 } else if ((fetchFlags & IMAP::kFetchBody) != 0) { 135 NotifyBodyFetched(ref, stream, attributes); 136 } 137 } 138 139 140 status_t 141 IMAPProtocol::SyncMessages() 142 { 143 puts("IMAP: sync"); 144 145 if (fWorkers.IsEmpty()) { 146 // Create main (and possibly initial) connection worker 147 IMAPConnectionWorker* worker = new IMAPConnectionWorker(*this, 148 fSettings, true); 149 if (!fWorkers.AddItem(worker)) { 150 delete worker; 151 return B_NO_MEMORY; 152 } 153 154 worker->EnqueueCheckSubscribedFolders(); 155 return worker->Run(); 156 } 157 158 return _EnqueueCheckMailboxes(); 159 } 160 161 162 status_t 163 IMAPProtocol::FetchBody(const entry_ref& ref) 164 { 165 printf("IMAP: fetch body %s\n", ref.name); 166 return B_ERROR; 167 } 168 169 170 status_t 171 IMAPProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flags) 172 { 173 printf("IMAP: mark as read %s: %d\n", ref.name, flags); 174 return B_ERROR; 175 } 176 177 178 status_t 179 IMAPProtocol::DeleteMessage(const entry_ref& ref) 180 { 181 printf("IMAP: delete message %s\n", ref.name); 182 return B_ERROR; 183 } 184 185 186 status_t 187 IMAPProtocol::AppendMessage(const entry_ref& ref) 188 { 189 printf("IMAP: append message %s\n", ref.name); 190 return B_ERROR; 191 } 192 193 194 void 195 IMAPProtocol::MessageReceived(BMessage* message) 196 { 197 switch (message->what) { 198 case B_READY_TO_RUN: 199 ReadyToRun(); 200 break; 201 202 default: 203 BInboundMailProtocol::MessageReceived(message); 204 break; 205 } 206 } 207 208 209 void 210 IMAPProtocol::ReadyToRun() 211 { 212 puts("IMAP: ready to run!"); 213 if (fSettings.IdleMode()) 214 SyncMessages(); 215 } 216 217 218 IMAPFolder* 219 IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator) 220 { 221 BString name = MailboxToFolderName(mailbox, separator); 222 223 BPath path(fSettings.Destination()); 224 if (path.Append(name.String()) != B_OK) { 225 fprintf(stderr, "Could not append path: %s\n", name.String()); 226 return NULL; 227 } 228 229 status_t status = create_directory(path.Path(), 0755); 230 if (status != B_OK) { 231 fprintf(stderr, "Could not create path %s: %s\n", path.Path(), 232 strerror(status)); 233 return NULL; 234 } 235 236 entry_ref ref; 237 status = get_ref_for_path(path.Path(), &ref); 238 if (status != B_OK) { 239 fprintf(stderr, "Could not get ref for %s: %s\n", path.Path(), 240 strerror(status)); 241 return NULL; 242 } 243 244 return new IMAPFolder(*this, mailbox, ref); 245 } 246 247 248 status_t 249 IMAPProtocol::_EnqueueCheckMailboxes() 250 { 251 for (int32 i = 0; i < fWorkers.CountItems(); i++) { 252 fWorkers.ItemAt(i)->EnqueueCheckMailboxes(); 253 } 254 255 return B_OK; 256 } 257 258 259 // #pragma mark - 260 261 262 extern "C" BInboundMailProtocol* 263 instantiate_inbound_protocol(const BMailAccountSettings& settings) 264 { 265 return new IMAPProtocol(settings); 266 } 267