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