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 <malloc.h> 24 #include <string.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <sys/utsname.h> 28 #include <ctype.h> 29 #include <parsedate.h> 30 31 #ifndef HAIKU_TARGET_PLATFORM_BEOS 32 #include <sys/socket.h> 33 #define BONE_SERIAL_PPP_GET_STATUS 0xbe230501 34 #define BSPPP_CONNECTED 4 35 typedef struct { 36 char if_name[32]; 37 int connection_status; 38 status_t last_error; 39 int connect_speed; 40 } bsppp_status_t; 41 #include <unistd.h> 42 #endif 43 44 class _EXPORT BEmailMessage; 45 46 #include <MailMessage.h> 47 #include <MailAttachment.h> 48 #include <MailSettings.h> 49 #include <MailDaemon.h> 50 #include <mail_util.h> 51 #include <StringList.h> 52 53 //-------Change the following!---------------------- 54 #define mime_boundary "----------Zoidberg-BeMail-temp--------" 55 #define mime_warning "This is a multipart message in MIME format." 56 57 58 BEmailMessage::BEmailMessage(BPositionIO *file, bool own, uint32 defaultCharSet) 59 : 60 BMailContainer (defaultCharSet), 61 fData(NULL), 62 _status(B_NO_ERROR), 63 _bcc(NULL), 64 _num_components(0), 65 _body(NULL), 66 _text_body(NULL) 67 { 68 BMailSettings settings; 69 _chain_id = settings.DefaultOutboundChainID(); 70 71 if (own) 72 fData = file; 73 74 if (file != NULL) 75 SetToRFC822(file,-1); 76 } 77 78 79 BEmailMessage::BEmailMessage(entry_ref *ref, uint32 defaultCharSet) 80 : 81 BMailContainer (defaultCharSet), 82 _bcc(NULL), 83 _num_components(0), 84 _body(NULL), 85 _text_body(NULL) 86 { 87 BMailSettings settings; 88 _chain_id = settings.DefaultOutboundChainID(); 89 90 fData = new BFile(); 91 _status = static_cast<BFile *>(fData)->SetTo(ref,B_READ_ONLY); 92 93 if (_status == B_OK) 94 SetToRFC822(fData,-1); 95 } 96 97 98 BEmailMessage::~BEmailMessage() 99 { 100 if (_bcc != NULL) 101 free(_bcc); 102 103 delete _body; 104 delete fData; 105 } 106 107 108 status_t BEmailMessage::InitCheck() const 109 { 110 return _status; 111 } 112 113 114 BEmailMessage * 115 BEmailMessage::ReplyMessage(mail_reply_to_mode replyTo, bool accountFromMail, 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 header << "CC: " << CC() << '\n'; // Can use CC rather than "Cc" since display only. 186 header << "Subject: " << Subject() << '\n'; 187 header << "Date: " << Date() << "\n\n"; 188 if (_text_body != NULL) 189 header << _text_body->Text() << '\n'; 190 BEmailMessage *message = new BEmailMessage(); 191 message->SetBodyTextTo(header.String()); 192 193 // set the subject 194 BString subject = Subject(); 195 if (subject.IFindFirst("fwd") == B_ERROR 196 && subject.IFindFirst("forward") == B_ERROR 197 && subject.FindFirst("FW") == B_ERROR) 198 subject << " (fwd)"; 199 message->SetSubject(subject.String()); 200 201 if (includeAttachments) { 202 for (int32 i = 0; i < CountComponents(); i++) { 203 BMailComponent *cmpt = GetComponent(i); 204 if (cmpt == _text_body || cmpt == NULL) 205 continue; 206 207 //---I am ashamed to have the written the code between here and the next comment 208 // ... and you still managed to get it wrong ;-)), axeld. 209 // we should really move this stuff into copy constructors 210 // or something like that 211 212 BMallocIO io; 213 cmpt->RenderToRFC822(&io); 214 BMailComponent *clone = cmpt->WhatIsThis(); 215 io.Seek(0, SEEK_SET); 216 clone->SetToRFC822(&io, io.BufferLength(), true); 217 message->AddComponent(clone); 218 //--- 219 } 220 } 221 if (accountFromMail) 222 message->SendViaAccountFrom(this); 223 224 return message; 225 } 226 227 228 const char * 229 BEmailMessage::To() 230 { 231 return HeaderField("To"); 232 } 233 234 235 const char * 236 BEmailMessage::From() 237 { 238 return HeaderField("From"); 239 } 240 241 242 const char * 243 BEmailMessage::ReplyTo() 244 { 245 return HeaderField("Reply-To"); 246 } 247 248 249 const char * 250 BEmailMessage::CC() 251 { 252 return HeaderField("Cc"); // Note case of CC is "Cc" in our internal headers. 253 } 254 255 256 const char * 257 BEmailMessage::Subject() 258 { 259 return HeaderField("Subject"); 260 } 261 262 263 const char * 264 BEmailMessage::Date() 265 { 266 return HeaderField("Date"); 267 } 268 269 int 270 BEmailMessage::Priority() 271 { 272 int priorityNumber; 273 const char *priorityString; 274 275 /* The usual values are a number from 1 to 5, or one of three words: 276 X-Priority: 1 and/or X-MSMail-Priority: High 277 X-Priority: 3 and/or X-MSMail-Priority: Normal 278 X-Priority: 5 and/or X-MSMail-Priority: Low 279 Also plain Priority: is "normal", "urgent" or "non-urgent", see RFC 1327. */ 280 281 priorityString = HeaderField("Priority"); 282 if (priorityString == NULL) 283 priorityString = HeaderField("X-Priority"); 284 if (priorityString == NULL) 285 priorityString = HeaderField("X-Msmail-Priority"); 286 if (priorityString == NULL) 287 return 3; 288 priorityNumber = atoi (priorityString); 289 if (priorityNumber != 0) { 290 if (priorityNumber > 5) 291 priorityNumber = 5; 292 if (priorityNumber < 1) 293 priorityNumber = 1; 294 return priorityNumber; 295 } 296 if (strcasecmp (priorityString, "Low") == 0 || 297 strcasecmp (priorityString, "non-urgent") == 0) 298 return 5; 299 if (strcasecmp (priorityString, "High") == 0 || 300 strcasecmp (priorityString, "urgent") == 0) 301 return 1; 302 return 3; 303 } 304 305 void BEmailMessage::SetSubject(const char *subject, uint32 charset, mail_encoding encoding) { 306 SetHeaderField("Subject", subject, charset, encoding); 307 } 308 309 void BEmailMessage::SetReplyTo(const char *reply_to, uint32 charset, mail_encoding encoding) { 310 SetHeaderField("Reply-To", reply_to, charset, encoding); 311 } 312 313 void BEmailMessage::SetFrom(const char *from, uint32 charset, mail_encoding encoding) { 314 SetHeaderField("From", from, charset, encoding); 315 } 316 317 void BEmailMessage::SetTo(const char *to, uint32 charset, mail_encoding encoding) { 318 SetHeaderField("To", to, charset, encoding); 319 } 320 321 void BEmailMessage::SetCC(const char *cc, uint32 charset, mail_encoding encoding) { 322 // For consistency with our header names, use Cc as the name. 323 SetHeaderField("Cc", cc, charset, encoding); 324 } 325 326 void BEmailMessage::SetBCC(const char *bcc) { 327 if (_bcc != NULL) 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 int32 BEmailMessage::CountComponents() const { 528 return _num_components; 529 } 530 531 void BEmailMessage::Attach(entry_ref *ref, bool includeAttributes) 532 { 533 if (includeAttributes) 534 AddComponent(new BAttributedMailAttachment(ref)); 535 else 536 AddComponent(new BSimpleMailAttachment(ref)); 537 } 538 539 bool BEmailMessage::IsComponentAttachment(int32 i) { 540 if ((i >= _num_components) || (_num_components == 0)) 541 return false; 542 543 if (_num_components == 1) 544 return _body->IsAttachment(); 545 546 BMIMEMultipartMailContainer *container = dynamic_cast<BMIMEMultipartMailContainer *>(_body); 547 if (container == NULL) 548 return false; //-----This should never, ever, ever, ever, happen 549 550 BMailComponent *component = container->GetComponent(i); 551 if (component == NULL) 552 return false; 553 return component->IsAttachment(); 554 } 555 556 void BEmailMessage::SetBodyTextTo(const char *text) { 557 if (_text_body == NULL) { 558 _text_body = new BTextMailComponent; 559 AddComponent(_text_body); 560 } 561 562 _text_body->SetText(text); 563 } 564 565 566 BTextMailComponent *BEmailMessage::Body() 567 { 568 if (_text_body == NULL) 569 _text_body = RetrieveTextBody(_body); 570 571 return _text_body; 572 } 573 574 575 const char *BEmailMessage::BodyText() { 576 if (Body() == NULL) 577 return NULL; 578 579 return _text_body->Text(); 580 } 581 582 583 status_t BEmailMessage::SetBody(BTextMailComponent *body) { 584 if (_text_body != NULL) { 585 return B_ERROR; 586 // removing doesn't exist for now 587 // RemoveComponent(_text_body); 588 // delete _text_body; 589 } 590 _text_body = body; 591 AddComponent(_text_body); 592 593 return B_OK; 594 } 595 596 597 BTextMailComponent *BEmailMessage::RetrieveTextBody(BMailComponent *component) 598 { 599 BTextMailComponent *body = dynamic_cast<BTextMailComponent *>(component); 600 if (body != NULL) 601 return body; 602 603 BMIMEMultipartMailContainer *container = dynamic_cast<BMIMEMultipartMailContainer *>(component); 604 if (container != NULL) { 605 for (int32 i = 0; i < container->CountComponents(); i++) { 606 if ((component = container->GetComponent(i)) == NULL) 607 continue; 608 609 switch (component->ComponentType()) 610 { 611 case B_MAIL_PLAIN_TEXT_BODY: 612 // AttributedAttachment returns the MIME type of its contents, so 613 // we have to use dynamic_cast here 614 body = dynamic_cast<BTextMailComponent *>(container->GetComponent(i)); 615 if (body != NULL) 616 return body; 617 break; 618 619 case B_MAIL_MULTIPART_CONTAINER: 620 body = RetrieveTextBody(container->GetComponent(i)); 621 if (body != NULL) 622 return body; 623 break; 624 } 625 } 626 } 627 return NULL; 628 } 629 630 631 status_t 632 BEmailMessage::SetToRFC822(BPositionIO *mail_file, size_t length, bool parse_now) 633 { 634 if (BFile *file = dynamic_cast<BFile *>(mail_file)) 635 file->ReadAttr("MAIL:chain",B_INT32_TYPE,0,&_chain_id,sizeof(_chain_id)); 636 637 mail_file->Seek(0,SEEK_END); 638 length = mail_file->Position(); 639 mail_file->Seek(0,SEEK_SET); 640 641 _status = BMailComponent::SetToRFC822(mail_file,length,parse_now); 642 if (_status < B_OK) 643 return _status; 644 645 _body = WhatIsThis(); 646 647 mail_file->Seek(0,SEEK_SET); 648 _status = _body->SetToRFC822(mail_file,length,parse_now); 649 if (_status < B_OK) 650 return _status; 651 652 //------------Move headers that we use to us, everything else to _body 653 const char *name; 654 for (int32 i = 0; (name = _body->HeaderAt(i)) != NULL; i++) { 655 if ((strcasecmp(name,"Subject") != 0) 656 && (strcasecmp(name,"To") != 0) 657 && (strcasecmp(name,"From") != 0) 658 && (strcasecmp(name,"Reply-To") != 0) 659 && (strcasecmp(name,"Cc") != 0) 660 && (strcasecmp(name,"Priority") != 0) 661 && (strcasecmp(name,"X-Priority") != 0) 662 && (strcasecmp(name,"X-Msmail-Priority") != 0) 663 && (strcasecmp(name,"Date") != 0)) { 664 RemoveHeader(name); 665 } 666 } 667 668 _body->RemoveHeader("Subject"); 669 _body->RemoveHeader("To"); 670 _body->RemoveHeader("From"); 671 _body->RemoveHeader("Reply-To"); 672 _body->RemoveHeader("Cc"); 673 _body->RemoveHeader("Priority"); 674 _body->RemoveHeader("X-Priority"); 675 _body->RemoveHeader("X-Msmail-Priority"); 676 _body->RemoveHeader("Date"); 677 678 _num_components = 1; 679 if (BMIMEMultipartMailContainer *container = dynamic_cast<BMIMEMultipartMailContainer *>(_body)) 680 _num_components = container->CountComponents(); 681 682 return B_OK; 683 } 684 685 686 status_t 687 BEmailMessage::RenderToRFC822(BPositionIO *file) 688 { 689 if (_body == NULL) 690 return B_MAIL_INVALID_MAIL; 691 692 // Do real rendering 693 694 if (From() == NULL) 695 SendViaAccount(_chain_id); //-----Set the from string 696 697 BList recipientList; 698 get_address_list(recipientList, To(), extract_address); 699 get_address_list(recipientList, CC(), extract_address); 700 get_address_list(recipientList, _bcc, extract_address); 701 702 BString recipients; 703 for (int32 i = recipientList.CountItems(); i-- > 0;) { 704 char *address = (char *)recipientList.RemoveItem(0L); 705 706 recipients << '<' << address << '>'; 707 if (i) 708 recipients << ','; 709 710 free(address); 711 } 712 713 // add the date field 714 int32 creationTime = time(NULL); 715 { 716 char date[128]; 717 struct tm tm; 718 localtime_r(&creationTime, &tm); 719 720 strftime(date, 128, "%a, %d %b %Y %H:%M:%S",&tm); 721 722 // GMT offsets are full hours, yes, but you never know :-) 723 if (tm.tm_gmtoff) 724 sprintf(date + strlen(date)," %+03d%02d",tm.tm_gmtoff / 3600,(tm.tm_gmtoff / 60) % 60); 725 726 uint32 length = strlen(date); 727 if (length < sizeof(date) - 5) 728 strftime(date + length, length - sizeof(date), " %Z", &tm); 729 730 SetHeaderField("Date", date); 731 } 732 733 // add a message-id 734 735 // empirical evidence indicates message id must be enclosed in 736 // angle brackets and there must be an "at" symbol in it 737 BString messageID; 738 messageID << "<"; 739 messageID << system_time(); 740 messageID << "-BeMail@"; 741 742 char host[255]; 743 if (gethostname(host, sizeof(host)) < 0 || !host[0]) 744 strcpy(host, "zoidberg"); 745 746 messageID << host; 747 messageID << ">"; 748 749 SetHeaderField("Message-Id", messageID.String()); 750 751 status_t err = BMailComponent::RenderToRFC822(file); 752 if (err < B_OK) 753 return err; 754 755 file->Seek(-2, SEEK_CUR); 756 // Remove division between headers 757 758 err = _body->RenderToRFC822(file); 759 if (err < B_OK) 760 return err; 761 762 // Set the message file's attributes. Do this after the rest of the file 763 // is filled in, in case the daemon attempts to send it before it is ready 764 // (since the daemon may send it when it sees the status attribute getting 765 // set to "Pending"). 766 767 if (BFile *attributed = dynamic_cast <BFile *>(file)) { 768 BNodeInfo(attributed).SetType(B_MAIL_TYPE); 769 770 attributed->WriteAttrString(B_MAIL_ATTR_RECIPIENTS,&recipients); 771 772 BString attr; 773 774 attr = To(); 775 attributed->WriteAttrString(B_MAIL_ATTR_TO,&attr); 776 attr = CC(); 777 attributed->WriteAttrString(B_MAIL_ATTR_CC,&attr); 778 attr = Subject(); 779 attributed->WriteAttrString(B_MAIL_ATTR_SUBJECT,&attr); 780 attr = ReplyTo(); 781 attributed->WriteAttrString(B_MAIL_ATTR_REPLY,&attr); 782 attr = From(); 783 attributed->WriteAttrString(B_MAIL_ATTR_FROM,&attr); 784 if (Priority() != 3 /* Normal is 3 */) { 785 sprintf (attr.LockBuffer (40), "%d", Priority()); 786 attr.UnlockBuffer(-1); 787 attributed->WriteAttrString(B_MAIL_ATTR_PRIORITY,&attr); 788 } 789 attr = "Pending"; 790 attributed->WriteAttrString(B_MAIL_ATTR_STATUS,&attr); 791 attr = "1.0"; 792 attributed->WriteAttrString(B_MAIL_ATTR_MIME,&attr); 793 attr = BMailChain(_chain_id).Name(); 794 attributed->WriteAttrString(B_MAIL_ATTR_ACCOUNT,&attr); 795 796 attributed->WriteAttr(B_MAIL_ATTR_WHEN,B_TIME_TYPE,0,&creationTime,sizeof(int32)); 797 int32 flags = B_MAIL_PENDING | B_MAIL_SAVE; 798 attributed->WriteAttr(B_MAIL_ATTR_FLAGS,B_INT32_TYPE,0,&flags,sizeof(int32)); 799 800 attributed->WriteAttr("MAIL:chain",B_INT32_TYPE,0,&_chain_id,sizeof(int32)); 801 } 802 803 return B_OK; 804 } 805 806 807 status_t 808 BEmailMessage::RenderTo(BDirectory *dir, BEntry *msg) 809 { 810 time_t currentTime; 811 char numericDateString [40]; 812 struct tm timeFields; 813 BString worker; 814 815 // Generate a file name for the outgoing message. See also 816 // FolderFilter::ProcessMailMessage which does something similar for 817 // incoming messages. 818 819 BString name = Subject(); 820 SubjectToThread (name); // Extract the core subject words. 821 if (name.Length() <= 0) 822 name = "No Subject"; 823 if (name[0] == '.') 824 name.Prepend ("_"); // Avoid hidden files, starting with a dot. 825 826 // Convert the date into a year-month-day fixed digit width format, so that 827 // sorting by file name will give all the messages with the same subject in 828 // order of date. 829 time (¤tTime); 830 localtime_r (¤tTime, &timeFields); 831 sprintf (numericDateString, "%04d%02d%02d%02d%02d%02d", 832 timeFields.tm_year + 1900, 833 timeFields.tm_mon + 1, 834 timeFields.tm_mday, 835 timeFields.tm_hour, 836 timeFields.tm_min, 837 timeFields.tm_sec); 838 name << " " << numericDateString; 839 840 worker = From(); 841 extract_address_name(worker); 842 name << " " << worker; 843 844 name.Truncate(222); // reserve space for the uniquer 845 846 // Get rid of annoying characters which are hard to use in the shell. 847 name.ReplaceAll('/','_'); 848 name.ReplaceAll('\'','_'); 849 name.ReplaceAll('"','_'); 850 name.ReplaceAll('!','_'); 851 name.ReplaceAll('<','_'); 852 name.ReplaceAll('>','_'); 853 while (name.FindFirst(" ") >= 0) // Remove multiple spaces. 854 name.Replace(" " /* Old */, " " /* New */, 1024 /* Count */); 855 856 int32 uniquer = time(NULL); 857 worker = name; 858 859 int32 tries = 30; 860 bool exists; 861 while (((exists = dir->Contains(worker.String())) == true) /* pacify mwcc */ && 862 (--tries > 0)) { 863 srand(rand()); 864 uniquer += (rand() >> 16) - 16384; 865 866 worker = name; 867 worker << ' ' << uniquer; 868 } 869 870 if (exists) 871 printf("could not create mail! (should be: %s)\n", worker.String()); 872 873 BFile file; 874 status_t status = dir->CreateFile(worker.String(), &file); 875 if (status < B_OK) 876 return status; 877 878 if (msg != NULL) 879 msg->SetTo(dir,worker.String()); 880 881 return RenderToRFC822(&file); 882 } 883 884 885 status_t 886 BEmailMessage::Send(bool send_now) 887 { 888 BMailChain *via = new BMailChain(_chain_id); 889 if ((via->InitCheck() != B_OK) || (via->ChainDirection() != outbound)) { 890 delete via; 891 via = new BMailChain(BMailSettings().DefaultOutboundChainID()); 892 SendViaAccount(via->ID()); 893 } 894 895 create_directory(via->MetaData()->FindString("path"),0777); 896 BDirectory directory(via->MetaData()->FindString("path")); 897 898 BEntry message; 899 900 status_t status = RenderTo(&directory,&message); 901 delete via; 902 if (status >= B_OK && send_now) { 903 BMailSettings settings_file; 904 if (settings_file.SendOnlyIfPPPUp()) { 905 #ifndef HAIKU_TARGET_PLATFORM_BEOS 906 int s = socket(AF_INET, SOCK_DGRAM, 0); 907 bsppp_status_t ppp_status; 908 909 strcpy(ppp_status.if_name, "ppp0"); 910 if (ioctl(s, BONE_SERIAL_PPP_GET_STATUS, &ppp_status, sizeof(ppp_status)) != 0) { 911 close(s); 912 return B_OK; 913 } else { 914 if (ppp_status.connection_status != BSPPP_CONNECTED) { 915 close(s); 916 return B_OK; 917 } 918 } 919 close(s); 920 #else 921 if (find_thread("tty_thread") <= 0) 922 return B_OK; 923 #endif 924 } 925 926 BMessenger daemon("application/x-vnd.Be-POST"); 927 if (!daemon.IsValid()) 928 return B_MAIL_NO_DAEMON; 929 930 BMessage msg('msnd'); 931 msg.AddInt32("chain",_chain_id); 932 BPath path; 933 message.GetPath(&path); 934 msg.AddString("message_path",path.Path()); 935 daemon.SendMessage(&msg); 936 } 937 938 return status; 939 } 940 941 void BEmailMessage::_ReservedMessage1() {} 942 void BEmailMessage::_ReservedMessage2() {} 943 void BEmailMessage::_ReservedMessage3() {} 944 945