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