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 39using namespace BPrivate; 40 41 42BMailProtocol::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 53BMailProtocol::~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 66const BMailAccountSettings& 67BMailProtocol::AccountSettings() const 68{ 69 return fAccountSettings; 70} 71 72 73void 74BMailProtocol::SetMailNotifier(BMailNotifier* mailNotifier) 75{ 76 delete fMailNotifier; 77 fMailNotifier = mailNotifier; 78} 79 80 81BMailNotifier* 82BMailProtocol::MailNotifier() const 83{ 84 return fMailNotifier; 85} 86 87 88bool 89BMailProtocol::AddFilter(BMailFilter* filter) 90{ 91 BAutolock locker(const_cast< BMailProtocol * >(this)); 92 return fFilterList.AddItem(filter); 93} 94 95 96int32 97BMailProtocol::CountFilter() const 98{ 99 BAutolock locker(const_cast< BMailProtocol * >(this)); 100 return fFilterList.CountItems(); 101} 102 103 104BMailFilter* 105BMailProtocol::FilterAt(int32 index) const 106{ 107 BAutolock locker(const_cast< BMailProtocol * >(this)); 108 return fFilterList.ItemAt(index); 109} 110 111 112BMailFilter* 113BMailProtocol::RemoveFilter(int32 index) 114{ 115 BAutolock locker(const_cast< BMailProtocol * >(this)); 116 return fFilterList.RemoveItemAt(index); 117} 118 119 120bool 121BMailProtocol::RemoveFilter(BMailFilter* filter) 122{ 123 BAutolock locker(const_cast< BMailProtocol * >(this)); 124 return fFilterList.RemoveItem(filter); 125} 126 127 128void 129BMailProtocol::MessageReceived(BMessage* message) 130{ 131 BLooper::MessageReceived(message); 132} 133 134 135void 136BMailProtocol::ShowError(const char* error) 137{ 138 if (MailNotifier() != NULL) 139 MailNotifier()->ShowError(error); 140} 141 142 143void 144BMailProtocol::ShowMessage(const char* message) 145{ 146 if (MailNotifier() != NULL) 147 MailNotifier()->ShowMessage(message); 148} 149 150 151void 152BMailProtocol::SetTotalItems(uint32 items) 153{ 154 if (MailNotifier() != NULL) 155 MailNotifier()->SetTotalItems(items); 156} 157 158 159void 160BMailProtocol::SetTotalItemsSize(uint64 size) 161{ 162 if (MailNotifier() != NULL) 163 MailNotifier()->SetTotalItemsSize(size); 164} 165 166 167void 168BMailProtocol::ReportProgress(uint32 messages, uint64 bytes, 169 const char* message) 170{ 171 if (MailNotifier() != NULL) 172 MailNotifier()->ReportProgress(messages, bytes, message); 173} 174 175 176void 177BMailProtocol::ResetProgress(const char* message) 178{ 179 if (MailNotifier() != NULL) 180 MailNotifier()->ResetProgress(message); 181} 182 183 184void 185BMailProtocol::NotifyNewMessagesToFetch(int32 count) 186{ 187 ResetProgress(); 188 SetTotalItems(count); 189} 190 191 192BMailFilterAction 193BMailProtocol::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 204void 205BMailProtocol::NotifyBodyFetched(const entry_ref& ref, BFile& file, 206 BMessage& attributes) 207{ 208 _NotifyBodyFetched(ref, file, attributes); 209 file << attributes; 210} 211 212 213BMailFilterAction 214BMailProtocol::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 227void 228BMailProtocol::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 235void 236BMailProtocol::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 243void 244BMailProtocol::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 256BMailProtocol::_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 269BMailFilter* 270BMailProtocol::_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 298BMailFilterAction 299BMailProtocol::_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 358void 359BMailProtocol::_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 370BInboundMailProtocol::BInboundMailProtocol(const char* name, 371 const BMailAccountSettings& settings) 372 : 373 BMailProtocol(name, settings) 374{ 375 LoadFilters(fAccountSettings.InboundSettings()); 376} 377 378 379BInboundMailProtocol::~BInboundMailProtocol() 380{ 381} 382 383 384void 385BInboundMailProtocol::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 425status_t 426BInboundMailProtocol::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 437status_t 438BInboundMailProtocol::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 446BInboundMailProtocol::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 456void 457BInboundMailProtocol::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 467BOutboundMailProtocol::BOutboundMailProtocol(const char* name, 468 const BMailAccountSettings& settings) 469 : 470 BMailProtocol(name, settings) 471{ 472 LoadFilters(fAccountSettings.OutboundSettings()); 473} 474 475 476BOutboundMailProtocol::~BOutboundMailProtocol() 477{ 478} 479 480 481status_t 482BOutboundMailProtocol::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 492void 493BOutboundMailProtocol::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