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