1 /* 2 * Copyright 2011-2016, Haiku, Inc. All rights reserved. 3 * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. 4 */ 5 6 7 #include <stdio.h> 8 #include <stdlib.h> 9 10 #include <fs_attr.h> 11 12 #include <Alert.h> 13 #include <Autolock.h> 14 #include <Directory.h> 15 #include <E-mail.h> 16 #include <FindDirectory.h> 17 #include <Node.h> 18 #include <NodeInfo.h> 19 #include <NodeMonitor.h> 20 #include <Path.h> 21 #include <Query.h> 22 #include <Roster.h> 23 #include <String.h> 24 #include <StringList.h> 25 #include <VolumeRoster.h> 26 27 #include <MailFilter.h> 28 #include <MailDaemon.h> 29 #include <MailProtocol.h> 30 #include <MailSettings.h> 31 32 #include <mail_util.h> 33 #include <MailPrivate.h> 34 #include <NodeMessage.h> 35 36 #include "HaikuMailFormatFilter.h" 37 38 39 using namespace BPrivate; 40 41 42 BMailProtocol::BMailProtocol(const char* name, 43 const BMailAccountSettings& settings) 44 : 45 BLooper(_LooperName(name, settings)), 46 fAccountSettings(settings), 47 fMailNotifier(NULL) 48 { 49 AddFilter(new HaikuMailFormatFilter(*this, settings)); 50 } 51 52 53 BMailProtocol::~BMailProtocol() 54 { 55 delete fMailNotifier; 56 57 for (int i = 0; i < fFilterList.CountItems(); i++) 58 delete fFilterList.ItemAt(i); 59 60 std::map<entry_ref, image_id>::iterator it = fFilterImages.begin(); 61 for (; it != fFilterImages.end(); it++) 62 unload_add_on(it->second); 63 } 64 65 66 const BMailAccountSettings& 67 BMailProtocol::AccountSettings() const 68 { 69 return fAccountSettings; 70 } 71 72 73 void 74 BMailProtocol::SetMailNotifier(BMailNotifier* mailNotifier) 75 { 76 delete fMailNotifier; 77 fMailNotifier = mailNotifier; 78 } 79 80 81 BMailNotifier* 82 BMailProtocol::MailNotifier() const 83 { 84 return fMailNotifier; 85 } 86 87 88 bool 89 BMailProtocol::AddFilter(BMailFilter* filter) 90 { 91 BAutolock locker(const_cast< BMailProtocol * >(this)); 92 return fFilterList.AddItem(filter); 93 } 94 95 96 int32 97 BMailProtocol::CountFilter() const 98 { 99 BAutolock locker(const_cast< BMailProtocol * >(this)); 100 return fFilterList.CountItems(); 101 } 102 103 104 BMailFilter* 105 BMailProtocol::FilterAt(int32 index) const 106 { 107 BAutolock locker(const_cast< BMailProtocol * >(this)); 108 return fFilterList.ItemAt(index); 109 } 110 111 112 BMailFilter* 113 BMailProtocol::RemoveFilter(int32 index) 114 { 115 BAutolock locker(const_cast< BMailProtocol * >(this)); 116 return fFilterList.RemoveItemAt(index); 117 } 118 119 120 bool 121 BMailProtocol::RemoveFilter(BMailFilter* filter) 122 { 123 BAutolock locker(const_cast< BMailProtocol * >(this)); 124 return fFilterList.RemoveItem(filter); 125 } 126 127 128 void 129 BMailProtocol::MessageReceived(BMessage* message) 130 { 131 BLooper::MessageReceived(message); 132 } 133 134 135 void 136 BMailProtocol::ShowError(const char* error) 137 { 138 if (MailNotifier() != NULL) 139 MailNotifier()->ShowError(error); 140 } 141 142 143 void 144 BMailProtocol::ShowMessage(const char* message) 145 { 146 if (MailNotifier() != NULL) 147 MailNotifier()->ShowMessage(message); 148 } 149 150 151 void 152 BMailProtocol::SetTotalItems(uint32 items) 153 { 154 if (MailNotifier() != NULL) 155 MailNotifier()->SetTotalItems(items); 156 } 157 158 159 void 160 BMailProtocol::SetTotalItemsSize(uint64 size) 161 { 162 if (MailNotifier() != NULL) 163 MailNotifier()->SetTotalItemsSize(size); 164 } 165 166 167 void 168 BMailProtocol::ReportProgress(uint32 messages, uint64 bytes, 169 const char* message) 170 { 171 if (MailNotifier() != NULL) 172 MailNotifier()->ReportProgress(messages, bytes, message); 173 } 174 175 176 void 177 BMailProtocol::ResetProgress(const char* message) 178 { 179 if (MailNotifier() != NULL) 180 MailNotifier()->ResetProgress(message); 181 } 182 183 184 void 185 BMailProtocol::NotifyNewMessagesToFetch(int32 count) 186 { 187 ResetProgress(); 188 SetTotalItems(count); 189 } 190 191 192 BMailFilterAction 193 BMailProtocol::ProcessHeaderFetched(entry_ref& ref, BFile& file, 194 BMessage& attributes) 195 { 196 BMailFilterAction action = _ProcessHeaderFetched(ref, file, attributes); 197 if (action >= B_OK && action != B_DELETE_MAIL_ACTION) 198 file << attributes; 199 200 return action; 201 } 202 203 204 void 205 BMailProtocol::NotifyBodyFetched(const entry_ref& ref, BFile& file, 206 BMessage& attributes) 207 { 208 _NotifyBodyFetched(ref, file, attributes); 209 file << attributes; 210 } 211 212 213 BMailFilterAction 214 BMailProtocol::ProcessMessageFetched(entry_ref& ref, BFile& file, 215 BMessage& attributes) 216 { 217 BMailFilterAction action = _ProcessHeaderFetched(ref, file, attributes); 218 if (action >= B_OK && action != B_DELETE_MAIL_ACTION) { 219 _NotifyBodyFetched(ref, file, attributes); 220 file << attributes; 221 } 222 223 return action; 224 } 225 226 227 void 228 BMailProtocol::NotifyMessageReadyToSend(const entry_ref& ref, BFile& file) 229 { 230 for (int i = 0; i < fFilterList.CountItems(); i++) 231 fFilterList.ItemAt(i)->MessageReadyToSend(ref, file); 232 } 233 234 235 void 236 BMailProtocol::NotifyMessageSent(const entry_ref& ref, BFile& file) 237 { 238 for (int i = 0; i < fFilterList.CountItems(); i++) 239 fFilterList.ItemAt(i)->MessageSent(ref, file); 240 } 241 242 243 void 244 BMailProtocol::LoadFilters(const BMailProtocolSettings& settings) 245 { 246 for (int i = 0; i < settings.CountFilterSettings(); i++) { 247 BMailAddOnSettings* filterSettings = settings.FilterSettingsAt(i); 248 BMailFilter* filter = _LoadFilter(*filterSettings); 249 if (filter != NULL) 250 AddFilter(filter); 251 } 252 } 253 254 255 /*static*/ BString 256 BMailProtocol::_LooperName(const char* addOnName, 257 const BMailAccountSettings& settings) 258 { 259 BString name = addOnName; 260 261 const char* accountName = settings.Name(); 262 if (accountName != NULL && accountName[0] != '\0') 263 name << " " << accountName; 264 265 return name; 266 } 267 268 269 BMailFilter* 270 BMailProtocol::_LoadFilter(const BMailAddOnSettings& settings) 271 { 272 const entry_ref& ref = settings.AddOnRef(); 273 std::map<entry_ref, image_id>::iterator it = fFilterImages.find(ref); 274 image_id image; 275 if (it != fFilterImages.end()) 276 image = it->second; 277 else { 278 BEntry entry(&ref); 279 BPath path(&entry); 280 image = load_add_on(path.Path()); 281 } 282 if (image < 0) 283 return NULL; 284 285 BMailFilter* (*instantiateFilter)(BMailProtocol& protocol, 286 const BMailAddOnSettings& settings); 287 if (get_image_symbol(image, "instantiate_filter", B_SYMBOL_TYPE_TEXT, 288 (void**)&instantiateFilter) != B_OK) { 289 unload_add_on(image); 290 return NULL; 291 } 292 293 fFilterImages[ref] = image; 294 return instantiateFilter(*this, settings); 295 } 296 297 298 BMailFilterAction 299 BMailProtocol::_ProcessHeaderFetched(entry_ref& ref, BFile& file, 300 BMessage& attributes) 301 { 302 entry_ref outRef = ref; 303 304 for (int i = 0; i < fFilterList.CountItems(); i++) { 305 BMailFilterAction action = fFilterList.ItemAt(i)->HeaderFetched(outRef, 306 file, attributes); 307 if (action == B_DELETE_MAIL_ACTION) { 308 // We have to delete the message 309 BEntry entry(&ref); 310 status_t status = entry.Remove(); 311 if (status != B_OK) { 312 fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could " 313 "not delete mail: %s\n", strerror(status)); 314 } 315 return B_DELETE_MAIL_ACTION; 316 } 317 } 318 319 if (ref == outRef) 320 return B_NO_MAIL_ACTION; 321 322 // We have to rename the file 323 node_ref newParentRef; 324 newParentRef.device = outRef.device; 325 newParentRef.node = outRef.directory; 326 327 BDirectory newParent(&newParentRef); 328 status_t status = newParent.InitCheck(); 329 BString workerName; 330 if (status == B_OK) { 331 int32 uniqueNumber = 1; 332 do { 333 workerName = outRef.name; 334 if (uniqueNumber > 1) 335 workerName << "_" << uniqueNumber; 336 337 // TODO: support copying to another device! 338 BEntry entry(&ref); 339 status = entry.Rename(workerName); 340 341 uniqueNumber++; 342 } while (status == B_FILE_EXISTS); 343 } 344 345 if (status != B_OK) { 346 fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could not " 347 "rename mail (%s)! (should be: %s)\n", strerror(status), 348 workerName.String()); 349 } 350 351 ref = outRef; 352 ref.set_name(workerName.String()); 353 354 return B_MOVE_MAIL_ACTION; 355 } 356 357 358 void 359 BMailProtocol::_NotifyBodyFetched(const entry_ref& ref, BFile& file, 360 BMessage& attributes) 361 { 362 for (int i = 0; i < fFilterList.CountItems(); i++) 363 fFilterList.ItemAt(i)->BodyFetched(ref, file, attributes); 364 } 365 366 367 // #pragma mark - 368 369 370 BInboundMailProtocol::BInboundMailProtocol(const char* name, 371 const BMailAccountSettings& settings) 372 : 373 BMailProtocol(name, settings) 374 { 375 LoadFilters(fAccountSettings.InboundSettings()); 376 } 377 378 379 BInboundMailProtocol::~BInboundMailProtocol() 380 { 381 } 382 383 384 void 385 BInboundMailProtocol::MessageReceived(BMessage* message) 386 { 387 switch (message->what) { 388 case kMsgSyncMessages: 389 { 390 NotiyMailboxSynchronized(SyncMessages()); 391 break; 392 } 393 394 case kMsgFetchBody: 395 { 396 entry_ref ref; 397 if (message->FindRef("ref", &ref) != B_OK) 398 break; 399 400 BMessenger target; 401 message->FindMessenger("target", &target); 402 403 status_t status = HandleFetchBody(ref, target); 404 if (status != B_OK) 405 ReplyBodyFetched(target, ref, status); 406 break; 407 } 408 409 case kMsgMarkMessageAsRead: 410 { 411 entry_ref ref; 412 message->FindRef("ref", &ref); 413 read_flags read = (read_flags)message->FindInt32("read"); 414 MarkMessageAsRead(ref, read); 415 break; 416 } 417 418 default: 419 BMailProtocol::MessageReceived(message); 420 break; 421 } 422 } 423 424 425 status_t 426 BInboundMailProtocol::FetchBody(const entry_ref& ref, BMessenger* replyTo) 427 { 428 BMessage message(kMsgFetchBody); 429 message.AddRef("ref", &ref); 430 if (replyTo != NULL) 431 message.AddMessenger("target", *replyTo); 432 433 return BMessenger(this).SendMessage(&message); 434 } 435 436 437 status_t 438 BInboundMailProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flag) 439 { 440 BNode node(&ref); 441 return write_read_attr(node, flag); 442 } 443 444 445 /*static*/ void 446 BInboundMailProtocol::ReplyBodyFetched(const BMessenger& replyTo, 447 const entry_ref& ref, status_t status) 448 { 449 BMessage message(B_MAIL_BODY_FETCHED); 450 message.AddInt32("status", status); 451 message.AddRef("ref", &ref); 452 replyTo.SendMessage(&message); 453 } 454 455 456 void 457 BInboundMailProtocol::NotiyMailboxSynchronized(status_t status) 458 { 459 for (int32 i = 0; i < CountFilter(); i++) 460 FilterAt(i)->MailboxSynchronized(status); 461 } 462 463 464 // #pragma mark - 465 466 467 BOutboundMailProtocol::BOutboundMailProtocol(const char* name, 468 const BMailAccountSettings& settings) 469 : 470 BMailProtocol(name, settings) 471 { 472 LoadFilters(fAccountSettings.OutboundSettings()); 473 } 474 475 476 BOutboundMailProtocol::~BOutboundMailProtocol() 477 { 478 } 479 480 481 status_t 482 BOutboundMailProtocol::SendMessages(const BMessage& files, off_t totalBytes) 483 { 484 BMessage message(kMsgSendMessages); 485 message.Append(files); 486 message.AddInt64("bytes", totalBytes); 487 488 return BMessenger(this).SendMessage(&message); 489 } 490 491 492 void 493 BOutboundMailProtocol::MessageReceived(BMessage* message) 494 { 495 switch (message->what) { 496 case kMsgSendMessages: 497 HandleSendMessages(*message, message->FindInt64("bytes")); 498 break; 499 500 default: 501 BMailProtocol::MessageReceived(message); 502 } 503 } 504