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