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