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 42using 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 50BEmailMessage::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 71BEmailMessage::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 90BEmailMessage::~BEmailMessage() 91{ 92 free(fBCC); 93 94 delete fBody; 95 delete fData; 96} 97 98 99status_t 100BEmailMessage::InitCheck() const 101{ 102 return fStatus; 103} 104 105 106BEmailMessage* 107BEmailMessage::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 177BEmailMessage* 178BEmailMessage::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: " << HeaderField("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 228const char* 229BEmailMessage::To() const 230{ 231 return HeaderField("To"); 232} 233 234 235const char* 236BEmailMessage::From() const 237{ 238 return HeaderField("From"); 239} 240 241 242const char* 243BEmailMessage::ReplyTo() const 244{ 245 return HeaderField("Reply-To"); 246} 247 248 249const char* 250BEmailMessage::CC() const 251{ 252 return HeaderField("Cc"); 253 // Note case of CC is "Cc" in our internal headers. 254} 255 256 257const char* 258BEmailMessage::Subject() const 259{ 260 return HeaderField("Subject"); 261} 262 263 264time_t 265BEmailMessage::Date() const 266{ 267 const char* dateField = HeaderField("Date"); 268 if (dateField == NULL) 269 return -1; 270 271 return ParseDateWithTimeZone(dateField); 272} 273 274 275int 276BEmailMessage::Priority() const 277{ 278 int priorityNumber; 279 const char* priorityString; 280 281 /* The usual values are a number from 1 to 5, or one of three words: 282 X-Priority: 1 and/or X-MSMail-Priority: High 283 X-Priority: 3 and/or X-MSMail-Priority: Normal 284 X-Priority: 5 and/or X-MSMail-Priority: Low 285 Also plain Priority: is "normal", "urgent" or "non-urgent", see RFC 1327. */ 286 287 priorityString = HeaderField("Priority"); 288 if (priorityString == NULL) 289 priorityString = HeaderField("X-Priority"); 290 if (priorityString == NULL) 291 priorityString = HeaderField("X-Msmail-Priority"); 292 if (priorityString == NULL) 293 return 3; 294 priorityNumber = atoi (priorityString); 295 if (priorityNumber != 0) { 296 if (priorityNumber > 5) 297 priorityNumber = 5; 298 if (priorityNumber < 1) 299 priorityNumber = 1; 300 return priorityNumber; 301 } 302 if (strcasecmp (priorityString, "Low") == 0 303 || strcasecmp (priorityString, "non-urgent") == 0) 304 return 5; 305 if (strcasecmp (priorityString, "High") == 0 306 || strcasecmp (priorityString, "urgent") == 0) 307 return 1; 308 return 3; 309} 310 311 312void 313BEmailMessage::SetSubject(const char* subject, uint32 charset, 314 mail_encoding encoding) 315{ 316 SetHeaderField("Subject", subject, charset, encoding); 317} 318 319 320void 321BEmailMessage::SetReplyTo(const char* replyTo, uint32 charset, 322 mail_encoding encoding) 323{ 324 SetHeaderField("Reply-To", replyTo, charset, encoding); 325} 326 327 328void 329BEmailMessage::SetFrom(const char* from, uint32 charset, mail_encoding encoding) 330{ 331 SetHeaderField("From", from, charset, encoding); 332} 333 334 335void 336BEmailMessage::SetTo(const char* to, uint32 charset, mail_encoding encoding) 337{ 338 SetHeaderField("To", to, charset, encoding); 339} 340 341 342void 343BEmailMessage::SetCC(const char* cc, uint32 charset, mail_encoding encoding) 344{ 345 // For consistency with our header names, use Cc as the name. 346 SetHeaderField("Cc", cc, charset, encoding); 347} 348 349 350void 351BEmailMessage::SetBCC(const char* bcc) 352{ 353 free(fBCC); 354 fBCC = strdup(bcc); 355} 356 357 358void 359BEmailMessage::SetPriority(int to) 360{ 361 char tempString[20]; 362 363 if (to < 1) 364 to = 1; 365 if (to > 5) 366 to = 5; 367 sprintf (tempString, "%d", to); 368 SetHeaderField("X-Priority", tempString); 369 if (to <= 2) { 370 SetHeaderField("Priority", "urgent"); 371 SetHeaderField("X-Msmail-Priority", "High"); 372 } else if (to >= 4) { 373 SetHeaderField("Priority", "non-urgent"); 374 SetHeaderField("X-Msmail-Priority", "Low"); 375 } else { 376 SetHeaderField("Priority", "normal"); 377 SetHeaderField("X-Msmail-Priority", "Normal"); 378 } 379} 380 381 382status_t 383BEmailMessage::GetName(char* name, int32 maxLength) const 384{ 385 if (name == NULL || maxLength <= 0) 386 return B_BAD_VALUE; 387 388 if (BFile* file = dynamic_cast<BFile*>(fData)) { 389 status_t status = file->ReadAttr(B_MAIL_ATTR_NAME, B_STRING_TYPE, 0, 390 name, maxLength); 391 name[maxLength - 1] = '\0'; 392 393 return status >= 0 ? B_OK : status; 394 } 395 // TODO: look at From header? But usually there is 396 // a file since only the BeMail GUI calls this. 397 return B_ERROR; 398} 399 400 401status_t 402BEmailMessage::GetName(BString* name) const 403{ 404 char* buffer = name->LockBuffer(B_FILE_NAME_LENGTH); 405 status_t status = GetName(buffer, B_FILE_NAME_LENGTH); 406 name->UnlockBuffer(); 407 408 return status; 409} 410 411 412void 413BEmailMessage::SendViaAccountFrom(BEmailMessage* message) 414{ 415 BString name; 416 if (message->GetAccountName(name) < B_OK) { 417 // just return the message with the default account 418 return; 419 } 420 421 SendViaAccount(name); 422} 423 424 425void 426BEmailMessage::SendViaAccount(const char* accountName) 427{ 428 BMailAccounts accounts; 429 BMailAccountSettings* account = accounts.AccountByName(accountName); 430 if (account != NULL) 431 SendViaAccount(account->AccountID()); 432} 433 434 435void 436BEmailMessage::SendViaAccount(int32 account) 437{ 438 fAccountID = account; 439 440 BMailAccounts accounts; 441 BMailAccountSettings* accountSettings = accounts.AccountByID(fAccountID); 442 443 BString from; 444 if (accountSettings) { 445 from << '\"' << accountSettings->RealName() << "\" <" 446 << accountSettings->ReturnAddress() << '>'; 447 } 448 SetFrom(from); 449} 450 451 452int32 453BEmailMessage::Account() const 454{ 455 return fAccountID; 456} 457 458 459status_t 460BEmailMessage::GetAccountName(BString& accountName) const 461{ 462 BFile* file = dynamic_cast<BFile*>(fData); 463 if (file == NULL) 464 return B_ERROR; 465 466 int32 accountID; 467 size_t read = file->ReadAttr(B_MAIL_ATTR_ACCOUNT, B_INT32_TYPE, 0, 468 &accountID, sizeof(int32)); 469 if (read < sizeof(int32)) 470 return B_ERROR; 471 472 BMailAccounts accounts; 473 BMailAccountSettings* account = accounts.AccountByID(accountID); 474 if (account != NULL) 475 accountName = account->Name(); 476 else 477 accountName = ""; 478 479 return B_OK; 480} 481 482 483status_t 484BEmailMessage::AddComponent(BMailComponent* component) 485{ 486 status_t status = B_OK; 487 488 if (fComponentCount == 0) 489 fBody = component; 490 else if (fComponentCount == 1) { 491 BMIMEMultipartMailContainer *container 492 = new BMIMEMultipartMailContainer( 493 mime_boundary, mime_warning, _charSetForTextDecoding); 494 status = container->AddComponent(fBody); 495 if (status == B_OK) 496 status = container->AddComponent(component); 497 fBody = container; 498 } else { 499 BMIMEMultipartMailContainer* container 500 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody); 501 if (container == NULL) 502 return B_MISMATCHED_VALUES; 503 504 status = container->AddComponent(component); 505 } 506 507 if (status == B_OK) 508 fComponentCount++; 509 return status; 510} 511 512 513status_t 514BEmailMessage::RemoveComponent(BMailComponent* /*component*/) 515{ 516 // not yet implemented 517 // BeMail/Enclosures.cpp:169: contains a warning about this fact 518 return B_ERROR; 519} 520 521 522status_t 523BEmailMessage::RemoveComponent(int32 /*index*/) 524{ 525 // not yet implemented 526 return B_ERROR; 527} 528 529 530BMailComponent* 531BEmailMessage::GetComponent(int32 i, bool parseNow) 532{ 533 if (BMIMEMultipartMailContainer* container 534 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody)) 535 return container->GetComponent(i, parseNow); 536 537 if (i < fComponentCount) 538 return fBody; 539 540 return NULL; 541} 542 543 544int32 545BEmailMessage::CountComponents() const 546{ 547 return fComponentCount; 548} 549 550 551void 552BEmailMessage::Attach(entry_ref* ref, bool includeAttributes) 553{ 554 if (includeAttributes) 555 AddComponent(new BAttributedMailAttachment(ref)); 556 else 557 AddComponent(new BSimpleMailAttachment(ref)); 558} 559 560 561bool 562BEmailMessage::IsComponentAttachment(int32 i) 563{ 564 if ((i >= fComponentCount) || (fComponentCount == 0)) 565 return false; 566 567 if (fComponentCount == 1) 568 return fBody->IsAttachment(); 569 570 BMIMEMultipartMailContainer* container 571 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody); 572 if (container == NULL) 573 return false; 574 575 BMailComponent* component = container->GetComponent(i); 576 if (component == NULL) 577 return false; 578 579 return component->IsAttachment(); 580} 581 582 583void 584BEmailMessage::SetBodyTextTo(const char* text) 585{ 586 if (fTextBody == NULL) { 587 fTextBody = new BTextMailComponent; 588 AddComponent(fTextBody); 589 } 590 591 fTextBody->SetText(text); 592} 593 594 595BTextMailComponent* 596BEmailMessage::Body() 597{ 598 if (fTextBody == NULL) 599 fTextBody = _RetrieveTextBody(fBody); 600 601 return fTextBody; 602} 603 604 605const char* 606BEmailMessage::BodyText() 607{ 608 if (Body() == NULL) 609 return NULL; 610 611 return fTextBody->Text(); 612} 613 614 615status_t 616BEmailMessage::SetBody(BTextMailComponent* body) 617{ 618 if (fTextBody != NULL) { 619 return B_ERROR; 620// removing doesn't exist for now 621// RemoveComponent(fTextBody); 622// delete fTextBody; 623 } 624 fTextBody = body; 625 AddComponent(fTextBody); 626 627 return B_OK; 628} 629 630 631BTextMailComponent* 632BEmailMessage::_RetrieveTextBody(BMailComponent* component) 633{ 634 BTextMailComponent* body = dynamic_cast<BTextMailComponent*>(component); 635 if (body != NULL) 636 return body; 637 638 BMIMEMultipartMailContainer* container 639 = dynamic_cast<BMIMEMultipartMailContainer*>(component); 640 if (container != NULL) { 641 for (int32 i = 0; i < container->CountComponents(); i++) { 642 if ((component = container->GetComponent(i)) == NULL) 643 continue; 644 645 switch (component->ComponentType()) { 646 case B_MAIL_PLAIN_TEXT_BODY: 647 // AttributedAttachment returns the MIME type of its 648 // contents, so we have to use dynamic_cast here 649 body = dynamic_cast<BTextMailComponent*>( 650 container->GetComponent(i)); 651 if (body != NULL) 652 return body; 653 break; 654 655 case B_MAIL_MULTIPART_CONTAINER: 656 body = _RetrieveTextBody(container->GetComponent(i)); 657 if (body != NULL) 658 return body; 659 break; 660 } 661 } 662 } 663 return NULL; 664} 665 666 667status_t 668BEmailMessage::SetToRFC822(BPositionIO* mailFile, size_t length, 669 bool parseNow) 670{ 671 if (BFile* file = dynamic_cast<BFile*>(mailFile)) { 672 file->ReadAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0, &fAccountID, 673 sizeof(fAccountID)); 674 } 675 676 mailFile->Seek(0, SEEK_END); 677 length = mailFile->Position(); 678 mailFile->Seek(0, SEEK_SET); 679 680 fStatus = BMailComponent::SetToRFC822(mailFile, length, parseNow); 681 if (fStatus < B_OK) 682 return fStatus; 683 684 fBody = WhatIsThis(); 685 686 mailFile->Seek(0, SEEK_SET); 687 fStatus = fBody->SetToRFC822(mailFile, length, parseNow); 688 if (fStatus < B_OK) 689 return fStatus; 690 691 // Move headers that we use to us, everything else to fBody 692 const char* name; 693 for (int32 i = 0; (name = fBody->HeaderAt(i)) != NULL; i++) { 694 if (strcasecmp(name, "Subject") != 0 695 && strcasecmp(name, "To") != 0 696 && strcasecmp(name, "From") != 0 697 && strcasecmp(name, "Reply-To") != 0 698 && strcasecmp(name, "Cc") != 0 699 && strcasecmp(name, "Priority") != 0 700 && strcasecmp(name, "X-Priority") != 0 701 && strcasecmp(name, "X-Msmail-Priority") != 0 702 && strcasecmp(name, "Date") != 0) { 703 RemoveHeader(name); 704 } 705 } 706 707 fBody->RemoveHeader("Subject"); 708 fBody->RemoveHeader("To"); 709 fBody->RemoveHeader("From"); 710 fBody->RemoveHeader("Reply-To"); 711 fBody->RemoveHeader("Cc"); 712 fBody->RemoveHeader("Priority"); 713 fBody->RemoveHeader("X-Priority"); 714 fBody->RemoveHeader("X-Msmail-Priority"); 715 fBody->RemoveHeader("Date"); 716 717 fComponentCount = 1; 718 if (BMIMEMultipartMailContainer* container 719 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody)) 720 fComponentCount = container->CountComponents(); 721 722 return B_OK; 723} 724 725 726status_t 727BEmailMessage::RenderToRFC822(BPositionIO* file) 728{ 729 if (fBody == NULL) 730 return B_MAIL_INVALID_MAIL; 731 732 // Do real rendering 733 734 if (From() == NULL) { 735 // set the "From:" string 736 SendViaAccount(fAccountID); 737 } 738 739 BList recipientList; 740 get_address_list(recipientList, To(), extract_address); 741 get_address_list(recipientList, CC(), extract_address); 742 get_address_list(recipientList, fBCC, extract_address); 743 744 BString recipients; 745 for (int32 i = recipientList.CountItems(); i-- > 0;) { 746 char *address = (char *)recipientList.RemoveItem((int32)0); 747 748 recipients << '<' << address << '>'; 749 if (i) 750 recipients << ','; 751 752 free(address); 753 } 754 755 // add the date field 756 time_t creationTime = time(NULL); 757 { 758 char date[128]; 759 struct tm tm; 760 localtime_r(&creationTime, &tm); 761 762 size_t length = strftime(date, sizeof(date), 763 "%a, %d %b %Y %H:%M:%S", &tm); 764 765 // GMT offsets are full hours, yes, but you never know :-) 766 snprintf(date + length, sizeof(date) - length, " %+03d%02d", 767 tm.tm_gmtoff / 3600, (tm.tm_gmtoff / 60) % 60); 768 769 SetHeaderField("Date", date); 770 } 771 772 // add a message-id 773 774 // empirical evidence indicates message id must be enclosed in 775 // angle brackets and there must be an "at" symbol in it 776 BString messageID; 777 messageID << "<"; 778 messageID << system_time(); 779 messageID << "-BeMail@"; 780 781 char host[255]; 782 if (gethostname(host, sizeof(host)) < 0 || !host[0]) 783 strcpy(host, "zoidberg"); 784 785 messageID << host; 786 messageID << ">"; 787 788 SetHeaderField("Message-Id", messageID.String()); 789 790 status_t err = BMailComponent::RenderToRFC822(file); 791 if (err < B_OK) 792 return err; 793 794 file->Seek(-2, SEEK_CUR); 795 // Remove division between headers 796 797 err = fBody->RenderToRFC822(file); 798 if (err < B_OK) 799 return err; 800 801 // Set the message file's attributes. Do this after the rest of the file 802 // is filled in, in case the daemon attempts to send it before it is ready 803 // (since the daemon may send it when it sees the status attribute getting 804 // set to "Pending"). 805 806 if (BFile* attributed = dynamic_cast <BFile*>(file)) { 807 BNodeInfo(attributed).SetType(B_MAIL_TYPE); 808 809 attributed->WriteAttrString(B_MAIL_ATTR_RECIPIENTS,&recipients); 810 811 BString attr; 812 813 attr = To(); 814 attributed->WriteAttrString(B_MAIL_ATTR_TO, &attr); 815 attr = CC(); 816 attributed->WriteAttrString(B_MAIL_ATTR_CC, &attr); 817 attr = Subject(); 818 attributed->WriteAttrString(B_MAIL_ATTR_SUBJECT, &attr); 819 attr = ReplyTo(); 820 attributed->WriteAttrString(B_MAIL_ATTR_REPLY, &attr); 821 attr = From(); 822 attributed->WriteAttrString(B_MAIL_ATTR_FROM, &attr); 823 if (Priority() != 3 /* Normal is 3 */) { 824 sprintf(attr.LockBuffer(40), "%d", Priority()); 825 attr.UnlockBuffer(-1); 826 attributed->WriteAttrString(B_MAIL_ATTR_PRIORITY, &attr); 827 } 828 attr = "Pending"; 829 attributed->WriteAttrString(B_MAIL_ATTR_STATUS, &attr); 830 attr = "1.0"; 831 attributed->WriteAttrString(B_MAIL_ATTR_MIME, &attr); 832 833 attributed->WriteAttr(B_MAIL_ATTR_ACCOUNT, B_INT32_TYPE, 0, 834 &fAccountID, sizeof(int32)); 835 836 attributed->WriteAttr(B_MAIL_ATTR_WHEN, B_TIME_TYPE, 0, &creationTime, 837 sizeof(int32)); 838 int32 flags = B_MAIL_PENDING | B_MAIL_SAVE; 839 attributed->WriteAttr(B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0, &flags, 840 sizeof(int32)); 841 842 attributed->WriteAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0, 843 &fAccountID, sizeof(int32)); 844 } 845 846 return B_OK; 847} 848 849 850status_t 851BEmailMessage::RenderTo(BDirectory* dir, BEntry* msg) 852{ 853 time_t currentTime; 854 char numericDateString[40]; 855 struct tm timeFields; 856 BString worker; 857 858 // Generate a file name for the outgoing message. See also 859 // FolderFilter::ProcessMailMessage which does something similar for 860 // incoming messages. 861 862 BString name = Subject(); 863 SubjectToThread(name); 864 // Extract the core subject words. 865 if (name.Length() <= 0) 866 name = "No Subject"; 867 if (name[0] == '.') { 868 // Avoid hidden files, starting with a dot. 869 name.Prepend("_"); 870 } 871 872 // Convert the date into a year-month-day fixed digit width format, so that 873 // sorting by file name will give all the messages with the same subject in 874 // order of date. 875 time (¤tTime); 876 localtime_r (¤tTime, &timeFields); 877 sprintf (numericDateString, "%04d%02d%02d%02d%02d%02d", 878 timeFields.tm_year + 1900, timeFields.tm_mon + 1, timeFields.tm_mday, 879 timeFields.tm_hour, timeFields.tm_min, timeFields.tm_sec); 880 name << " " << numericDateString; 881 882 worker = From(); 883 extract_address_name(worker); 884 name << " " << worker; 885 886 name.Truncate(222); // reserve space for the uniquer 887 888 // Get rid of annoying characters which are hard to use in the shell. 889 name.ReplaceAll('/','_'); 890 name.ReplaceAll('\'','_'); 891 name.ReplaceAll('"','_'); 892 name.ReplaceAll('!','_'); 893 name.ReplaceAll('<','_'); 894 name.ReplaceAll('>','_'); 895 896 // Remove multiple spaces. 897 while (name.FindFirst(" ") >= 0) 898 name.Replace(" ", " ", 1024); 899 900 int32 uniquer = time(NULL); 901 worker = name; 902 903 int32 tries = 30; 904 bool exists; 905 while ((exists = dir->Contains(worker.String())) && --tries > 0) { 906 srand(rand()); 907 uniquer += (rand() >> 16) - 16384; 908 909 worker = name; 910 worker << ' ' << uniquer; 911 } 912 913 if (exists) 914 printf("could not create mail! (should be: %s)\n", worker.String()); 915 916 BFile file; 917 status_t status = dir->CreateFile(worker.String(), &file); 918 if (status != B_OK) 919 return status; 920 921 if (msg != NULL) 922 msg->SetTo(dir,worker.String()); 923 924 return RenderToRFC822(&file); 925} 926 927 928status_t 929BEmailMessage::Send(bool sendNow) 930{ 931 BMailAccounts accounts; 932 BMailAccountSettings* account = accounts.AccountByID(fAccountID); 933 if (account == NULL || !account->HasOutbound()) { 934 account = accounts.AccountByID( 935 BMailSettings().DefaultOutboundAccount()); 936 if (!account) 937 return B_ERROR; 938 SendViaAccount(account->AccountID()); 939 } 940 941 BString path; 942 if (account->OutboundSettings().FindString("path", &path) != B_OK) { 943 BPath defaultMailOutPath; 944 if (find_directory(B_USER_DIRECTORY, &defaultMailOutPath) != B_OK 945 || defaultMailOutPath.Append("mail/out") != B_OK) 946 path = "/boot/home/mail/out"; 947 else 948 path = defaultMailOutPath.Path(); 949 } 950 951 create_directory(path.String(), 0777); 952 BDirectory directory(path.String()); 953 954 BEntry message; 955 956 status_t status = RenderTo(&directory, &message); 957 if (status >= B_OK && sendNow) { 958 // TODO: check whether or not the internet connection is available 959 BMessenger daemon(B_MAIL_DAEMON_SIGNATURE); 960 if (!daemon.IsValid()) 961 return B_MAIL_NO_DAEMON; 962 963 BMessage msg(kMsgSendMessages); 964 msg.AddInt32("account", fAccountID); 965 BPath path; 966 message.GetPath(&path); 967 msg.AddString("message_path", path.Path()); 968 daemon.SendMessage(&msg); 969 } 970 971 return status; 972} 973 974 975void BEmailMessage::_ReservedMessage1() {} 976void BEmailMessage::_ReservedMessage2() {} 977void BEmailMessage::_ReservedMessage3() {} 978