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