1/* 2 * Copyright 2011, Haiku, Inc. All rights reserved. 3 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de> 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8#include "IMAPMailbox.h" 9 10#include "IMAPHandler.h" 11#include "IMAPStorage.h" 12 13 14#define DEBUG_IMAP_MAILBOX 15#ifdef DEBUG_IMAP_MAILBOX 16# include <stdio.h> 17# define TRACE(x...) printf(x) 18#else 19# define TRACE(x...) ; 20#endif 21 22 23MinMessage::MinMessage() 24{ 25 uid = 0; 26 flags = 0; 27} 28 29 30// #pragma mark - 31 32 33IMAPMailbox::IMAPMailbox(IMAPStorage& storage) 34 : 35 fStorage(storage), 36 37 fMailboxSelectHandler(*this), 38 fExistsHandler(*this), 39 fExpungeHandler(*this), 40 fFlagsHandler(*this), 41 42 fWatching(0), 43 44 fFetchBodyLimit(0) 45{ 46 fHandlerList.AddItem(&fCapabilityHandler); 47 fIMAPMailboxListener = &fNULLListener; 48} 49 50 51IMAPMailbox::~IMAPMailbox() 52{ 53 Disconnect(); 54} 55 56 57void 58IMAPMailbox::SetListener(IMAPMailboxListener* listener) 59{ 60 if (listener == NULL) 61 fIMAPMailboxListener = &fNULLListener; 62 else 63 fIMAPMailboxListener = listener; 64} 65 66 67status_t 68IMAPMailbox::SelectMailbox(const char* mailbox) 69{ 70 TRACE("SELECT %s\n", mailbox); 71 fMailboxSelectHandler.SetTo(mailbox); 72 status_t status = ProcessCommand(&fMailboxSelectHandler); 73 if (status != B_OK) 74 return status; 75 fSelectedMailbox = mailbox; 76 77 if (fCapabilityHandler.Capabilities() != "") 78 return status; 79 //else get them 80 status = ProcessCommand(fCapabilityHandler.Command()); 81 if (status != B_OK) 82 return status; 83 84 return B_OK; 85} 86 87 88BString 89IMAPMailbox::Mailbox() 90{ 91 return fSelectedMailbox; 92} 93 94 95status_t 96IMAPMailbox::Sync() 97{ 98 TRACE("Sync\n"); 99 if (fMailboxSelectHandler.NextUID() <= 1) 100 return B_OK; 101 102 _InstallUnsolicitedHandler(false); 103 104 fMessageList.clear(); 105 FetchMessageListCommand fetchMessageListCommand(*this, &fMessageList, 106 fMailboxSelectHandler.NextUID()); 107 status_t status = ProcessCommand(&fetchMessageListCommand); 108 if (status != B_OK) 109 return status; 110 111 _InstallUnsolicitedHandler(true); 112 return B_OK; 113} 114 115 116bool 117IMAPMailbox::SupportWatching() 118{ 119 if (fCapabilityHandler.Capabilities().FindFirst("IDLE") < 0) 120 return false; 121 return true; 122} 123 124 125status_t 126IMAPMailbox::StartWatchingMailbox(sem_id startedSem) 127{ 128 atomic_set(&fWatching, 1); 129 130 bool firstIDLE = true; 131 // refresh every 29 min 132 bigtime_t timeout = 1000 * 1000 * 60 * 29; // 29 min 133 status_t status; 134 while (true) { 135 int32 commandID = NextCommandID(); 136 TRACE("IDLE ...\n"); 137 status = SendCommand("IDLE", commandID); 138 if (firstIDLE) { 139 release_sem(startedSem); 140 firstIDLE = false; 141 } 142 if (status != B_OK) 143 break; 144 145 status = HandleResponse(commandID, timeout, false); 146 ProcessAfterQuacks(kIMAP4ClientTimeout); 147 148 if (atomic_get(&fWatching) == 0) 149 break; 150 151 if (status == B_TIMED_OUT) { 152 TRACE("Renew IDLE connection.\n"); 153 status = SendRawCommand("DONE"); 154 if (status != B_OK) 155 break; 156 // handle IDLE response and more 157 status = ProcessCommand("NOOP"); 158 if (status != B_OK) 159 break; 160 else 161 continue; 162 } 163 if (status != B_OK) 164 break; 165 } 166 167 atomic_set(&fWatching, 0); 168 return status; 169} 170 171 172 173status_t 174IMAPMailbox::StopWatchingMailbox() 175{ 176 if (atomic_get(&fWatching) == 0) 177 return B_OK; 178 atomic_set(&fWatching, 0); 179 return SendRawCommand("DONE"); 180} 181 182 183status_t 184IMAPMailbox::CheckMailbox() 185{ 186 return ProcessCommand("NOOP"); 187} 188 189 190status_t 191IMAPMailbox::FetchMinMessage(int32 messageNumber, BPositionIO** data) 192{ 193 if (messageNumber <= 0) 194 return B_BAD_VALUE; 195 FetchMinMessageCommand fetchMinMessageCommand(*this, messageNumber, 196 &fMessageList, data); 197 return ProcessCommand(&fetchMinMessageCommand); 198} 199 200 201status_t 202IMAPMailbox::FetchBody(int32 messageNumber, BPositionIO* data) 203{ 204 if (data == NULL || messageNumber <= 0) { 205 delete data; 206 return B_BAD_VALUE; 207 } 208 209 FetchBodyCommand fetchBodyCommand(*this, messageNumber, data); 210 status_t status = ProcessCommand(&fetchBodyCommand); 211 212 return status; 213} 214 215 216void 217IMAPMailbox::SetFetchBodyLimit(int32 limit) 218{ 219 fFetchBodyLimit = limit; 220} 221 222 223status_t 224IMAPMailbox::FetchMessage(int32 messageNumber) 225{ 226 return FetchMessages(messageNumber, -1); 227} 228 229 230status_t 231IMAPMailbox::FetchMessages(int32 firstMessage, int32 lastMessage) 232{ 233 FetchMessageCommand fetchCommand(*this, firstMessage, lastMessage, 234 fFetchBodyLimit); 235 return ProcessCommand(&fetchCommand); 236} 237 238 239status_t 240IMAPMailbox::FetchBody(int32 messageNumber) 241{ 242 if (fStorage.BodyFetched(MessageNumberToUID(messageNumber))) 243 return B_BAD_VALUE; 244 245 BPositionIO* file; 246 status_t status = fStorage.OpenMessage(MessageNumberToUID(messageNumber), 247 &file); 248 if (status != B_OK) 249 return status; 250 251 // fetch command deletes the file 252 status = FetchBody(messageNumber, file); 253 return status; 254} 255 256 257status_t 258IMAPMailbox::SetFlags(int32 messageNumber, int32 flags) 259{ 260 if (messageNumber <= 0) 261 return B_BAD_VALUE; 262 263 SetFlagsCommand setFlagsCommand(*this, messageNumber, flags); 264 return ProcessCommand(&setFlagsCommand); 265} 266 267 268status_t 269IMAPMailbox::AppendMessage(BPositionIO& message, off_t size, int32 flags, 270 time_t time) 271{ 272 AppendCommand appendCommand(*this, message, size, flags, time); 273 return ProcessCommand(&appendCommand); 274} 275 276 277int32 278IMAPMailbox::UIDToMessageNumber(int32 uid) 279{ 280 for (unsigned int i = 0; i < fMessageList.size(); i++) { 281 if (fMessageList[i].uid == uid) 282 return i + 1; 283 } 284 return -1; 285} 286 287 288int32 289IMAPMailbox::MessageNumberToUID(int32 messageNumber) 290{ 291 int32 index = messageNumber - 1; 292 if (index < 0 || index >= (int32)fMessageList.size()) 293 return -1; 294 return fMessageList[index].uid; 295} 296 297 298status_t 299IMAPMailbox::DeleteMessage(int32 uid, bool permanently) 300{ 301 int32 flags = fStorage.GetFlags(uid); 302 flags |= kDeleted; 303 status_t status = SetFlags(UIDToMessageNumber(uid), flags); 304 305 if (!permanently || status != B_OK) 306 return status; 307 308 // delete permanently by invoking expunge 309 ExpungeCommmand expungeCommand(*this); 310 return ProcessCommand(&expungeCommand); 311} 312 313 314void 315IMAPMailbox::_InstallUnsolicitedHandler(bool install) 316{ 317 if (install) { 318 fHandlerList.AddItem(&fFlagsHandler, 0); 319 fHandlerList.AddItem(&fExpungeHandler, 0); 320 fHandlerList.AddItem(&fExistsHandler, 0); 321 } else { 322 fHandlerList.RemoveItem(&fFlagsHandler); 323 fHandlerList.RemoveItem(&fExpungeHandler); 324 fHandlerList.RemoveItem(&fExistsHandler); 325 } 326} 327