1 /* 2 * Copyright 2007-2015, Haiku Inc. All Rights Reserved. 3 * Copyright 2001-2004 Dr. Zoidberg Enterprises. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 //! The main general purpose mail message class 10 11 12 #include <MailMessage.h> 13 14 #include <ctype.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <sys/utsname.h> 19 20 #include <parsedate.h> 21 22 #include <Directory.h> 23 #include <E-mail.h> 24 #include <Entry.h> 25 #include <File.h> 26 #include <FindDirectory.h> 27 #include <List.h> 28 #include <MailAttachment.h> 29 #include <MailDaemon.h> 30 #include <MailSettings.h> 31 #include <Messenger.h> 32 #include <netdb.h> 33 #include <NodeInfo.h> 34 #include <Path.h> 35 #include <String.h> 36 #include <StringList.h> 37 38 #include <MailPrivate.h> 39 #include <mail_util.h> 40 41 42 using namespace BPrivate; 43 44 45 //-------Change the following!---------------------- 46 #define mime_boundary "----------Zoidberg-BeMail-temp--------" 47 #define mime_warning "This is a multipart message in MIME format." 48 49 50 BEmailMessage::BEmailMessage(BPositionIO* file, bool own, uint32 defaultCharSet) 51 : 52 BMailContainer(defaultCharSet), 53 fData(NULL), 54 fStatus(B_NO_ERROR), 55 fBCC(NULL), 56 fComponentCount(0), 57 fBody(NULL), 58 fTextBody(NULL) 59 { 60 BMailSettings settings; 61 fAccountID = settings.DefaultOutboundAccount(); 62 63 if (own) 64 fData = file; 65 66 if (file != NULL) 67 SetToRFC822(file, ~0L); 68 } 69 70 71 BEmailMessage::BEmailMessage(const entry_ref* ref, uint32 defaultCharSet) 72 : 73 BMailContainer(defaultCharSet), 74 fBCC(NULL), 75 fComponentCount(0), 76 fBody(NULL), 77 fTextBody(NULL) 78 { 79 BMailSettings settings; 80 fAccountID = settings.DefaultOutboundAccount(); 81 82 fData = new BFile(); 83 fStatus = static_cast<BFile*>(fData)->SetTo(ref, B_READ_ONLY); 84 85 if (fStatus == B_OK) 86 SetToRFC822(fData, ~0L); 87 } 88 89 90 BEmailMessage::~BEmailMessage() 91 { 92 free(fBCC); 93 94 delete fBody; 95 delete fData; 96 } 97 98 99 status_t 100 BEmailMessage::InitCheck() const 101 { 102 return fStatus; 103 } 104 105 106 BEmailMessage* 107 BEmailMessage::ReplyMessage(mail_reply_to_mode replyTo, bool accountFromMail, 108 const char* quoteStyle) 109 { 110 BEmailMessage* reply = new BEmailMessage; 111 112 // Set ReplyTo: 113 114 if (replyTo == B_MAIL_REPLY_TO_ALL) { 115 reply->SetTo(From()); 116 117 BList list; 118 get_address_list(list, CC(), extract_address); 119 get_address_list(list, To(), extract_address); 120 121 // Filter out the sender 122 BMailAccounts accounts; 123 BMailAccountSettings* account = accounts.AccountByID(Account()); 124 BString sender; 125 if (account != NULL) 126 sender = account->ReturnAddress(); 127 extract_address(sender); 128 129 BString cc; 130 131 for (int32 i = list.CountItems(); i-- > 0;) { 132 char* address = (char*)list.RemoveItem((int32)0); 133 134 // Add everything which is not the sender and not already in the 135 // list 136 if (sender.ICompare(address) && cc.FindFirst(address) < 0) { 137 if (cc.Length() > 0) 138 cc << ", "; 139 140 cc << address; 141 } 142 143 free(address); 144 } 145 146 if (cc.Length() > 0) 147 reply->SetCC(cc.String()); 148 } else if (replyTo == B_MAIL_REPLY_TO_SENDER || ReplyTo() == NULL) 149 reply->SetTo(From()); 150 else 151 reply->SetTo(ReplyTo()); 152 153 // Set special "In-Reply-To:" header (used for threading) 154 const char* messageID = fBody ? fBody->HeaderField("Message-Id") : NULL; 155 if (messageID != NULL) 156 reply->SetHeaderField("In-Reply-To", messageID); 157 158 // quote body text 159 reply->SetBodyTextTo(BodyText()); 160 if (quoteStyle) 161 reply->Body()->Quote(quoteStyle); 162 163 // Set the subject (and add a "Re:" if needed) 164 BString string = Subject(); 165 if (string.ICompare("re:", 3) != 0) 166 string.Prepend("Re: "); 167 reply->SetSubject(string.String()); 168 169 // set the matching outbound chain 170 if (accountFromMail) 171 reply->SendViaAccountFrom(this); 172 173 return reply; 174 } 175 176 177 BEmailMessage* 178 BEmailMessage::ForwardMessage(bool accountFromMail, bool includeAttachments) 179 { 180 BString header = "------ Forwarded Message: ------\n"; 181 header << "To: " << To() << '\n'; 182 header << "From: " << From() << '\n'; 183 if (CC() != NULL) { 184 // Can use CC rather than "Cc" since display only. 185 header << "CC: " << CC() << '\n'; 186 } 187 header << "Subject: " << Subject() << '\n'; 188 header << "Date: " << Date() << "\n\n"; 189 if (fTextBody != NULL) 190 header << fTextBody->Text() << '\n'; 191 BEmailMessage *message = new BEmailMessage(); 192 message->SetBodyTextTo(header.String()); 193 194 // set the subject 195 BString subject = Subject(); 196 if (subject.IFindFirst("fwd") == B_ERROR 197 && subject.IFindFirst("forward") == B_ERROR 198 && subject.FindFirst("FW") == B_ERROR) 199 subject << " (fwd)"; 200 message->SetSubject(subject.String()); 201 202 if (includeAttachments) { 203 for (int32 i = 0; i < CountComponents(); i++) { 204 BMailComponent* component = GetComponent(i); 205 if (component == fTextBody || component == NULL) 206 continue; 207 208 //---I am ashamed to have the written the code between here and the next comment 209 // ... and you still managed to get it wrong ;-)), axeld. 210 // we should really move this stuff into copy constructors 211 // or something like that 212 213 BMallocIO io; 214 component->RenderToRFC822(&io); 215 BMailComponent* clone = component->WhatIsThis(); 216 io.Seek(0, SEEK_SET); 217 clone->SetToRFC822(&io, io.BufferLength(), true); 218 message->AddComponent(clone); 219 } 220 } 221 if (accountFromMail) 222 message->SendViaAccountFrom(this); 223 224 return message; 225 } 226 227 228 const char* 229 BEmailMessage::To() 230 { 231 return HeaderField("To"); 232 } 233 234 235 const char* 236 BEmailMessage::From() 237 { 238 return HeaderField("From"); 239 } 240 241 242 const char* 243 BEmailMessage::ReplyTo() 244 { 245 return HeaderField("Reply-To"); 246 } 247 248 249 const char* 250 BEmailMessage::CC() 251 { 252 return HeaderField("Cc"); 253 // Note case of CC is "Cc" in our internal headers. 254 } 255 256 257 const char* 258 BEmailMessage::Subject() 259 { 260 return HeaderField("Subject"); 261 } 262 263 264 const char* 265 BEmailMessage::Date() 266 { 267 return HeaderField("Date"); 268 } 269 270 271 int 272 BEmailMessage::Priority() 273 { 274 int priorityNumber; 275 const char* priorityString; 276 277 /* The usual values are a number from 1 to 5, or one of three words: 278 X-Priority: 1 and/or X-MSMail-Priority: High 279 X-Priority: 3 and/or X-MSMail-Priority: Normal 280 X-Priority: 5 and/or X-MSMail-Priority: Low 281 Also plain Priority: is "normal", "urgent" or "non-urgent", see RFC 1327. */ 282 283 priorityString = HeaderField("Priority"); 284 if (priorityString == NULL) 285 priorityString = HeaderField("X-Priority"); 286 if (priorityString == NULL) 287 priorityString = HeaderField("X-Msmail-Priority"); 288 if (priorityString == NULL) 289 return 3; 290 priorityNumber = atoi (priorityString); 291 if (priorityNumber != 0) { 292 if (priorityNumber > 5) 293 priorityNumber = 5; 294 if (priorityNumber < 1) 295 priorityNumber = 1; 296 return priorityNumber; 297 } 298 if (strcasecmp (priorityString, "Low") == 0 299 || strcasecmp (priorityString, "non-urgent") == 0) 300 return 5; 301 if (strcasecmp (priorityString, "High") == 0 302 || strcasecmp (priorityString, "urgent") == 0) 303 return 1; 304 return 3; 305 } 306 307 308 void 309 BEmailMessage::SetSubject(const char* subject, uint32 charset, 310 mail_encoding encoding) 311 { 312 SetHeaderField("Subject", subject, charset, encoding); 313 } 314 315 316 void 317 BEmailMessage::SetReplyTo(const char* replyTo, uint32 charset, 318 mail_encoding encoding) 319 { 320 SetHeaderField("Reply-To", replyTo, charset, encoding); 321 } 322 323 324 void 325 BEmailMessage::SetFrom(const char* from, uint32 charset, mail_encoding encoding) 326 { 327 SetHeaderField("From", from, charset, encoding); 328 } 329 330 331 void 332 BEmailMessage::SetTo(const char* to, uint32 charset, mail_encoding encoding) 333 { 334 SetHeaderField("To", to, charset, encoding); 335 } 336 337 338 void 339 BEmailMessage::SetCC(const char* cc, uint32 charset, mail_encoding encoding) 340 { 341 // For consistency with our header names, use Cc as the name. 342 SetHeaderField("Cc", cc, charset, encoding); 343 } 344 345 346 void 347 BEmailMessage::SetBCC(const char* bcc) 348 { 349 free(fBCC); 350 fBCC = strdup(bcc); 351 } 352 353 354 void 355 BEmailMessage::SetPriority(int to) 356 { 357 char tempString[20]; 358 359 if (to < 1) 360 to = 1; 361 if (to > 5) 362 to = 5; 363 sprintf (tempString, "%d", to); 364 SetHeaderField("X-Priority", tempString); 365 if (to <= 2) { 366 SetHeaderField("Priority", "urgent"); 367 SetHeaderField("X-Msmail-Priority", "High"); 368 } else if (to >= 4) { 369 SetHeaderField("Priority", "non-urgent"); 370 SetHeaderField("X-Msmail-Priority", "Low"); 371 } else { 372 SetHeaderField("Priority", "normal"); 373 SetHeaderField("X-Msmail-Priority", "Normal"); 374 } 375 } 376 377 378 status_t 379 BEmailMessage::GetName(char* name, int32 maxLength) const 380 { 381 if (name == NULL || maxLength <= 0) 382 return B_BAD_VALUE; 383 384 if (BFile* file = dynamic_cast<BFile*>(fData)) { 385 status_t status = file->ReadAttr(B_MAIL_ATTR_NAME, B_STRING_TYPE, 0, 386 name, maxLength); 387 name[maxLength - 1] = '\0'; 388 389 return status >= 0 ? B_OK : status; 390 } 391 // TODO: look at From header? But usually there is 392 // a file since only the BeMail GUI calls this. 393 return B_ERROR; 394 } 395 396 397 status_t 398 BEmailMessage::GetName(BString* name) const 399 { 400 char* buffer = name->LockBuffer(B_FILE_NAME_LENGTH); 401 status_t status = GetName(buffer, B_FILE_NAME_LENGTH); 402 name->UnlockBuffer(); 403 404 return status; 405 } 406 407 408 void 409 BEmailMessage::SendViaAccountFrom(BEmailMessage* message) 410 { 411 BString name; 412 if (message->GetAccountName(name) < B_OK) { 413 // just return the message with the default account 414 return; 415 } 416 417 SendViaAccount(name); 418 } 419 420 421 void 422 BEmailMessage::SendViaAccount(const char* accountName) 423 { 424 BMailAccounts accounts; 425 BMailAccountSettings* account = accounts.AccountByName(accountName); 426 if (account != NULL) 427 SendViaAccount(account->AccountID()); 428 } 429 430 431 void 432 BEmailMessage::SendViaAccount(int32 account) 433 { 434 fAccountID = account; 435 436 BMailAccounts accounts; 437 BMailAccountSettings* accountSettings = accounts.AccountByID(fAccountID); 438 439 BString from; 440 if (accountSettings) { 441 from << '\"' << accountSettings->RealName() << "\" <" 442 << accountSettings->ReturnAddress() << '>'; 443 } 444 SetFrom(from); 445 } 446 447 448 int32 449 BEmailMessage::Account() const 450 { 451 return fAccountID; 452 } 453 454 455 status_t 456 BEmailMessage::GetAccountName(BString& accountName) const 457 { 458 BFile* file = dynamic_cast<BFile*>(fData); 459 if (file == NULL) 460 return B_ERROR; 461 462 int32 accountID; 463 size_t read = file->ReadAttr(B_MAIL_ATTR_ACCOUNT, B_INT32_TYPE, 0, 464 &accountID, sizeof(int32)); 465 if (read < sizeof(int32)) 466 return B_ERROR; 467 468 BMailAccounts accounts; 469 BMailAccountSettings* account = accounts.AccountByID(accountID); 470 if (account != NULL) 471 accountName = account->Name(); 472 else 473 accountName = ""; 474 475 return B_OK; 476 } 477 478 479 status_t 480 BEmailMessage::AddComponent(BMailComponent* component) 481 { 482 status_t status = B_OK; 483 484 if (fComponentCount == 0) 485 fBody = component; 486 else if (fComponentCount == 1) { 487 BMIMEMultipartMailContainer *container 488 = new BMIMEMultipartMailContainer( 489 mime_boundary, mime_warning, _charSetForTextDecoding); 490 status = container->AddComponent(fBody); 491 if (status == B_OK) 492 status = container->AddComponent(component); 493 fBody = container; 494 } else { 495 BMIMEMultipartMailContainer* container 496 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody); 497 if (container == NULL) 498 return B_MISMATCHED_VALUES; 499 500 status = container->AddComponent(component); 501 } 502 503 if (status == B_OK) 504 fComponentCount++; 505 return status; 506 } 507 508 509 status_t 510 BEmailMessage::RemoveComponent(BMailComponent* /*component*/) 511 { 512 // not yet implemented 513 // BeMail/Enclosures.cpp:169: contains a warning about this fact 514 return B_ERROR; 515 } 516 517 518 status_t 519 BEmailMessage::RemoveComponent(int32 /*index*/) 520 { 521 // not yet implemented 522 return B_ERROR; 523 } 524 525 526 BMailComponent* 527 BEmailMessage::GetComponent(int32 i, bool parseNow) 528 { 529 if (BMIMEMultipartMailContainer* container 530 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody)) 531 return container->GetComponent(i, parseNow); 532 533 if (i < fComponentCount) 534 return fBody; 535 536 return NULL; 537 } 538 539 540 int32 541 BEmailMessage::CountComponents() const 542 { 543 return fComponentCount; 544 } 545 546 547 void 548 BEmailMessage::Attach(entry_ref* ref, bool includeAttributes) 549 { 550 if (includeAttributes) 551 AddComponent(new BAttributedMailAttachment(ref)); 552 else 553 AddComponent(new BSimpleMailAttachment(ref)); 554 } 555 556 557 bool 558 BEmailMessage::IsComponentAttachment(int32 i) 559 { 560 if ((i >= fComponentCount) || (fComponentCount == 0)) 561 return false; 562 563 if (fComponentCount == 1) 564 return fBody->IsAttachment(); 565 566 BMIMEMultipartMailContainer* container 567 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody); 568 if (container == NULL) 569 return false; 570 571 BMailComponent* component = container->GetComponent(i); 572 if (component == NULL) 573 return false; 574 575 return component->IsAttachment(); 576 } 577 578 579 void 580 BEmailMessage::SetBodyTextTo(const char* text) 581 { 582 if (fTextBody == NULL) { 583 fTextBody = new BTextMailComponent; 584 AddComponent(fTextBody); 585 } 586 587 fTextBody->SetText(text); 588 } 589 590 591 BTextMailComponent* 592 BEmailMessage::Body() 593 { 594 if (fTextBody == NULL) 595 fTextBody = _RetrieveTextBody(fBody); 596 597 return fTextBody; 598 } 599 600 601 const char* 602 BEmailMessage::BodyText() 603 { 604 if (Body() == NULL) 605 return NULL; 606 607 return fTextBody->Text(); 608 } 609 610 611 status_t 612 BEmailMessage::SetBody(BTextMailComponent* body) 613 { 614 if (fTextBody != NULL) { 615 return B_ERROR; 616 // removing doesn't exist for now 617 // RemoveComponent(fTextBody); 618 // delete fTextBody; 619 } 620 fTextBody = body; 621 AddComponent(fTextBody); 622 623 return B_OK; 624 } 625 626 627 BTextMailComponent* 628 BEmailMessage::_RetrieveTextBody(BMailComponent* component) 629 { 630 BTextMailComponent* body = dynamic_cast<BTextMailComponent*>(component); 631 if (body != NULL) 632 return body; 633 634 BMIMEMultipartMailContainer* container 635 = dynamic_cast<BMIMEMultipartMailContainer*>(component); 636 if (container != NULL) { 637 for (int32 i = 0; i < container->CountComponents(); i++) { 638 if ((component = container->GetComponent(i)) == NULL) 639 continue; 640 641 switch (component->ComponentType()) { 642 case B_MAIL_PLAIN_TEXT_BODY: 643 // AttributedAttachment returns the MIME type of its 644 // contents, so we have to use dynamic_cast here 645 body = dynamic_cast<BTextMailComponent*>( 646 container->GetComponent(i)); 647 if (body != NULL) 648 return body; 649 break; 650 651 case B_MAIL_MULTIPART_CONTAINER: 652 body = _RetrieveTextBody(container->GetComponent(i)); 653 if (body != NULL) 654 return body; 655 break; 656 } 657 } 658 } 659 return NULL; 660 } 661 662 663 status_t 664 BEmailMessage::SetToRFC822(BPositionIO* mailFile, size_t length, 665 bool parseNow) 666 { 667 if (BFile* file = dynamic_cast<BFile*>(mailFile)) { 668 file->ReadAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0, &fAccountID, 669 sizeof(fAccountID)); 670 } 671 672 mailFile->Seek(0, SEEK_END); 673 length = mailFile->Position(); 674 mailFile->Seek(0, SEEK_SET); 675 676 fStatus = BMailComponent::SetToRFC822(mailFile, length, parseNow); 677 if (fStatus < B_OK) 678 return fStatus; 679 680 fBody = WhatIsThis(); 681 682 mailFile->Seek(0, SEEK_SET); 683 fStatus = fBody->SetToRFC822(mailFile, length, parseNow); 684 if (fStatus < B_OK) 685 return fStatus; 686 687 // Move headers that we use to us, everything else to fBody 688 const char* name; 689 for (int32 i = 0; (name = fBody->HeaderAt(i)) != NULL; i++) { 690 if (strcasecmp(name, "Subject") != 0 691 && strcasecmp(name, "To") != 0 692 && strcasecmp(name, "From") != 0 693 && strcasecmp(name, "Reply-To") != 0 694 && strcasecmp(name, "Cc") != 0 695 && strcasecmp(name, "Priority") != 0 696 && strcasecmp(name, "X-Priority") != 0 697 && strcasecmp(name, "X-Msmail-Priority") != 0 698 && strcasecmp(name, "Date") != 0) { 699 RemoveHeader(name); 700 } 701 } 702 703 fBody->RemoveHeader("Subject"); 704 fBody->RemoveHeader("To"); 705 fBody->RemoveHeader("From"); 706 fBody->RemoveHeader("Reply-To"); 707 fBody->RemoveHeader("Cc"); 708 fBody->RemoveHeader("Priority"); 709 fBody->RemoveHeader("X-Priority"); 710 fBody->RemoveHeader("X-Msmail-Priority"); 711 fBody->RemoveHeader("Date"); 712 713 fComponentCount = 1; 714 if (BMIMEMultipartMailContainer* container 715 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody)) 716 fComponentCount = container->CountComponents(); 717 718 return B_OK; 719 } 720 721 722 status_t 723 BEmailMessage::RenderToRFC822(BPositionIO* file) 724 { 725 if (fBody == NULL) 726 return B_MAIL_INVALID_MAIL; 727 728 // Do real rendering 729 730 if (From() == NULL) { 731 // set the "From:" string 732 SendViaAccount(fAccountID); 733 } 734 735 BList recipientList; 736 get_address_list(recipientList, To(), extract_address); 737 get_address_list(recipientList, CC(), extract_address); 738 get_address_list(recipientList, fBCC, extract_address); 739 740 BString recipients; 741 for (int32 i = recipientList.CountItems(); i-- > 0;) { 742 char *address = (char *)recipientList.RemoveItem((int32)0); 743 744 recipients << '<' << address << '>'; 745 if (i) 746 recipients << ','; 747 748 free(address); 749 } 750 751 // add the date field 752 int32 creationTime = time(NULL); 753 { 754 char date[128]; 755 struct tm tm; 756 localtime_r(&creationTime, &tm); 757 758 size_t length = strftime(date, sizeof(date), 759 "%a, %d %b %Y %H:%M:%S", &tm); 760 761 // GMT offsets are full hours, yes, but you never know :-) 762 snprintf(date + length, sizeof(date) - length, " %+03d%02d", 763 tm.tm_gmtoff / 3600, (tm.tm_gmtoff / 60) % 60); 764 765 SetHeaderField("Date", date); 766 } 767 768 // add a message-id 769 770 // empirical evidence indicates message id must be enclosed in 771 // angle brackets and there must be an "at" symbol in it 772 BString messageID; 773 messageID << "<"; 774 messageID << system_time(); 775 messageID << "-BeMail@"; 776 777 char host[255]; 778 if (gethostname(host, sizeof(host)) < 0 || !host[0]) 779 strcpy(host, "zoidberg"); 780 781 messageID << host; 782 messageID << ">"; 783 784 SetHeaderField("Message-Id", messageID.String()); 785 786 status_t err = BMailComponent::RenderToRFC822(file); 787 if (err < B_OK) 788 return err; 789 790 file->Seek(-2, SEEK_CUR); 791 // Remove division between headers 792 793 err = fBody->RenderToRFC822(file); 794 if (err < B_OK) 795 return err; 796 797 // Set the message file's attributes. Do this after the rest of the file 798 // is filled in, in case the daemon attempts to send it before it is ready 799 // (since the daemon may send it when it sees the status attribute getting 800 // set to "Pending"). 801 802 if (BFile* attributed = dynamic_cast <BFile*>(file)) { 803 BNodeInfo(attributed).SetType(B_MAIL_TYPE); 804 805 attributed->WriteAttrString(B_MAIL_ATTR_RECIPIENTS,&recipients); 806 807 BString attr; 808 809 attr = To(); 810 attributed->WriteAttrString(B_MAIL_ATTR_TO, &attr); 811 attr = CC(); 812 attributed->WriteAttrString(B_MAIL_ATTR_CC, &attr); 813 attr = Subject(); 814 attributed->WriteAttrString(B_MAIL_ATTR_SUBJECT, &attr); 815 attr = ReplyTo(); 816 attributed->WriteAttrString(B_MAIL_ATTR_REPLY, &attr); 817 attr = From(); 818 attributed->WriteAttrString(B_MAIL_ATTR_FROM, &attr); 819 if (Priority() != 3 /* Normal is 3 */) { 820 sprintf(attr.LockBuffer(40), "%d", Priority()); 821 attr.UnlockBuffer(-1); 822 attributed->WriteAttrString(B_MAIL_ATTR_PRIORITY, &attr); 823 } 824 attr = "Pending"; 825 attributed->WriteAttrString(B_MAIL_ATTR_STATUS, &attr); 826 attr = "1.0"; 827 attributed->WriteAttrString(B_MAIL_ATTR_MIME, &attr); 828 829 attributed->WriteAttr(B_MAIL_ATTR_ACCOUNT, B_INT32_TYPE, 0, 830 &fAccountID, sizeof(int32)); 831 832 attributed->WriteAttr(B_MAIL_ATTR_WHEN, B_TIME_TYPE, 0, &creationTime, 833 sizeof(int32)); 834 int32 flags = B_MAIL_PENDING | B_MAIL_SAVE; 835 attributed->WriteAttr(B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0, &flags, 836 sizeof(int32)); 837 838 attributed->WriteAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0, 839 &fAccountID, sizeof(int32)); 840 } 841 842 return B_OK; 843 } 844 845 846 status_t 847 BEmailMessage::RenderTo(BDirectory* dir, BEntry* msg) 848 { 849 time_t currentTime; 850 char numericDateString[40]; 851 struct tm timeFields; 852 BString worker; 853 854 // Generate a file name for the outgoing message. See also 855 // FolderFilter::ProcessMailMessage which does something similar for 856 // incoming messages. 857 858 BString name = Subject(); 859 SubjectToThread(name); 860 // Extract the core subject words. 861 if (name.Length() <= 0) 862 name = "No Subject"; 863 if (name[0] == '.') { 864 // Avoid hidden files, starting with a dot. 865 name.Prepend("_"); 866 } 867 868 // Convert the date into a year-month-day fixed digit width format, so that 869 // sorting by file name will give all the messages with the same subject in 870 // order of date. 871 time (¤tTime); 872 localtime_r (¤tTime, &timeFields); 873 sprintf (numericDateString, "%04d%02d%02d%02d%02d%02d", 874 timeFields.tm_year + 1900, timeFields.tm_mon + 1, timeFields.tm_mday, 875 timeFields.tm_hour, timeFields.tm_min, timeFields.tm_sec); 876 name << " " << numericDateString; 877 878 worker = From(); 879 extract_address_name(worker); 880 name << " " << worker; 881 882 name.Truncate(222); // reserve space for the uniquer 883 884 // Get rid of annoying characters which are hard to use in the shell. 885 name.ReplaceAll('/','_'); 886 name.ReplaceAll('\'','_'); 887 name.ReplaceAll('"','_'); 888 name.ReplaceAll('!','_'); 889 name.ReplaceAll('<','_'); 890 name.ReplaceAll('>','_'); 891 892 // Remove multiple spaces. 893 while (name.FindFirst(" ") >= 0) 894 name.Replace(" ", " ", 1024); 895 896 int32 uniquer = time(NULL); 897 worker = name; 898 899 int32 tries = 30; 900 bool exists; 901 while ((exists = dir->Contains(worker.String())) && --tries > 0) { 902 srand(rand()); 903 uniquer += (rand() >> 16) - 16384; 904 905 worker = name; 906 worker << ' ' << uniquer; 907 } 908 909 if (exists) 910 printf("could not create mail! (should be: %s)\n", worker.String()); 911 912 BFile file; 913 status_t status = dir->CreateFile(worker.String(), &file); 914 if (status != B_OK) 915 return status; 916 917 if (msg != NULL) 918 msg->SetTo(dir,worker.String()); 919 920 return RenderToRFC822(&file); 921 } 922 923 924 status_t 925 BEmailMessage::Send(bool sendNow) 926 { 927 BMailAccounts accounts; 928 BMailAccountSettings* account = accounts.AccountByID(fAccountID); 929 if (account == NULL || !account->HasOutbound()) { 930 account = accounts.AccountByID( 931 BMailSettings().DefaultOutboundAccount()); 932 if (!account) 933 return B_ERROR; 934 SendViaAccount(account->AccountID()); 935 } 936 937 BString path; 938 if (account->OutboundSettings().FindString("path", &path) != B_OK) { 939 BPath defaultMailOutPath; 940 if (find_directory(B_USER_DIRECTORY, &defaultMailOutPath) != B_OK 941 || defaultMailOutPath.Append("mail/out") != B_OK) 942 path = "/boot/home/mail/out"; 943 else 944 path = defaultMailOutPath.Path(); 945 } 946 947 create_directory(path.String(), 0777); 948 BDirectory directory(path.String()); 949 950 BEntry message; 951 952 status_t status = RenderTo(&directory, &message); 953 if (status >= B_OK && sendNow) { 954 // TODO: check whether or not the internet connection is available 955 BMessenger daemon(B_MAIL_DAEMON_SIGNATURE); 956 if (!daemon.IsValid()) 957 return B_MAIL_NO_DAEMON; 958 959 BMessage msg(kMsgSendMessages); 960 msg.AddInt32("account", fAccountID); 961 BPath path; 962 message.GetPath(&path); 963 msg.AddString("message_path", path.Path()); 964 daemon.SendMessage(&msg); 965 } 966 967 return status; 968 } 969 970 971 void BEmailMessage::_ReservedMessage1() {} 972 void BEmailMessage::_ReservedMessage2() {} 973 void BEmailMessage::_ReservedMessage3() {} 974