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