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