xref: /haiku/src/kits/mail/MailMessage.cpp (revision 24159a0c7d6d6dcba9f2a0c1a7c08d2c8167f21b)
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 (&currentTime);
825 	localtime_r (&currentTime, &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