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, 640 bool parse_now) 641 { 642 if (BFile *file = dynamic_cast<BFile *>(mail_file)) { 643 file->ReadAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0, &_account_id, 644 sizeof(_account_id)); 645 } 646 647 mail_file->Seek(0,SEEK_END); 648 length = mail_file->Position(); 649 mail_file->Seek(0,SEEK_SET); 650 651 _status = BMailComponent::SetToRFC822(mail_file,length,parse_now); 652 if (_status < B_OK) 653 return _status; 654 655 _body = WhatIsThis(); 656 657 mail_file->Seek(0,SEEK_SET); 658 _status = _body->SetToRFC822(mail_file,length,parse_now); 659 if (_status < B_OK) 660 return _status; 661 662 //------------Move headers that we use to us, everything else to _body 663 const char *name; 664 for (int32 i = 0; (name = _body->HeaderAt(i)) != NULL; i++) { 665 if ((strcasecmp(name,"Subject") != 0) 666 && (strcasecmp(name,"To") != 0) 667 && (strcasecmp(name,"From") != 0) 668 && (strcasecmp(name,"Reply-To") != 0) 669 && (strcasecmp(name,"Cc") != 0) 670 && (strcasecmp(name,"Priority") != 0) 671 && (strcasecmp(name,"X-Priority") != 0) 672 && (strcasecmp(name,"X-Msmail-Priority") != 0) 673 && (strcasecmp(name,"Date") != 0)) { 674 RemoveHeader(name); 675 } 676 } 677 678 _body->RemoveHeader("Subject"); 679 _body->RemoveHeader("To"); 680 _body->RemoveHeader("From"); 681 _body->RemoveHeader("Reply-To"); 682 _body->RemoveHeader("Cc"); 683 _body->RemoveHeader("Priority"); 684 _body->RemoveHeader("X-Priority"); 685 _body->RemoveHeader("X-Msmail-Priority"); 686 _body->RemoveHeader("Date"); 687 688 _num_components = 1; 689 if (BMIMEMultipartMailContainer *container = dynamic_cast<BMIMEMultipartMailContainer *>(_body)) 690 _num_components = container->CountComponents(); 691 692 return B_OK; 693 } 694 695 696 status_t 697 BEmailMessage::RenderToRFC822(BPositionIO *file) 698 { 699 if (_body == NULL) 700 return B_MAIL_INVALID_MAIL; 701 702 // Do real rendering 703 704 if (From() == NULL) { 705 // set the "From:" string 706 SendViaAccount(_account_id); 707 } 708 709 BList recipientList; 710 get_address_list(recipientList, To(), extract_address); 711 get_address_list(recipientList, CC(), extract_address); 712 get_address_list(recipientList, _bcc, extract_address); 713 714 BString recipients; 715 for (int32 i = recipientList.CountItems(); i-- > 0;) { 716 char *address = (char *)recipientList.RemoveItem(0L); 717 718 recipients << '<' << address << '>'; 719 if (i) 720 recipients << ','; 721 722 free(address); 723 } 724 725 // add the date field 726 int32 creationTime = time(NULL); 727 { 728 char date[128]; 729 struct tm tm; 730 localtime_r(&creationTime, &tm); 731 732 size_t length = strftime(date, sizeof(date), 733 "%a, %d %b %Y %H:%M:%S", &tm); 734 735 // GMT offsets are full hours, yes, but you never know :-) 736 snprintf(date + length, sizeof(date) - length, " %+03d%02d", 737 tm.tm_gmtoff / 3600, (tm.tm_gmtoff / 60) % 60); 738 739 SetHeaderField("Date", date); 740 } 741 742 // add a message-id 743 744 // empirical evidence indicates message id must be enclosed in 745 // angle brackets and there must be an "at" symbol in it 746 BString messageID; 747 messageID << "<"; 748 messageID << system_time(); 749 messageID << "-BeMail@"; 750 751 char host[255]; 752 if (gethostname(host, sizeof(host)) < 0 || !host[0]) 753 strcpy(host, "zoidberg"); 754 755 messageID << host; 756 messageID << ">"; 757 758 SetHeaderField("Message-Id", messageID.String()); 759 760 status_t err = BMailComponent::RenderToRFC822(file); 761 if (err < B_OK) 762 return err; 763 764 file->Seek(-2, SEEK_CUR); 765 // Remove division between headers 766 767 err = _body->RenderToRFC822(file); 768 if (err < B_OK) 769 return err; 770 771 // Set the message file's attributes. Do this after the rest of the file 772 // is filled in, in case the daemon attempts to send it before it is ready 773 // (since the daemon may send it when it sees the status attribute getting 774 // set to "Pending"). 775 776 if (BFile *attributed = dynamic_cast <BFile *>(file)) { 777 BNodeInfo(attributed).SetType(B_MAIL_TYPE); 778 779 attributed->WriteAttrString(B_MAIL_ATTR_RECIPIENTS,&recipients); 780 781 BString attr; 782 783 attr = To(); 784 attributed->WriteAttrString(B_MAIL_ATTR_TO,&attr); 785 attr = CC(); 786 attributed->WriteAttrString(B_MAIL_ATTR_CC,&attr); 787 attr = Subject(); 788 attributed->WriteAttrString(B_MAIL_ATTR_SUBJECT,&attr); 789 attr = ReplyTo(); 790 attributed->WriteAttrString(B_MAIL_ATTR_REPLY,&attr); 791 attr = From(); 792 attributed->WriteAttrString(B_MAIL_ATTR_FROM,&attr); 793 if (Priority() != 3 /* Normal is 3 */) { 794 sprintf (attr.LockBuffer (40), "%d", Priority()); 795 attr.UnlockBuffer(-1); 796 attributed->WriteAttrString(B_MAIL_ATTR_PRIORITY,&attr); 797 } 798 attr = "Pending"; 799 attributed->WriteAttrString(B_MAIL_ATTR_STATUS, &attr); 800 attr = "1.0"; 801 attributed->WriteAttrString(B_MAIL_ATTR_MIME, &attr); 802 803 attributed->WriteAttr(B_MAIL_ATTR_ACCOUNT, B_INT32_TYPE, 0, 804 &_account_id, sizeof(int32)); 805 806 attributed->WriteAttr(B_MAIL_ATTR_WHEN, B_TIME_TYPE, 0, &creationTime, 807 sizeof(int32)); 808 int32 flags = B_MAIL_PENDING | B_MAIL_SAVE; 809 attributed->WriteAttr(B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0, &flags, 810 sizeof(int32)); 811 812 attributed->WriteAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0, 813 &_account_id, sizeof(int32)); 814 } 815 816 return B_OK; 817 } 818 819 820 status_t 821 BEmailMessage::RenderTo(BDirectory *dir, BEntry *msg) 822 { 823 time_t currentTime; 824 char numericDateString [40]; 825 struct tm timeFields; 826 BString worker; 827 828 // Generate a file name for the outgoing message. See also 829 // FolderFilter::ProcessMailMessage which does something similar for 830 // incoming messages. 831 832 BString name = Subject(); 833 SubjectToThread (name); // Extract the core subject words. 834 if (name.Length() <= 0) 835 name = "No Subject"; 836 if (name[0] == '.') 837 name.Prepend ("_"); // Avoid hidden files, starting with a dot. 838 839 // Convert the date into a year-month-day fixed digit width format, so that 840 // sorting by file name will give all the messages with the same subject in 841 // order of date. 842 time (¤tTime); 843 localtime_r (¤tTime, &timeFields); 844 sprintf (numericDateString, "%04d%02d%02d%02d%02d%02d", 845 timeFields.tm_year + 1900, 846 timeFields.tm_mon + 1, 847 timeFields.tm_mday, 848 timeFields.tm_hour, 849 timeFields.tm_min, 850 timeFields.tm_sec); 851 name << " " << numericDateString; 852 853 worker = From(); 854 extract_address_name(worker); 855 name << " " << worker; 856 857 name.Truncate(222); // reserve space for the uniquer 858 859 // Get rid of annoying characters which are hard to use in the shell. 860 name.ReplaceAll('/','_'); 861 name.ReplaceAll('\'','_'); 862 name.ReplaceAll('"','_'); 863 name.ReplaceAll('!','_'); 864 name.ReplaceAll('<','_'); 865 name.ReplaceAll('>','_'); 866 while (name.FindFirst(" ") >= 0) // Remove multiple spaces. 867 name.Replace(" " /* Old */, " " /* New */, 1024 /* Count */); 868 869 int32 uniquer = time(NULL); 870 worker = name; 871 872 int32 tries = 30; 873 bool exists; 874 while (((exists = dir->Contains(worker.String())) == true) /* pacify mwcc */ && 875 (--tries > 0)) { 876 srand(rand()); 877 uniquer += (rand() >> 16) - 16384; 878 879 worker = name; 880 worker << ' ' << uniquer; 881 } 882 883 if (exists) 884 printf("could not create mail! (should be: %s)\n", worker.String()); 885 886 BFile file; 887 status_t status = dir->CreateFile(worker.String(), &file); 888 if (status < B_OK) 889 return status; 890 891 if (msg != NULL) 892 msg->SetTo(dir,worker.String()); 893 894 return RenderToRFC822(&file); 895 } 896 897 898 status_t 899 BEmailMessage::Send(bool send_now) 900 { 901 BMailAccounts accounts; 902 BMailAccountSettings* account = accounts.AccountByID(_account_id); 903 if (!account || !account->HasOutbound()) { 904 account = accounts.AccountByID( 905 BMailSettings().DefaultOutboundAccount()); 906 if (!account) 907 return B_ERROR; 908 SendViaAccount(account->AccountID()); 909 } 910 911 BString path; 912 if (account->OutboundSettings().Settings().FindString("path", &path) 913 != B_OK) { 914 BPath defaultMailOutPath; 915 if (find_directory(B_USER_DIRECTORY, &defaultMailOutPath) != B_OK 916 || defaultMailOutPath.Append("mail/out") != B_OK) 917 path = "/boot/home/mail/out"; 918 else 919 path = defaultMailOutPath.Path(); 920 } 921 922 create_directory(path.String(), 0777); 923 BDirectory directory(path.String()); 924 925 BEntry message; 926 927 status_t status = RenderTo(&directory, &message); 928 if (status >= B_OK && send_now) { 929 BMailSettings settings_file; 930 if (settings_file.SendOnlyIfPPPUp()) { 931 #ifndef HAIKU_TARGET_PLATFORM_BEOS 932 int s = socket(AF_INET, SOCK_DGRAM, 0); 933 bsppp_status_t ppp_status; 934 935 strcpy(ppp_status.if_name, "ppp0"); 936 if (ioctl(s, BONE_SERIAL_PPP_GET_STATUS, &ppp_status, sizeof(ppp_status)) != 0) { 937 close(s); 938 return B_OK; 939 } else { 940 if (ppp_status.connection_status != BSPPP_CONNECTED) { 941 close(s); 942 return B_OK; 943 } 944 } 945 close(s); 946 #else 947 if (find_thread("tty_thread") <= 0) 948 return B_OK; 949 #endif 950 } 951 952 BMessenger daemon("application/x-vnd.Be-POST"); 953 if (!daemon.IsValid()) 954 return B_MAIL_NO_DAEMON; 955 956 BMessage msg('msnd'); 957 msg.AddInt32("account",_account_id); 958 BPath path; 959 message.GetPath(&path); 960 msg.AddString("message_path",path.Path()); 961 daemon.SendMessage(&msg); 962 } 963 964 return status; 965 } 966 967 void BEmailMessage::_ReservedMessage1() {} 968 void BEmailMessage::_ReservedMessage2() {} 969 void BEmailMessage::_ReservedMessage3() {} 970 971