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