BPicture: Fix archive constructor.
[haiku.git] / src / kits / mail / MailMessage.cpp
bloba70386da9b5f8bff992fb01f14107b60e629aec2
1 /*
2 * Copyright 2007-2015, Haiku Inc. All Rights Reserved.
3 * Copyright 2001-2004 Dr. Zoidberg Enterprises. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 */
9 //! The main general purpose mail message class
12 #include <MailMessage.h>
14 #include <ctype.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/utsname.h>
20 #include <parsedate.h>
22 #include <Directory.h>
23 #include <E-mail.h>
24 #include <Entry.h>
25 #include <File.h>
26 #include <FindDirectory.h>
27 #include <List.h>
28 #include <MailAttachment.h>
29 #include <MailDaemon.h>
30 #include <MailSettings.h>
31 #include <Messenger.h>
32 #include <netdb.h>
33 #include <NodeInfo.h>
34 #include <Path.h>
35 #include <String.h>
36 #include <StringList.h>
38 #include <MailPrivate.h>
39 #include <mail_util.h>
42 using namespace BPrivate;
45 //-------Change the following!----------------------
46 #define mime_boundary "----------Zoidberg-BeMail-temp--------"
47 #define mime_warning "This is a multipart message in MIME format."
50 BEmailMessage::BEmailMessage(BPositionIO* file, bool own, uint32 defaultCharSet)
52 BMailContainer(defaultCharSet),
53 fData(NULL),
54 fStatus(B_NO_ERROR),
55 fBCC(NULL),
56 fComponentCount(0),
57 fBody(NULL),
58 fTextBody(NULL)
60 BMailSettings settings;
61 fAccountID = settings.DefaultOutboundAccount();
63 if (own)
64 fData = file;
66 if (file != NULL)
67 SetToRFC822(file, ~0L);
71 BEmailMessage::BEmailMessage(const entry_ref* ref, uint32 defaultCharSet)
73 BMailContainer(defaultCharSet),
74 fBCC(NULL),
75 fComponentCount(0),
76 fBody(NULL),
77 fTextBody(NULL)
79 BMailSettings settings;
80 fAccountID = settings.DefaultOutboundAccount();
82 fData = new BFile();
83 fStatus = static_cast<BFile*>(fData)->SetTo(ref, B_READ_ONLY);
85 if (fStatus == B_OK)
86 SetToRFC822(fData, ~0L);
90 BEmailMessage::~BEmailMessage()
92 free(fBCC);
94 delete fBody;
95 delete fData;
99 status_t
100 BEmailMessage::InitCheck() const
102 return fStatus;
106 BEmailMessage*
107 BEmailMessage::ReplyMessage(mail_reply_to_mode replyTo, bool accountFromMail,
108 const char* quoteStyle)
110 BEmailMessage* reply = new BEmailMessage;
112 // Set ReplyTo:
114 if (replyTo == B_MAIL_REPLY_TO_ALL) {
115 reply->SetTo(From());
117 BList list;
118 get_address_list(list, CC(), extract_address);
119 get_address_list(list, To(), extract_address);
121 // Filter out the sender
122 BMailAccounts accounts;
123 BMailAccountSettings* account = accounts.AccountByID(Account());
124 BString sender;
125 if (account != NULL)
126 sender = account->ReturnAddress();
127 extract_address(sender);
129 BString cc;
131 for (int32 i = list.CountItems(); i-- > 0;) {
132 char* address = (char*)list.RemoveItem((int32)0);
134 // Add everything which is not the sender and not already in the
135 // list
136 if (sender.ICompare(address) && cc.FindFirst(address) < 0) {
137 if (cc.Length() > 0)
138 cc << ", ";
140 cc << address;
143 free(address);
146 if (cc.Length() > 0)
147 reply->SetCC(cc.String());
148 } else if (replyTo == B_MAIL_REPLY_TO_SENDER || ReplyTo() == NULL)
149 reply->SetTo(From());
150 else
151 reply->SetTo(ReplyTo());
153 // Set special "In-Reply-To:" header (used for threading)
154 const char* messageID = fBody ? fBody->HeaderField("Message-Id") : NULL;
155 if (messageID != NULL)
156 reply->SetHeaderField("In-Reply-To", messageID);
158 // quote body text
159 reply->SetBodyTextTo(BodyText());
160 if (quoteStyle)
161 reply->Body()->Quote(quoteStyle);
163 // Set the subject (and add a "Re:" if needed)
164 BString string = Subject();
165 if (string.ICompare("re:", 3) != 0)
166 string.Prepend("Re: ");
167 reply->SetSubject(string.String());
169 // set the matching outbound chain
170 if (accountFromMail)
171 reply->SendViaAccountFrom(this);
173 return reply;
177 BEmailMessage*
178 BEmailMessage::ForwardMessage(bool accountFromMail, bool includeAttachments)
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';
187 header << "Subject: " << Subject() << '\n';
188 header << "Date: " << Date() << "\n\n";
189 if (fTextBody != NULL)
190 header << fTextBody->Text() << '\n';
191 BEmailMessage *message = new BEmailMessage();
192 message->SetBodyTextTo(header.String());
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());
202 if (includeAttachments) {
203 for (int32 i = 0; i < CountComponents(); i++) {
204 BMailComponent* component = GetComponent(i);
205 if (component == fTextBody || component == NULL)
206 continue;
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
213 BMallocIO io;
214 component->RenderToRFC822(&io);
215 BMailComponent* clone = component->WhatIsThis();
216 io.Seek(0, SEEK_SET);
217 clone->SetToRFC822(&io, io.BufferLength(), true);
218 message->AddComponent(clone);
221 if (accountFromMail)
222 message->SendViaAccountFrom(this);
224 return message;
228 const char*
229 BEmailMessage::To() const
231 return HeaderField("To");
235 const char*
236 BEmailMessage::From() const
238 return HeaderField("From");
242 const char*
243 BEmailMessage::ReplyTo() const
245 return HeaderField("Reply-To");
249 const char*
250 BEmailMessage::CC() const
252 return HeaderField("Cc");
253 // Note case of CC is "Cc" in our internal headers.
257 const char*
258 BEmailMessage::Subject() const
260 return HeaderField("Subject");
264 time_t
265 BEmailMessage::Date() const
267 const char* dateField = HeaderField("Date");
268 if (dateField == NULL)
269 return -1;
271 return ParseDateWithTimeZone(dateField);
276 BEmailMessage::Priority() const
278 int priorityNumber;
279 const char* priorityString;
281 /* The usual values are a number from 1 to 5, or one of three words:
282 X-Priority: 1 and/or X-MSMail-Priority: High
283 X-Priority: 3 and/or X-MSMail-Priority: Normal
284 X-Priority: 5 and/or X-MSMail-Priority: Low
285 Also plain Priority: is "normal", "urgent" or "non-urgent", see RFC 1327. */
287 priorityString = HeaderField("Priority");
288 if (priorityString == NULL)
289 priorityString = HeaderField("X-Priority");
290 if (priorityString == NULL)
291 priorityString = HeaderField("X-Msmail-Priority");
292 if (priorityString == NULL)
293 return 3;
294 priorityNumber = atoi (priorityString);
295 if (priorityNumber != 0) {
296 if (priorityNumber > 5)
297 priorityNumber = 5;
298 if (priorityNumber < 1)
299 priorityNumber = 1;
300 return priorityNumber;
302 if (strcasecmp (priorityString, "Low") == 0
303 || strcasecmp (priorityString, "non-urgent") == 0)
304 return 5;
305 if (strcasecmp (priorityString, "High") == 0
306 || strcasecmp (priorityString, "urgent") == 0)
307 return 1;
308 return 3;
312 void
313 BEmailMessage::SetSubject(const char* subject, uint32 charset,
314 mail_encoding encoding)
316 SetHeaderField("Subject", subject, charset, encoding);
320 void
321 BEmailMessage::SetReplyTo(const char* replyTo, uint32 charset,
322 mail_encoding encoding)
324 SetHeaderField("Reply-To", replyTo, charset, encoding);
328 void
329 BEmailMessage::SetFrom(const char* from, uint32 charset, mail_encoding encoding)
331 SetHeaderField("From", from, charset, encoding);
335 void
336 BEmailMessage::SetTo(const char* to, uint32 charset, mail_encoding encoding)
338 SetHeaderField("To", to, charset, encoding);
342 void
343 BEmailMessage::SetCC(const char* cc, uint32 charset, mail_encoding encoding)
345 // For consistency with our header names, use Cc as the name.
346 SetHeaderField("Cc", cc, charset, encoding);
350 void
351 BEmailMessage::SetBCC(const char* bcc)
353 free(fBCC);
354 fBCC = strdup(bcc);
358 void
359 BEmailMessage::SetPriority(int to)
361 char tempString[20];
363 if (to < 1)
364 to = 1;
365 if (to > 5)
366 to = 5;
367 sprintf (tempString, "%d", to);
368 SetHeaderField("X-Priority", tempString);
369 if (to <= 2) {
370 SetHeaderField("Priority", "urgent");
371 SetHeaderField("X-Msmail-Priority", "High");
372 } else if (to >= 4) {
373 SetHeaderField("Priority", "non-urgent");
374 SetHeaderField("X-Msmail-Priority", "Low");
375 } else {
376 SetHeaderField("Priority", "normal");
377 SetHeaderField("X-Msmail-Priority", "Normal");
382 status_t
383 BEmailMessage::GetName(char* name, int32 maxLength) const
385 if (name == NULL || maxLength <= 0)
386 return B_BAD_VALUE;
388 if (BFile* file = dynamic_cast<BFile*>(fData)) {
389 status_t status = file->ReadAttr(B_MAIL_ATTR_NAME, B_STRING_TYPE, 0,
390 name, maxLength);
391 name[maxLength - 1] = '\0';
393 return status >= 0 ? B_OK : status;
395 // TODO: look at From header? But usually there is
396 // a file since only the BeMail GUI calls this.
397 return B_ERROR;
401 status_t
402 BEmailMessage::GetName(BString* name) const
404 char* buffer = name->LockBuffer(B_FILE_NAME_LENGTH);
405 status_t status = GetName(buffer, B_FILE_NAME_LENGTH);
406 name->UnlockBuffer();
408 return status;
412 void
413 BEmailMessage::SendViaAccountFrom(BEmailMessage* message)
415 BString name;
416 if (message->GetAccountName(name) < B_OK) {
417 // just return the message with the default account
418 return;
421 SendViaAccount(name);
425 void
426 BEmailMessage::SendViaAccount(const char* accountName)
428 BMailAccounts accounts;
429 BMailAccountSettings* account = accounts.AccountByName(accountName);
430 if (account != NULL)
431 SendViaAccount(account->AccountID());
435 void
436 BEmailMessage::SendViaAccount(int32 account)
438 fAccountID = account;
440 BMailAccounts accounts;
441 BMailAccountSettings* accountSettings = accounts.AccountByID(fAccountID);
443 BString from;
444 if (accountSettings) {
445 from << '\"' << accountSettings->RealName() << "\" <"
446 << accountSettings->ReturnAddress() << '>';
448 SetFrom(from);
452 int32
453 BEmailMessage::Account() const
455 return fAccountID;
459 status_t
460 BEmailMessage::GetAccountName(BString& accountName) const
462 BFile* file = dynamic_cast<BFile*>(fData);
463 if (file == NULL)
464 return B_ERROR;
466 int32 accountID;
467 size_t read = file->ReadAttr(B_MAIL_ATTR_ACCOUNT, B_INT32_TYPE, 0,
468 &accountID, sizeof(int32));
469 if (read < sizeof(int32))
470 return B_ERROR;
472 BMailAccounts accounts;
473 BMailAccountSettings* account = accounts.AccountByID(accountID);
474 if (account != NULL)
475 accountName = account->Name();
476 else
477 accountName = "";
479 return B_OK;
483 status_t
484 BEmailMessage::AddComponent(BMailComponent* component)
486 status_t status = B_OK;
488 if (fComponentCount == 0)
489 fBody = component;
490 else if (fComponentCount == 1) {
491 BMIMEMultipartMailContainer *container
492 = new BMIMEMultipartMailContainer(
493 mime_boundary, mime_warning, _charSetForTextDecoding);
494 status = container->AddComponent(fBody);
495 if (status == B_OK)
496 status = container->AddComponent(component);
497 fBody = container;
498 } else {
499 BMIMEMultipartMailContainer* container
500 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody);
501 if (container == NULL)
502 return B_MISMATCHED_VALUES;
504 status = container->AddComponent(component);
507 if (status == B_OK)
508 fComponentCount++;
509 return status;
513 status_t
514 BEmailMessage::RemoveComponent(BMailComponent* /*component*/)
516 // not yet implemented
517 // BeMail/Enclosures.cpp:169: contains a warning about this fact
518 return B_ERROR;
522 status_t
523 BEmailMessage::RemoveComponent(int32 /*index*/)
525 // not yet implemented
526 return B_ERROR;
530 BMailComponent*
531 BEmailMessage::GetComponent(int32 i, bool parseNow)
533 if (BMIMEMultipartMailContainer* container
534 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody))
535 return container->GetComponent(i, parseNow);
537 if (i < fComponentCount)
538 return fBody;
540 return NULL;
544 int32
545 BEmailMessage::CountComponents() const
547 return fComponentCount;
551 void
552 BEmailMessage::Attach(entry_ref* ref, bool includeAttributes)
554 if (includeAttributes)
555 AddComponent(new BAttributedMailAttachment(ref));
556 else
557 AddComponent(new BSimpleMailAttachment(ref));
561 bool
562 BEmailMessage::IsComponentAttachment(int32 i)
564 if ((i >= fComponentCount) || (fComponentCount == 0))
565 return false;
567 if (fComponentCount == 1)
568 return fBody->IsAttachment();
570 BMIMEMultipartMailContainer* container
571 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody);
572 if (container == NULL)
573 return false;
575 BMailComponent* component = container->GetComponent(i);
576 if (component == NULL)
577 return false;
579 return component->IsAttachment();
583 void
584 BEmailMessage::SetBodyTextTo(const char* text)
586 if (fTextBody == NULL) {
587 fTextBody = new BTextMailComponent;
588 AddComponent(fTextBody);
591 fTextBody->SetText(text);
595 BTextMailComponent*
596 BEmailMessage::Body()
598 if (fTextBody == NULL)
599 fTextBody = _RetrieveTextBody(fBody);
601 return fTextBody;
605 const char*
606 BEmailMessage::BodyText()
608 if (Body() == NULL)
609 return NULL;
611 return fTextBody->Text();
615 status_t
616 BEmailMessage::SetBody(BTextMailComponent* body)
618 if (fTextBody != NULL) {
619 return B_ERROR;
620 // removing doesn't exist for now
621 // RemoveComponent(fTextBody);
622 // delete fTextBody;
624 fTextBody = body;
625 AddComponent(fTextBody);
627 return B_OK;
631 BTextMailComponent*
632 BEmailMessage::_RetrieveTextBody(BMailComponent* component)
634 BTextMailComponent* body = dynamic_cast<BTextMailComponent*>(component);
635 if (body != NULL)
636 return body;
638 BMIMEMultipartMailContainer* container
639 = dynamic_cast<BMIMEMultipartMailContainer*>(component);
640 if (container != NULL) {
641 for (int32 i = 0; i < container->CountComponents(); i++) {
642 if ((component = container->GetComponent(i)) == NULL)
643 continue;
645 switch (component->ComponentType()) {
646 case B_MAIL_PLAIN_TEXT_BODY:
647 // AttributedAttachment returns the MIME type of its
648 // contents, so we have to use dynamic_cast here
649 body = dynamic_cast<BTextMailComponent*>(
650 container->GetComponent(i));
651 if (body != NULL)
652 return body;
653 break;
655 case B_MAIL_MULTIPART_CONTAINER:
656 body = _RetrieveTextBody(container->GetComponent(i));
657 if (body != NULL)
658 return body;
659 break;
663 return NULL;
667 status_t
668 BEmailMessage::SetToRFC822(BPositionIO* mailFile, size_t length,
669 bool parseNow)
671 if (BFile* file = dynamic_cast<BFile*>(mailFile)) {
672 file->ReadAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0, &fAccountID,
673 sizeof(fAccountID));
676 mailFile->Seek(0, SEEK_END);
677 length = mailFile->Position();
678 mailFile->Seek(0, SEEK_SET);
680 fStatus = BMailComponent::SetToRFC822(mailFile, length, parseNow);
681 if (fStatus < B_OK)
682 return fStatus;
684 fBody = WhatIsThis();
686 mailFile->Seek(0, SEEK_SET);
687 fStatus = fBody->SetToRFC822(mailFile, length, parseNow);
688 if (fStatus < B_OK)
689 return fStatus;
691 // Move headers that we use to us, everything else to fBody
692 const char* name;
693 for (int32 i = 0; (name = fBody->HeaderAt(i)) != NULL; i++) {
694 if (strcasecmp(name, "Subject") != 0
695 && strcasecmp(name, "To") != 0
696 && strcasecmp(name, "From") != 0
697 && strcasecmp(name, "Reply-To") != 0
698 && strcasecmp(name, "Cc") != 0
699 && strcasecmp(name, "Priority") != 0
700 && strcasecmp(name, "X-Priority") != 0
701 && strcasecmp(name, "X-Msmail-Priority") != 0
702 && strcasecmp(name, "Date") != 0) {
703 RemoveHeader(name);
707 fBody->RemoveHeader("Subject");
708 fBody->RemoveHeader("To");
709 fBody->RemoveHeader("From");
710 fBody->RemoveHeader("Reply-To");
711 fBody->RemoveHeader("Cc");
712 fBody->RemoveHeader("Priority");
713 fBody->RemoveHeader("X-Priority");
714 fBody->RemoveHeader("X-Msmail-Priority");
715 fBody->RemoveHeader("Date");
717 fComponentCount = 1;
718 if (BMIMEMultipartMailContainer* container
719 = dynamic_cast<BMIMEMultipartMailContainer*>(fBody))
720 fComponentCount = container->CountComponents();
722 return B_OK;
726 status_t
727 BEmailMessage::RenderToRFC822(BPositionIO* file)
729 if (fBody == NULL)
730 return B_MAIL_INVALID_MAIL;
732 // Do real rendering
734 if (From() == NULL) {
735 // set the "From:" string
736 SendViaAccount(fAccountID);
739 BList recipientList;
740 get_address_list(recipientList, To(), extract_address);
741 get_address_list(recipientList, CC(), extract_address);
742 get_address_list(recipientList, fBCC, extract_address);
744 BString recipients;
745 for (int32 i = recipientList.CountItems(); i-- > 0;) {
746 char *address = (char *)recipientList.RemoveItem((int32)0);
748 recipients << '<' << address << '>';
749 if (i)
750 recipients << ',';
752 free(address);
755 // add the date field
756 int32 creationTime = time(NULL);
758 char date[128];
759 struct tm tm;
760 localtime_r(&creationTime, &tm);
762 size_t length = strftime(date, sizeof(date),
763 "%a, %d %b %Y %H:%M:%S", &tm);
765 // GMT offsets are full hours, yes, but you never know :-)
766 snprintf(date + length, sizeof(date) - length, " %+03d%02d",
767 tm.tm_gmtoff / 3600, (tm.tm_gmtoff / 60) % 60);
769 SetHeaderField("Date", date);
772 // add a message-id
774 // empirical evidence indicates message id must be enclosed in
775 // angle brackets and there must be an "at" symbol in it
776 BString messageID;
777 messageID << "<";
778 messageID << system_time();
779 messageID << "-BeMail@";
781 char host[255];
782 if (gethostname(host, sizeof(host)) < 0 || !host[0])
783 strcpy(host, "zoidberg");
785 messageID << host;
786 messageID << ">";
788 SetHeaderField("Message-Id", messageID.String());
790 status_t err = BMailComponent::RenderToRFC822(file);
791 if (err < B_OK)
792 return err;
794 file->Seek(-2, SEEK_CUR);
795 // Remove division between headers
797 err = fBody->RenderToRFC822(file);
798 if (err < B_OK)
799 return err;
801 // Set the message file's attributes. Do this after the rest of the file
802 // is filled in, in case the daemon attempts to send it before it is ready
803 // (since the daemon may send it when it sees the status attribute getting
804 // set to "Pending").
806 if (BFile* attributed = dynamic_cast <BFile*>(file)) {
807 BNodeInfo(attributed).SetType(B_MAIL_TYPE);
809 attributed->WriteAttrString(B_MAIL_ATTR_RECIPIENTS,&recipients);
811 BString attr;
813 attr = To();
814 attributed->WriteAttrString(B_MAIL_ATTR_TO, &attr);
815 attr = CC();
816 attributed->WriteAttrString(B_MAIL_ATTR_CC, &attr);
817 attr = Subject();
818 attributed->WriteAttrString(B_MAIL_ATTR_SUBJECT, &attr);
819 attr = ReplyTo();
820 attributed->WriteAttrString(B_MAIL_ATTR_REPLY, &attr);
821 attr = From();
822 attributed->WriteAttrString(B_MAIL_ATTR_FROM, &attr);
823 if (Priority() != 3 /* Normal is 3 */) {
824 sprintf(attr.LockBuffer(40), "%d", Priority());
825 attr.UnlockBuffer(-1);
826 attributed->WriteAttrString(B_MAIL_ATTR_PRIORITY, &attr);
828 attr = "Pending";
829 attributed->WriteAttrString(B_MAIL_ATTR_STATUS, &attr);
830 attr = "1.0";
831 attributed->WriteAttrString(B_MAIL_ATTR_MIME, &attr);
833 attributed->WriteAttr(B_MAIL_ATTR_ACCOUNT, B_INT32_TYPE, 0,
834 &fAccountID, sizeof(int32));
836 attributed->WriteAttr(B_MAIL_ATTR_WHEN, B_TIME_TYPE, 0, &creationTime,
837 sizeof(int32));
838 int32 flags = B_MAIL_PENDING | B_MAIL_SAVE;
839 attributed->WriteAttr(B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0, &flags,
840 sizeof(int32));
842 attributed->WriteAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE, 0,
843 &fAccountID, sizeof(int32));
846 return B_OK;
850 status_t
851 BEmailMessage::RenderTo(BDirectory* dir, BEntry* msg)
853 time_t currentTime;
854 char numericDateString[40];
855 struct tm timeFields;
856 BString worker;
858 // Generate a file name for the outgoing message. See also
859 // FolderFilter::ProcessMailMessage which does something similar for
860 // incoming messages.
862 BString name = Subject();
863 SubjectToThread(name);
864 // Extract the core subject words.
865 if (name.Length() <= 0)
866 name = "No Subject";
867 if (name[0] == '.') {
868 // Avoid hidden files, starting with a dot.
869 name.Prepend("_");
872 // Convert the date into a year-month-day fixed digit width format, so that
873 // sorting by file name will give all the messages with the same subject in
874 // order of date.
875 time (&currentTime);
876 localtime_r (&currentTime, &timeFields);
877 sprintf (numericDateString, "%04d%02d%02d%02d%02d%02d",
878 timeFields.tm_year + 1900, timeFields.tm_mon + 1, timeFields.tm_mday,
879 timeFields.tm_hour, timeFields.tm_min, timeFields.tm_sec);
880 name << " " << numericDateString;
882 worker = From();
883 extract_address_name(worker);
884 name << " " << worker;
886 name.Truncate(222); // reserve space for the uniquer
888 // Get rid of annoying characters which are hard to use in the shell.
889 name.ReplaceAll('/','_');
890 name.ReplaceAll('\'','_');
891 name.ReplaceAll('"','_');
892 name.ReplaceAll('!','_');
893 name.ReplaceAll('<','_');
894 name.ReplaceAll('>','_');
896 // Remove multiple spaces.
897 while (name.FindFirst(" ") >= 0)
898 name.Replace(" ", " ", 1024);
900 int32 uniquer = time(NULL);
901 worker = name;
903 int32 tries = 30;
904 bool exists;
905 while ((exists = dir->Contains(worker.String())) && --tries > 0) {
906 srand(rand());
907 uniquer += (rand() >> 16) - 16384;
909 worker = name;
910 worker << ' ' << uniquer;
913 if (exists)
914 printf("could not create mail! (should be: %s)\n", worker.String());
916 BFile file;
917 status_t status = dir->CreateFile(worker.String(), &file);
918 if (status != B_OK)
919 return status;
921 if (msg != NULL)
922 msg->SetTo(dir,worker.String());
924 return RenderToRFC822(&file);
928 status_t
929 BEmailMessage::Send(bool sendNow)
931 BMailAccounts accounts;
932 BMailAccountSettings* account = accounts.AccountByID(fAccountID);
933 if (account == NULL || !account->HasOutbound()) {
934 account = accounts.AccountByID(
935 BMailSettings().DefaultOutboundAccount());
936 if (!account)
937 return B_ERROR;
938 SendViaAccount(account->AccountID());
941 BString path;
942 if (account->OutboundSettings().FindString("path", &path) != B_OK) {
943 BPath defaultMailOutPath;
944 if (find_directory(B_USER_DIRECTORY, &defaultMailOutPath) != B_OK
945 || defaultMailOutPath.Append("mail/out") != B_OK)
946 path = "/boot/home/mail/out";
947 else
948 path = defaultMailOutPath.Path();
951 create_directory(path.String(), 0777);
952 BDirectory directory(path.String());
954 BEntry message;
956 status_t status = RenderTo(&directory, &message);
957 if (status >= B_OK && sendNow) {
958 // TODO: check whether or not the internet connection is available
959 BMessenger daemon(B_MAIL_DAEMON_SIGNATURE);
960 if (!daemon.IsValid())
961 return B_MAIL_NO_DAEMON;
963 BMessage msg(kMsgSendMessages);
964 msg.AddInt32("account", fAccountID);
965 BPath path;
966 message.GetPath(&path);
967 msg.AddString("message_path", path.Path());
968 daemon.SendMessage(&msg);
971 return status;
975 void BEmailMessage::_ReservedMessage1() {}
976 void BEmailMessage::_ReservedMessage2() {}
977 void BEmailMessage::_ReservedMessage3() {}