xref: /haiku/src/servers/mail/main.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
1 /*
2  * Copyright 2009-2010, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 //!	The daemon's inner workings
10 
11 
12 #include <Application.h>
13 #include <Message.h>
14 #include <File.h>
15 #include <MessageRunner.h>
16 #include <Deskbar.h>
17 #include <Roster.h>
18 #include <Button.h>
19 #include <StringView.h>
20 #include <Mime.h>
21 #include <Beep.h>
22 #include <fs_index.h>
23 #include <fs_info.h>
24 #include <String.h>
25 #include <VolumeRoster.h>
26 #include <Query.h>
27 #include <ChainRunner.h>
28 #include <NodeMonitor.h>
29 #include <Path.h>
30 
31 #include <string.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <sys/socket.h>
35 #include <map>
36 
37 #include <E-mail.h>
38 #include <MailSettings.h>
39 #include <MailMessage.h>
40 #include <status.h>
41 #include <StringList.h>
42 
43 #include "DeskbarView.h"
44 #include "LEDAnimation.h"
45 
46 #include <MDRLanguage.h>
47 
48 
49 using std::map;
50 
51 typedef struct glorbal {
52 	size_t bytes;
53 	BStringList msgs;
54 } snuzzwut;
55 
56 
57 static BMailStatusWindow* sStatus;
58 
59 
60 class MailDaemonApp : public BApplication {
61 public:
62 								MailDaemonApp();
63 	virtual						~MailDaemonApp();
64 
65 	virtual	void				MessageReceived(BMessage* message);
66 	virtual	void				RefsReceived(BMessage* message);
67 
68 	virtual void				Pulse();
69 	virtual bool				QuitRequested();
70 	virtual void				ReadyToRun();
71 
72 			void				InstallDeskbarIcon();
73 			void				RemoveDeskbarIcon();
74 
75 			void				RunChains(BList& list, BMessage* message);
76 			void				SendPendingMessages(BMessage* message);
77 			void				GetNewMessages(BMessage* message);
78 
79 private:
80 			void				_UpdateAutoCheck(bigtime_t interval);
81 			bool				_IsPending(BNode& entry) const;
82 
83 private:
84 			BMessageRunner*		fAutoCheckRunner;
85 			BMailSettings		fSettingsFile;
86 
87 			int32				fNewMessages;
88 			bool				fCentralBeep;
89 				// TRUE to do a beep when the status window closes. This happens
90 				// when all mail has been received, so you get one beep for
91 				// everything rather than individual beeps for each mail
92 				// account.
93 				// Set to TRUE by the 'mcbp' message that the mail Notification
94 				// filter sends us, cleared when the beep is done.
95 			BList				fFetchDoneRespondents;
96 			BList				fQueries;
97 
98 			LEDAnimation*		fLEDAnimation;
99 
100 			BString				fAlertString;
101 };
102 
103 
104 MailDaemonApp::MailDaemonApp()
105 	:
106 	BApplication("application/x-vnd.Be-POST")
107 {
108 	sStatus = new BMailStatusWindow(BRect(40, 400, 360, 400), "Mail Status",
109 		fSettingsFile.ShowStatusWindow());
110 	fAutoCheckRunner = NULL;
111 }
112 
113 
114 MailDaemonApp::~MailDaemonApp()
115 {
116 	delete fAutoCheckRunner;
117 
118 	for (int32 i = 0; i < fQueries.CountItems(); i++)
119 		delete (BQuery*)fQueries.ItemAt(i);
120 
121 	delete fLEDAnimation;
122 }
123 
124 
125 void
126 MailDaemonApp::ReadyToRun()
127 {
128 	InstallDeskbarIcon();
129 
130 	_UpdateAutoCheck(fSettingsFile.AutoCheckInterval());
131 
132 	BVolume volume;
133 	BVolumeRoster roster;
134 
135 	fNewMessages = 0;
136 
137 	while (roster.GetNextVolume(&volume) == B_OK) {
138 		//{char name[255];volume.GetName(name);printf("Volume: %s\n",name);}
139 
140 		BQuery* query = new BQuery;
141 
142 		query->SetTarget(this);
143 		query->SetVolume(&volume);
144 		query->PushAttr(B_MAIL_ATTR_STATUS);
145 		query->PushString("New");
146 		query->PushOp(B_EQ);
147 		query->PushAttr("BEOS:TYPE");
148 		query->PushString("text/x-email");
149 		query->PushOp(B_EQ);
150 		query->PushAttr("BEOS:TYPE");
151 		query->PushString("text/x-partial-email");
152 		query->PushOp(B_EQ);
153 		query->PushOp(B_OR);
154 		query->PushOp(B_AND);
155 		query->Fetch();
156 
157 		BEntry entry;
158 		while (query->GetNextEntry(&entry) == B_OK)
159 			fNewMessages++;
160 
161 		fQueries.AddItem(query);
162 	}
163 
164 	BString string;
165 	MDR_DIALECT_CHOICE(
166 		if (fNewMessages > 0)
167 			string << fNewMessages;
168 		else
169 			string << "No";
170 		if (fNewMessages != 1)
171 			string << " new messages.";
172 		else
173 			string << " new message.";,
174 		if (fNewMessages > 0)
175 			string << fNewMessages << " 通の未読メッセージがあります ";
176 		else
177 			string << "未読メッセージはありません";
178 	);
179 	fCentralBeep = false;
180 	sStatus->SetDefaultMessage(string);
181 
182 	fLEDAnimation = new LEDAnimation;
183 	SetPulseRate(1000000);
184 }
185 
186 
187 void
188 MailDaemonApp::RefsReceived(BMessage* message)
189 {
190 	sStatus->Activate(true);
191 
192 	entry_ref ref;
193 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
194 		BNode node(&ref);
195 		if (node.InitCheck() < B_OK)
196 			continue;
197 
198 		BString uid;
199 		if (node.ReadAttrString("MAIL:unique_id", &uid) < 0)
200 			continue;
201 
202 		int32 id;
203 		if (node.ReadAttr("MAIL:chain", B_INT32_TYPE, 0, &id, sizeof(id)) < 0)
204 			continue;
205 
206 		int32 size;
207 		if (node.ReadAttr("MAIL:fullsize", B_SIZE_T_TYPE, 0, &size,
208 				sizeof(size)) < 0) {
209 			size = -1;
210 		}
211 
212 		BPath path(&ref);
213 		BMailChainRunner* runner = GetMailChainRunner(id, sStatus);
214 		if (runner != NULL)
215 			runner->GetSingleMessage(uid.String(), size, &path);
216 	}
217 }
218 
219 
220 void
221 MailDaemonApp::_UpdateAutoCheck(bigtime_t interval)
222 {
223 	if (interval > 0) {
224 		if (fAutoCheckRunner != NULL) {
225 			fAutoCheckRunner->SetInterval(interval);
226 			fAutoCheckRunner->SetCount(-1);
227 		} else
228 			fAutoCheckRunner = new BMessageRunner(be_app_messenger,
229 				new BMessage('moto'), interval);
230 	} else {
231 		delete fAutoCheckRunner;
232 		fAutoCheckRunner = NULL;
233 	}
234 }
235 
236 
237 void
238 MailDaemonApp::MessageReceived(BMessage* msg)
239 {
240 	switch (msg->what) {
241 		case 'moto':
242 			if (fSettingsFile.CheckOnlyIfPPPUp()) {
243 				// TODO: check whether internet is up and running!
244 			}
245 			// supposed to fall through
246 		case 'mbth':	// check & send messages
247 			msg->what = 'msnd';
248 			PostMessage(msg);
249 			// supposed to fall trough
250 		case 'mnow':	// check messages
251 			GetNewMessages(msg);
252 			break;
253 
254 		case 'msnd':	// send messages
255 			SendPendingMessages(msg);
256 			break;
257 
258 		case 'mrrs':
259 			fSettingsFile.Reload();
260 			_UpdateAutoCheck(fSettingsFile.AutoCheckInterval());
261 			sStatus->SetShowCriterion(fSettingsFile.ShowStatusWindow());
262 			break;
263 		case 'shst':	// when to show the status window
264 		{
265 			int32 mode;
266 			if (msg->FindInt32("ShowStatusWindow", &mode) == B_OK)
267 				sStatus->SetShowCriterion(mode);
268 			break;
269 		}
270 
271 		case 'lkch':	// status window look changed
272 		case 'wsch':	// workspace changed
273 			sStatus->PostMessage(msg);
274 			break;
275 
276 		case 'stwg':	// Status window gone
277 		{
278 			BMessage reply('mnuc');
279 			reply.AddInt32("num_new_messages", fNewMessages);
280 
281 			while ((msg = (BMessage*)fFetchDoneRespondents.RemoveItem(0L))) {
282 				msg->SendReply(&reply);
283 				delete msg;
284 			}
285 
286 			if (fAlertString != B_EMPTY_STRING) {
287 				fAlertString.Truncate(fAlertString.Length() - 1);
288 				BAlert* alert = new BAlert(MDR_DIALECT_CHOICE("New Messages",
289 					"新着メッセージ"), fAlertString.String(), "OK", NULL, NULL,
290 					B_WIDTH_AS_USUAL);
291 				alert->SetFeel(B_NORMAL_WINDOW_FEEL);
292 				alert->Go(NULL);
293 				fAlertString = B_EMPTY_STRING;
294 			}
295 
296 			if (fCentralBeep) {
297 				system_beep("New E-mail");
298 				fCentralBeep = false;
299 			}
300 			break;
301 		}
302 
303 		case 'mcbp':
304 			if (fNewMessages > 0)
305 				fCentralBeep = true;
306 			break;
307 
308 		case 'mnum':	// Number of new messages
309 		{
310 			BMessage reply('mnuc');	// Mail New message Count
311 			if (msg->FindBool("wait_for_fetch_done")) {
312 				fFetchDoneRespondents.AddItem(DetachCurrentMessage());
313 				break;
314 			}
315 
316 			reply.AddInt32("num_new_messages", fNewMessages);
317 			msg->SendReply(&reply);
318 			break;
319 		}
320 
321 		case 'mblk':	// Mail Blink
322 			if (fNewMessages > 0)
323 				fLEDAnimation->Start();
324 			break;
325 
326 		case 'enda':	// End Auto Check
327 			delete fAutoCheckRunner;
328 			fAutoCheckRunner = NULL;
329 			break;
330 
331 		case 'numg':
332 		{
333 			int32 numMessages = msg->FindInt32("num_messages");
334 			MDR_DIALECT_CHOICE(
335 				fAlertString << numMessages << " new message";
336 				if (numMessages > 1)
337 					fAlertString << 's';
338 
339 				fAlertString << " for " << msg->FindString("chain_name")
340 					<< '\n';,
341 
342 				fAlertString << msg->FindString("chain_name") << "より\n"
343 					<< numMessages << " 通のメッセージが届きました  ";
344 			);
345 			break;
346 		}
347 
348 		case B_QUERY_UPDATE:
349 		{
350 			int32 what;
351 			msg->FindInt32("opcode", &what);
352 			switch (what) {
353 				case B_ENTRY_CREATED:
354 					fNewMessages++;
355 					break;
356 				case B_ENTRY_REMOVED:
357 					fNewMessages--;
358 					break;
359 			}
360 
361 			BString string;
362 
363 			MDR_DIALECT_CHOICE(
364 				if (fNewMessages > 0)
365 					string << fNewMessages;
366 				else
367 					string << "No";
368 				if (fNewMessages != 1)
369 					string << " new messages.";
370 				else
371 					string << " new message.";,
372 
373 				if (fNewMessages > 0)
374 					string << fNewMessages << " 通の未読メッセージがあります";
375 				else
376 					string << "未読メッセージはありません";
377 			);
378 
379 			sStatus->SetDefaultMessage(string.String());
380 			break;
381 		}
382 
383 		default:
384 			BApplication::MessageReceived(msg);
385 			break;
386 	}
387 }
388 
389 
390 void
391 MailDaemonApp::InstallDeskbarIcon()
392 {
393 	BDeskbar deskbar;
394 
395 	if (!deskbar.HasItem("mail_daemon")) {
396 		BRoster roster;
397 		entry_ref ref;
398 
399 		status_t status = roster.FindApp("application/x-vnd.Be-POST", &ref);
400 		if (status < B_OK) {
401 			fprintf(stderr, "Can't find application to tell deskbar: %s\n",
402 				strerror(status));
403 			return;
404 		}
405 
406 		status = deskbar.AddItem(&ref);
407 		if (status < B_OK) {
408 			fprintf(stderr, "Can't add deskbar replicant: %s\n", strerror(status));
409 			return;
410 		}
411 	}
412 }
413 
414 
415 void
416 MailDaemonApp::RemoveDeskbarIcon()
417 {
418 	BDeskbar deskbar;
419 	if (deskbar.HasItem("mail_daemon"))
420 		deskbar.RemoveItem("mail_daemon");
421 }
422 
423 
424 bool
425 MailDaemonApp::QuitRequested()
426 {
427 	RemoveDeskbarIcon();
428 
429 	return true;
430 }
431 
432 
433 void
434 MailDaemonApp::RunChains(BList& list, BMessage* msg)
435 {
436 	BMailChain* chain;
437 
438 	int32 index = 0, id;
439 	for (; msg->FindInt32("chain", index, &id) == B_OK; index++) {
440 		for (int32 i = 0; i < list.CountItems(); i++) {
441 			chain = (BMailChain*)list.ItemAt(i);
442 
443 			if (chain->ID() == (unsigned)id) {
444 				chain->RunChain(sStatus, true, false, true);
445 				list.RemoveItem(i);	// the chain runner deletes the chain
446 				break;
447 			}
448 		}
449 	}
450 
451 	if (index == 0) {
452 		// invoke all chains
453 		for (int32 i = 0; i < list.CountItems(); i++) {
454 			chain = (BMailChain*)list.ItemAt(i);
455 
456 			chain->RunChain(sStatus, true, false, true);
457 		}
458 	} else {
459 		// delete unused chains
460 		for (int32 i = list.CountItems(); i-- > 0;)
461 			delete (BMailChain*)list.RemoveItem(i);
462 	}
463 }
464 
465 
466 void
467 MailDaemonApp::GetNewMessages(BMessage* msg)
468 {
469 	BList list;
470 	GetInboundMailChains(&list);
471 
472 	RunChains(list, msg);
473 }
474 
475 
476 void
477 MailDaemonApp::SendPendingMessages(BMessage* msg)
478 {
479 	BVolumeRoster roster;
480 	BVolume volume;
481 
482 	while (roster.GetNextVolume(&volume) == B_OK) {
483 		BQuery query;
484 		query.SetVolume(&volume);
485 		query.PushAttr(B_MAIL_ATTR_FLAGS);
486 		query.PushInt32(B_MAIL_PENDING);
487 		query.PushOp(B_EQ);
488 
489 		query.PushAttr(B_MAIL_ATTR_FLAGS);
490 		query.PushInt32(B_MAIL_PENDING | B_MAIL_SAVE);
491 		query.PushOp(B_EQ);
492 
493 		query.PushOp(B_OR);
494 
495 		int32 chainID = -1;
496 
497 		if (msg->FindInt32("chain", &chainID) == B_OK) {
498 			query.PushAttr("MAIL:chain");
499 			query.PushInt32(chainID);
500 			query.PushOp(B_EQ);
501 			query.PushOp(B_AND);
502 		} else
503 			chainID = -1;
504 
505 		if (!msg->HasString("message_path")) {
506 			if (chainID == -1) {
507 				map<int32, snuzzwut*> messages;
508 
509 				query.Fetch();
510 				BEntry entry;
511 				BPath path;
512 				BNode node;
513 				int32 chain;
514 				int32 defaultChain(BMailSettings().DefaultOutboundChainID());
515 				off_t size;
516 
517 				while (query.GetNextEntry(&entry) == B_OK) {
518 					while (node.SetTo(&entry) == B_BUSY)
519 						snooze(1000);
520 					if (!_IsPending(node))
521 						continue;
522 
523 					if (node.ReadAttr("MAIL:chain", B_INT32_TYPE, 0, &chain, 4)
524 							< B_OK)
525 						chain = defaultChain;
526 					entry.GetPath(&path);
527 					node.GetSize(&size);
528 					if (messages[chain] == NULL) {
529 						messages[chain] = new snuzzwut;
530 						messages[chain]->bytes = 0;
531 					}
532 
533 					messages[chain]->msgs += path.Path();
534 					messages[chain]->bytes += size;
535 				}
536 
537 				map<int32, snuzzwut*>::iterator iter = messages.begin();
538 				map<int32, snuzzwut*>::iterator end = messages.end();
539 				while (iter != end) {
540 					if (iter->first > 0 && BMailChain(iter->first)
541 							.ChainDirection() == outbound) {
542 						BMailChainRunner* runner
543 							= GetMailChainRunner(iter->first, sStatus);
544 						runner->GetMessages(&messages[iter->first]->msgs,
545 							messages[iter->first]->bytes);
546 						delete messages[iter->first];
547 						runner->Stop();
548 					}
549 
550 					iter++;
551 				}
552 			} else {
553 				BStringList ids;
554 				size_t bytes = 0;
555 
556 				query.Fetch();
557 				BEntry entry;
558 				BPath path;
559 				BNode node;
560 				off_t size;
561 
562 				while (query.GetNextEntry(&entry) == B_OK) {
563 					node.SetTo(&entry);
564 					if (!_IsPending(node))
565 						continue;
566 
567 					entry.GetPath(&path);
568 					node.GetSize(&size);
569 					ids += path.Path();
570 					bytes += size;
571 				}
572 
573 				BMailChainRunner* runner
574 					= GetMailChainRunner(chainID, sStatus);
575 				runner->GetMessages(&ids, bytes);
576 				runner->Stop();
577 			}
578 		} else {
579 			const char* path;
580 			if (msg->FindString("message_path", &path) != B_OK)
581 				return;
582 
583 			off_t size;
584 			if (BNode(path).GetSize(&size) != B_OK)
585 				return;
586 
587 			BStringList ids;
588 			ids += path;
589 
590 			BMailChainRunner* runner = GetMailChainRunner(chainID, sStatus);
591 			runner->GetMessages(&ids, size);
592 			runner->Stop();
593 		}
594 	}
595 }
596 
597 
598 void
599 MailDaemonApp::Pulse()
600 {
601 	bigtime_t idle = idle_time();
602 	if (fLEDAnimation->IsRunning() && idle < 100000)
603 		fLEDAnimation->Stop();
604 }
605 
606 
607 /*!	Work-around for a broken index that contains out-of-date information.
608 */
609 bool
610 MailDaemonApp::_IsPending(BNode& node) const
611 {
612 	int32 flags;
613 	if (node.ReadAttr(B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0, &flags, sizeof(int32))
614 			!= (ssize_t)sizeof(int32))
615 		return false;
616 
617 	return (flags & B_MAIL_PENDING) != 0;
618 }
619 
620 
621 //	#pragma mark -
622 
623 
624 void
625 makeIndices()
626 {
627 	const char* stringIndices[] = {
628 		B_MAIL_ATTR_ACCOUNT, B_MAIL_ATTR_CC, B_MAIL_ATTR_FROM, B_MAIL_ATTR_NAME,
629 		B_MAIL_ATTR_PRIORITY, B_MAIL_ATTR_REPLY, B_MAIL_ATTR_STATUS,
630 		B_MAIL_ATTR_SUBJECT, B_MAIL_ATTR_TO, B_MAIL_ATTR_THREAD,
631 		NULL
632 	};
633 
634 	// add mail indices for all devices capable of querying
635 
636 	int32 cookie = 0;
637 	dev_t device;
638 	while ((device = next_dev(&cookie)) >= B_OK) {
639 		fs_info info;
640 		if (fs_stat_dev(device, &info) < 0
641 			|| (info.flags & B_FS_HAS_QUERY) == 0)
642 			continue;
643 
644 		for (int32 i = 0; stringIndices[i]; i++)
645 			fs_create_index(device, stringIndices[i], B_STRING_TYPE, 0);
646 
647 		fs_create_index(device, "MAIL:draft", B_INT32_TYPE, 0);
648 		fs_create_index(device, B_MAIL_ATTR_WHEN, B_INT32_TYPE, 0);
649 		fs_create_index(device, B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0);
650 		fs_create_index(device, "MAIL:chain", B_INT32_TYPE, 0);
651 		fs_create_index(device, "MAIL:pending_chain", B_INT32_TYPE, 0);
652 	}
653 }
654 
655 
656 void
657 addAttribute(BMessage& msg, const char* name, const char* publicName,
658 	int32 type = B_STRING_TYPE, bool viewable = true, bool editable = false,
659 	int32 width = 200)
660 {
661 	msg.AddString("attr:name", name);
662 	msg.AddString("attr:public_name", publicName);
663 	msg.AddInt32("attr:type", type);
664 	msg.AddBool("attr:viewable", viewable);
665 	msg.AddBool("attr:editable", editable);
666 	msg.AddInt32("attr:width", width);
667 	msg.AddInt32("attr:alignment", B_ALIGN_LEFT);
668 }
669 
670 
671 void
672 makeMimeType(bool remakeMIMETypes)
673 {
674 	// Add MIME database entries for the e-mail file types we handle.  Either
675 	// do a full rebuild from nothing, or just add on the new attributes that
676 	// we support which the regular BeOS mail daemon didn't have.
677 
678 	const char* types[2] = {"text/x-email", "text/x-partial-email"};
679 	BMimeType mime;
680 	BMessage info;
681 
682 	for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
683 		info.MakeEmpty();
684 		mime.SetTo(types[i]);
685 		if (mime.InitCheck() != B_OK) {
686 			fputs("could not init mime type.\n", stderr);
687 			return;
688 		}
689 
690 		if (!mime.IsInstalled() || remakeMIMETypes) {
691 			// install the full mime type
692 			mime.Delete ();
693 			mime.Install();
694 
695 			// Set up the list of e-mail related attributes that Tracker will
696 			// let you display in columns for e-mail messages.
697 			addAttribute(info, B_MAIL_ATTR_NAME, "Name");
698 			addAttribute(info, B_MAIL_ATTR_SUBJECT, "Subject");
699 			addAttribute(info, B_MAIL_ATTR_TO, "To");
700 			addAttribute(info, B_MAIL_ATTR_CC, "Cc");
701 			addAttribute(info, B_MAIL_ATTR_FROM, "From");
702 			addAttribute(info, B_MAIL_ATTR_REPLY, "Reply To");
703 			addAttribute(info, B_MAIL_ATTR_STATUS, "Status");
704 			addAttribute(info, B_MAIL_ATTR_PRIORITY, "Priority", B_STRING_TYPE,
705 				true, true, 40);
706 			addAttribute(info, B_MAIL_ATTR_WHEN, "When", B_TIME_TYPE, true,
707 				false, 150);
708 			addAttribute(info, B_MAIL_ATTR_THREAD, "Thread");
709 			addAttribute(info, B_MAIL_ATTR_ACCOUNT, "Account", B_STRING_TYPE,
710 				true, false, 100);
711 			mime.SetAttrInfo(&info);
712 
713 			if (i == 0) {
714 				mime.SetShortDescription("E-mail");
715 				mime.SetLongDescription("Electronic Mail Message");
716 				mime.SetPreferredApp("application/x-vnd.Be-MAIL");
717 			} else {
718 				mime.SetShortDescription("Partial E-mail");
719 				mime.SetLongDescription("A Partially Downloaded E-mail");
720 				mime.SetPreferredApp("application/x-vnd.Be-POST");
721 			}
722 		} else {
723 			// Just add the e-mail related attribute types we use to the MIME
724 			// system.
725 			mime.GetAttrInfo(&info);
726 			bool hasAccount = false;
727 			bool hasThread = false;
728 			bool hasSize = false;
729 			const char* result;
730 			for (int32 index = 0; info.FindString("attr:name", index, &result)
731 					== B_OK; index++) {
732 				if (!strcmp(result, B_MAIL_ATTR_ACCOUNT))
733 					hasAccount = true;
734 				if (!strcmp(result, B_MAIL_ATTR_THREAD))
735 					hasThread = true;
736 				if (!strcmp(result, "MAIL:fullsize"))
737 					hasSize = true;
738 			}
739 
740 			if (!hasAccount) {
741 				addAttribute(info, B_MAIL_ATTR_ACCOUNT, "Account",
742 					B_STRING_TYPE, true, false, 100);
743 			}
744 			if (!hasThread)
745 				addAttribute(info, B_MAIL_ATTR_THREAD, "Thread");
746 			/*if (!hasSize)
747 				addAttribute(info,"MAIL:fullsize","Message Size",B_SIZE_T_TYPE,true,false,100);*/
748 			// TODO: Tracker can't display SIZT attributes. What a pain.
749 			if (!hasAccount || !hasThread/* || !hasSize*/)
750 				mime.SetAttrInfo(&info);
751 		}
752 		mime.Unset();
753 	}
754 }
755 
756 
757 int
758 main(int argc, const char** argv)
759 {
760 	bool remakeMIMETypes = false;
761 
762 	for (int i = 1; i < argc; i++) {
763 		if (strcmp(argv[i], "-E") == 0) {
764 			if (!BMailSettings().DaemonAutoStarts())
765 				return 0;
766 		}
767 		if (strcmp(argv[i], "-M") == 0) {
768 			remakeMIMETypes = true;
769 		}
770 	}
771 
772 	MailDaemonApp app;
773 
774 	// install MimeTypes, attributes, indices, and the
775 	// system beep add startup
776 
777 	makeMimeType(remakeMIMETypes);
778 	makeIndices();
779 	add_system_beep_event("New E-mail");
780 
781 	be_app->Run();
782 	return 0;
783 }
784 
785