xref: /haiku/src/apps/mail/MailApp.cpp (revision fccd8899fcb583bfb73c5c26c9fcd714b963959b)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2001, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or
30 registered trademarks of Be Incorporated in the United States and other
31 countries. Other brand product names are registered trademarks or trademarks
32 of their respective holders. All rights reserved.
33 */
34 
35 
36 #include "MailApp.h"
37 
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <sys/utsname.h>
44 #include <unistd.h>
45 
46 #include <Autolock.h>
47 #include <Catalog.h>
48 #include <CharacterSet.h>
49 #include <CharacterSetRoster.h>
50 #include <Clipboard.h>
51 #include <Debug.h>
52 #include <E-mail.h>
53 #include <InterfaceKit.h>
54 #include <Locale.h>
55 #include <Roster.h>
56 #include <Screen.h>
57 #include <StorageKit.h>
58 #include <String.h>
59 #include <TextView.h>
60 #include <UTF8.h>
61 
62 #include <fs_index.h>
63 #include <fs_info.h>
64 
65 #include <MailMessage.h>
66 #include <MailSettings.h>
67 #include <MailDaemon.h>
68 #include <mail_util.h>
69 
70 #include <CharacterSetRoster.h>
71 
72 using namespace BPrivate ;
73 
74 #include "ButtonBar.h"
75 #include "Content.h"
76 #include "Enclosures.h"
77 #include "FieldMsg.h"
78 #include "FindWindow.h"
79 #include "Header.h"
80 #include "MailSupport.h"
81 #include "MailWindow.h"
82 #include "Messages.h"
83 #include "Prefs.h"
84 #include "QueryMenu.h"
85 #include "Signature.h"
86 #include "Status.h"
87 #include "String.h"
88 #include "Utilities.h"
89 #include "Words.h"
90 
91 
92 #define B_TRANSLATE_CONTEXT "Mail"
93 
94 
95 static const char *kDictDirectory = "word_dictionary";
96 static const char *kIndexDirectory = "word_index";
97 static const char *kWordsPath = "/boot/optional/goodies/words";
98 static const char *kExact = ".exact";
99 static const char *kMetaphone = ".metaphone";
100 
101 
102 TMailApp::TMailApp()
103 	:
104 	BApplication("application/x-vnd.Be-MAIL"),
105 	fWindowCount(0),
106 	fPrefsWindow(NULL),
107 	fSigWindow(NULL),
108 
109 	fPrintSettings(NULL),
110 	fPrintHelpAndExit(false),
111 
112 	fWrapMode(true),
113 	fAttachAttributes(true),
114 	fColoredQuotes(true),
115 	fShowButtonBar(true),
116 	fWarnAboutUnencodableCharacters(true),
117 	fStartWithSpellCheckOn(false),
118 	fShowSpamGUI(true),
119 	fMailCharacterSet(B_MAIL_UTF8_CONVERSION),
120 	fContentFont(be_fixed_font)
121 {
122 	// set default values
123 	fContentFont.SetSize(12.0);
124 	fAutoMarkRead = true;
125 	fSignature = (char*)malloc(strlen(B_TRANSLATE("None")) + 1);
126 	strcpy(fSignature, B_TRANSLATE("None"));
127 	fReplyPreamble = strdup(B_TRANSLATE("%e wrote:\\n"));
128 
129 	fMailWindowFrame.Set(0, 0, 0, 0);
130 	fSignatureWindowFrame.Set(6, TITLE_BAR_HEIGHT, 6 + kSigWidth,
131 		TITLE_BAR_HEIGHT + kSigHeight);
132 	fPrefsWindowPos.Set(6, TITLE_BAR_HEIGHT);
133 
134 	const BCharacterSet *defaultComposeEncoding =
135 		BCharacterSetRoster::FindCharacterSetByName(
136 		B_TRANSLATE_COMMENT("UTF-8", "This string is used as a key to set "
137 		"default message compose encoding. It must be correct IANA name from "
138 		"http://cgit.haiku-os.org/haiku/tree/src/kits/textencoding"
139 		"/character_sets.cpp Translate it only if you want to change default "
140 		"message compose encoding for your locale. If you don't know what is "
141 		"it and why it may needs changing, just leave \"UTF-8\"."));
142 	if (defaultComposeEncoding != NULL)
143 		fMailCharacterSet = defaultComposeEncoding->GetConversionID();
144 
145 	// Find and read settings file.
146 	LoadSettings();
147 
148 	_CheckForSpamFilterExistence();
149 	fContentFont.SetSpacing(B_BITMAP_SPACING);
150 	fLastMailWindowFrame = fMailWindowFrame;
151 }
152 
153 
154 TMailApp::~TMailApp()
155 {
156 }
157 
158 
159 void
160 TMailApp::ArgvReceived(int32 argc, char **argv)
161 {
162 	BEntry entry;
163 	BString names;
164 	BString ccNames;
165 	BString bccNames;
166 	BString subject;
167 	BString body;
168 	BMessage enclosure(B_REFS_RECEIVED);
169 	// a "mailto:" with no name should open an empty window
170 	// so remember if we got a "mailto:" even if there isn't a name
171 	// that goes along with it (this allows deskbar replicant to open
172 	// an empty message even when Mail is already running)
173 	bool gotmailto = false;
174 
175 	for (int32 loop = 1; loop < argc; loop++)
176 	{
177 		if (strcmp(argv[loop], "-h") == 0
178 			|| strcmp(argv[loop], "--help") == 0)
179 		{
180 			printf(" usage: %s [ mailto:<address> ] [ -subject \"<text>\" ] [ ccto:<address> ] [ bccto:<address> ] "
181 				"[ -body \"<body text>\" ] [ enclosure:<path> ] [ <message to read> ...] \n",
182 				argv[0]);
183 			fPrintHelpAndExit = true;
184 			be_app->PostMessage(B_QUIT_REQUESTED);
185 			return;
186 		}
187 		else if (strncmp(argv[loop], "mailto:", 7) == 0)
188 		{
189 			if (names.Length())
190 				names += ", ";
191 			char *options;
192 			if ((options = strchr(argv[loop],'?')) != NULL)
193 			{
194 				names.Append(argv[loop] + 7, options - argv[loop] - 7);
195 				if (!strncmp(++options,"subject=",8))
196 					subject = options + 8;
197 			}
198 			else
199 				names += argv[loop] + 7;
200 			gotmailto = true;
201 		}
202 		else if (strncmp(argv[loop], "ccto:", 5) == 0)
203 		{
204 			if (ccNames.Length())
205 				ccNames += ", ";
206 			ccNames += argv[loop] + 5;
207 		}
208 		else if (strncmp(argv[loop], "bccto:", 6) == 0)
209 		{
210 			if (bccNames.Length())
211 				bccNames += ", ";
212 			bccNames += argv[loop] + 6;
213 		}
214 		else if (strcmp(argv[loop], "-subject") == 0)
215 			subject = argv[++loop];
216 		else if (strcmp(argv[loop], "-body") == 0 && argv[loop + 1])
217 			body = argv[++loop];
218 		else if (strncmp(argv[loop], "enclosure:", 10) == 0)
219 		{
220 			BEntry tmp(argv[loop] + 10, true);
221 			if (tmp.InitCheck() == B_OK && tmp.Exists())
222 			{
223 				entry_ref ref;
224 				tmp.GetRef(&ref);
225 				enclosure.AddRef("refs", &ref);
226 			}
227 		}
228 		else if (entry.SetTo(argv[loop]) == B_NO_ERROR)
229 		{
230 			BMessage msg(B_REFS_RECEIVED);
231 			entry_ref ref;
232 			entry.GetRef(&ref);
233 			msg.AddRef("refs", &ref);
234 			RefsReceived(&msg);
235 		}
236 	}
237 
238 	if (gotmailto || names.Length() || ccNames.Length() || bccNames.Length() || subject.Length()
239 		|| body.Length() || enclosure.HasRef("refs"))
240 	{
241 		TMailWindow	*window = NewWindow(NULL, names.String());
242 		window->SetTo(names.String(), subject.String(), ccNames.String(), bccNames.String(),
243 			&body, &enclosure);
244 		window->Show();
245 	}
246 }
247 
248 
249 void
250 TMailApp::MessageReceived(BMessage *msg)
251 {
252 	TMailWindow	*window = NULL;
253 	entry_ref ref;
254 
255 	switch (msg->what) {
256 		case M_NEW:
257 		{
258 			int32 type;
259 			msg->FindInt32("type", &type);
260 			switch (type) {
261 				case M_NEW:
262 					window = NewWindow();
263 					break;
264 
265 				case M_RESEND:
266 				{
267 					msg->FindRef("ref", &ref);
268 					BNode file(&ref);
269 					BString string;
270 
271 					if (file.InitCheck() == B_OK)
272 						file.ReadAttrString(B_MAIL_ATTR_TO, &string);
273 
274 					window = NewWindow(&ref, string.String(), true);
275 					break;
276 				}
277 				case M_FORWARD:
278 				case M_FORWARD_WITHOUT_ATTACHMENTS:
279 				{
280 					TMailWindow	*sourceWindow;
281 					if (msg->FindPointer("window", (void **)&sourceWindow) < B_OK
282 						|| !sourceWindow->Lock())
283 						break;
284 
285 					msg->FindRef("ref", &ref);
286 					window = NewWindow();
287 					if (window->Lock()) {
288 						window->Forward(&ref, sourceWindow, type == M_FORWARD);
289 						window->Unlock();
290 					}
291 					sourceWindow->Unlock();
292 					break;
293 				}
294 
295 				case M_REPLY:
296 				case M_REPLY_TO_SENDER:
297 				case M_REPLY_ALL:
298 				case M_COPY_TO_NEW:
299 				{
300 					TMailWindow	*sourceWindow;
301 					if (msg->FindPointer("window", (void **)&sourceWindow) < B_OK
302 						|| !sourceWindow->Lock())
303 						break;
304 					msg->FindRef("ref", &ref);
305 					window = NewWindow();
306 					if (window->Lock()) {
307 						if (type == M_COPY_TO_NEW)
308 							window->CopyMessage(&ref, sourceWindow);
309 						else
310 							window->Reply(&ref, sourceWindow, type);
311 						window->Unlock();
312 					}
313 					sourceWindow->Unlock();
314 					break;
315 				}
316 			}
317 			if (window)
318 				window->Show();
319 			break;
320 		}
321 
322 		case M_PREFS:
323 			if (fPrefsWindow)
324 				fPrefsWindow->Activate(true);
325 			else {
326 				fPrefsWindow = new TPrefsWindow(BRect(fPrefsWindowPos.x,
327 						fPrefsWindowPos.y, fPrefsWindowPos.x + PREF_WIDTH,
328 						fPrefsWindowPos.y + PREF_HEIGHT),
329 						&fContentFont, NULL, &fWrapMode, &fAttachAttributes,
330 						&fColoredQuotes, &fDefaultAccount, &fUseAccountFrom,
331 						&fReplyPreamble, &fSignature, &fMailCharacterSet,
332 						&fWarnAboutUnencodableCharacters,
333 						&fStartWithSpellCheckOn, &fAutoMarkRead,
334 						&fShowButtonBar);
335 				fPrefsWindow->Show();
336 			}
337 			break;
338 
339 		case PREFS_CHANGED:
340 		{
341 			// Notify all Mail windows
342 			TMailWindow	*window;
343 			for (int32 i = 0; (window=(TMailWindow *)fWindowList.ItemAt(i))
344 				!= NULL; i++)
345 			{
346 				window->Lock();
347 				window->UpdatePreferences();
348 				window->UpdateViews();
349 				window->Unlock();
350 			}
351 			break;
352 		}
353 
354 		case M_ACCOUNTS:
355 			be_roster->Launch("application/x-vnd.Haiku-Mail");
356 			break;
357 
358 		case M_EDIT_SIGNATURE:
359 			if (fSigWindow)
360 				fSigWindow->Activate(true);
361 			else {
362 				fSigWindow = new TSignatureWindow(fSignatureWindowFrame);
363 				fSigWindow->Show();
364 			}
365 			break;
366 
367 		case M_FONT:
368 			FontChange();
369 			break;
370 
371 		case REFS_RECEIVED:
372 			if (msg->HasPointer("window")) {
373 				msg->FindPointer("window", (void **)&window);
374 				BMessage message(*msg);
375 				window->PostMessage(&message, window);
376 			}
377 			break;
378 
379 		case WINDOW_CLOSED:
380 			switch (msg->FindInt32("kind")) {
381 				case MAIL_WINDOW:
382 				{
383 					TMailWindow	*window;
384 					if( msg->FindPointer( "window", (void **)&window ) == B_OK )
385 						fWindowList.RemoveItem(window);
386 					fWindowCount--;
387 					break;
388 				}
389 
390 				case PREFS_WINDOW:
391 					fPrefsWindow = NULL;
392 					msg->FindPoint("window pos", &fPrefsWindowPos);
393 					break;
394 
395 				case SIG_WINDOW:
396 					fSigWindow = NULL;
397 					msg->FindRect("window frame", &fSignatureWindowFrame);
398 					break;
399 			}
400 
401 			if (!fWindowCount && !fSigWindow && !fPrefsWindow)
402 				be_app->PostMessage(B_QUIT_REQUESTED);
403 			break;
404 
405 		case B_REFS_RECEIVED:
406 			RefsReceived(msg);
407 			break;
408 
409 		case B_PRINTER_CHANGED:
410 			_ClearPrintSettings();
411 			break;
412 
413 		default:
414 			BApplication::MessageReceived(msg);
415 	}
416 }
417 
418 
419 bool
420 TMailApp::QuitRequested()
421 {
422 	if (!BApplication::QuitRequested())
423 		return false;
424 
425     fMailWindowFrame = fLastMailWindowFrame;
426     	// Last closed window becomes standard window size.
427 
428 	// Shut down the spam server if it's still running. If the user has trained it on a message, it will stay
429 	// open. This is actually a good thing if there's quite a bit of spam -- no waiting for the thing to start
430 	// up for each message, but it has no business staying that way if the user isn't doing anything with e-mail. :)
431 	if (be_roster->IsRunning(kSpamServerSignature)) {
432 		team_id serverTeam = be_roster->TeamFor(kSpamServerSignature);
433 		if (serverTeam >= 0) {
434 			int32 errorCode = B_SERVER_NOT_FOUND;
435 			BMessenger messengerToSpamServer(kSpamServerSignature, serverTeam, &errorCode);
436 			if (messengerToSpamServer.IsValid()) {
437 				BMessage quitMessage(B_QUIT_REQUESTED);
438 				messengerToSpamServer.SendMessage(&quitMessage);
439 			}
440 		}
441 
442 	}
443 
444 	SaveSettings();
445 	return true;
446 }
447 
448 
449 void
450 TMailApp::ReadyToRun()
451 {
452 	// Create needed indices for META:group, META:email, MAIL:draft,
453 	// INDEX_SIGNATURE, INDEX_STATUS on the boot volume
454 
455 	BVolume volume;
456 	BVolumeRoster().GetBootVolume(&volume);
457 
458 	fs_create_index(volume.Device(), "META:group", B_STRING_TYPE, 0);
459 	fs_create_index(volume.Device(), "META:email", B_STRING_TYPE, 0);
460 	fs_create_index(volume.Device(), "MAIL:draft", B_INT32_TYPE, 0);
461 	fs_create_index(volume.Device(), INDEX_SIGNATURE, B_STRING_TYPE, 0);
462 	fs_create_index(volume.Device(), INDEX_STATUS, B_STRING_TYPE, 0);
463 
464 	// Load dictionaries
465 	BPath indexDir;
466 	BPath dictionaryDir;
467 	BPath dataPath;
468 	BPath indexPath;
469 	BDirectory directory;
470 	BEntry entry;
471 
472 	// Locate user settings directory
473 	find_directory(B_SYSTEM_DATA_DIRECTORY, &indexDir, true);
474 	indexDir.Append("spell_check");
475 	dictionaryDir = indexDir;
476 
477 	// Setup directory paths
478 	indexDir.Append(kIndexDirectory);
479 	dictionaryDir.Append(kDictDirectory);
480 
481 	// Create directories if needed
482 	directory.CreateDirectory(indexDir.Path(), NULL);
483 	directory.CreateDirectory(dictionaryDir.Path(), NULL);
484 
485 	dataPath = dictionaryDir;
486 	dataPath.Append("words");
487 
488 	// Only Load if Words Dictionary
489 	if (BEntry(kWordsPath).Exists() || BEntry(dataPath.Path()).Exists()) {
490 		// If "/boot/optional/goodies/words" exists but there is no
491 		// system dictionary, copy words
492 		if (!BEntry(dataPath.Path()).Exists() && BEntry(kWordsPath).Exists()) {
493 			BFile words(kWordsPath, B_READ_ONLY);
494 			BFile copy(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE);
495 			char buffer[4096];
496 			ssize_t size;
497 
498 			while ((size = words.Read( buffer, 4096)) > 0)
499 				copy.Write(buffer, size);
500 			BNodeInfo(&copy).SetType("text/plain");
501 		}
502 
503 		// Create user dictionary if it does not exist
504 		dataPath = dictionaryDir;
505 		dataPath.Append("user");
506 		if (!BEntry(dataPath.Path()).Exists()) {
507 			BFile user(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE);
508 			BNodeInfo(&user).SetType("text/plain");
509 		}
510 
511 		// Load dictionaries
512 		directory.SetTo(dictionaryDir.Path());
513 
514 		BString leafName;
515 		gUserDict = -1;
516 
517 		while (gDictCount < MAX_DICTIONARIES
518 			&& directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) {
519 			dataPath.SetTo(&entry);
520 
521 			// Identify the user dictionary
522 			if (strcmp("user", dataPath.Leaf()) == 0) {
523 				gUserDictFile = new BFile(dataPath.Path(), B_WRITE_ONLY | B_OPEN_AT_END);
524 				gUserDict = gDictCount;
525 			}
526 
527 			indexPath = indexDir;
528 			leafName.SetTo(dataPath.Leaf());
529 			leafName.Append(kMetaphone);
530 			indexPath.Append(leafName.String());
531 			gWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), true);
532 
533 			indexPath = indexDir;
534 			leafName.SetTo(dataPath.Leaf());
535 			leafName.Append(kExact);
536 			indexPath.Append(leafName.String());
537 			gExactWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), false);
538 			gDictCount++;
539 		}
540 	}
541 
542 	// Create a new window if starting up without any extra arguments.
543 
544 	if (!fPrintHelpAndExit && !fWindowCount) {
545 		TMailWindow	*window;
546 		window = NewWindow();
547 		window->Show();
548 	}
549 }
550 
551 
552 void
553 TMailApp::RefsReceived(BMessage *msg)
554 {
555 	bool		have_names = false;
556 	BString		names;
557 	char		type[B_FILE_NAME_LENGTH];
558 	int32		item = 0;
559 	BFile		file;
560 	TMailWindow	*window;
561 	entry_ref	ref;
562 
563 	//
564 	// If a tracker window opened me, get a messenger from it.
565 	//
566 	BMessenger messenger;
567 	if (msg->HasMessenger("TrackerViewToken"))
568 		msg->FindMessenger("TrackerViewToken", &messenger);
569 
570 	while (msg->HasRef("refs", item)) {
571 		msg->FindRef("refs", item++, &ref);
572 		if ((window = FindWindow(ref)) != NULL)
573 			window->Activate(true);
574 		else {
575 			file.SetTo(&ref, O_RDONLY);
576 			if (file.InitCheck() == B_NO_ERROR) {
577 				BNodeInfo	node(&file);
578 				node.GetType(type);
579 				if (strcmp(type, B_MAIL_TYPE) == 0
580 					|| strcmp(type, B_PARTIAL_MAIL_TYPE) == 0) {
581 					window = NewWindow(&ref, NULL, false, &messenger);
582 					window->Show();
583 				} else if(strcmp(type, "application/x-person") == 0) {
584 					/* Got a People contact info file, see if it has an Email address. */
585 					BString name;
586 					BString email;
587 					attr_info	info;
588 					char *attrib;
589 
590 					if (file.GetAttrInfo("META:email", &info) == B_NO_ERROR) {
591 						attrib = (char *) malloc(info.size + 1);
592 						file.ReadAttr("META:email", B_STRING_TYPE, 0, attrib, info.size);
593 						attrib[info.size] = 0; // Just in case it wasn't NUL terminated.
594 						email << attrib;
595 						free(attrib);
596 
597 						/* we got something... */
598 						if (email.Length() > 0) {
599 							/* see if we can get a username as well */
600 							if(file.GetAttrInfo("META:name", &info) == B_NO_ERROR) {
601 								attrib = (char *) malloc(info.size + 1);
602 								file.ReadAttr("META:name", B_STRING_TYPE, 0, attrib, info.size);
603 								attrib[info.size] = 0; // Just in case it wasn't NUL terminated.
604 								name << "\"" << attrib << "\" ";
605 								email.Prepend("<");
606 								email.Append(">");
607 								free(attrib);
608 							}
609 
610 							if (names.Length() == 0) {
611 								names << name << email;
612 							} else {
613 								names << ", " << name << email;
614 							}
615 							have_names = true;
616 							email.SetTo("");
617 							name.SetTo("");
618 						}
619 					}
620 				}
621 				else if (strcmp(type, kDraftType) == 0) {
622 					window = NewWindow();
623 
624 					// If it's a draft message, open it
625 					window->OpenMessage(&ref);
626 					window->Show();
627 				}
628 			} /* end of else(file.InitCheck() == B_NO_ERROR */
629 		}
630 	}
631 
632 	if (have_names) {
633 		window = NewWindow(NULL, names.String());
634 		window->Show();
635 	}
636 }
637 
638 
639 TMailWindow *
640 TMailApp::FindWindow(const entry_ref &ref)
641 {
642 	BEntry entry(&ref);
643 	if (entry.InitCheck() < B_OK)
644 		return NULL;
645 
646 	node_ref nodeRef;
647 	if (entry.GetNodeRef(&nodeRef) < B_OK)
648 		return NULL;
649 
650 	BWindow	*window;
651 	int32 index = 0;
652 	while ((window = WindowAt(index++)) != NULL) {
653 		TMailWindow *mailWindow = dynamic_cast<TMailWindow *>(window);
654 		if (mailWindow == NULL)
655 			continue;
656 
657 		node_ref mailNodeRef;
658 		if (mailWindow->GetMailNodeRef(mailNodeRef) == B_OK
659 			&& mailNodeRef == nodeRef)
660 			return mailWindow;
661 	}
662 
663 	return NULL;
664 }
665 
666 
667 void
668 TMailApp::_CheckForSpamFilterExistence()
669 {
670 	// Looks at the filter settings to see if the user is using a spam filter.
671 	// If there is one there, set fShowSpamGUI to TRUE, otherwise to FALSE.
672 
673 	int32 addonNameIndex;
674 	const char *addonNamePntr;
675 	BDirectory inChainDir;
676 	BPath path;
677 	BEntry settingsEntry;
678 	BFile settingsFile;
679 	BMessage settingsMessage;
680 
681 	fShowSpamGUI = false;
682 
683 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
684 		return;
685 	// TODO use new settings
686 	path.Append("Mail/chains/inbound");
687 	if (inChainDir.SetTo(path.Path()) != B_OK)
688 		return;
689 
690 	while (inChainDir.GetNextEntry (&settingsEntry, true /* traverse */) == B_OK) {
691 		if (!settingsEntry.IsFile())
692 			continue;
693 		if (settingsFile.SetTo (&settingsEntry, B_READ_ONLY) != B_OK)
694 			continue;
695 		if (settingsMessage.Unflatten (&settingsFile) != B_OK)
696 			continue;
697 		for (addonNameIndex = 0; B_OK == settingsMessage.FindString (
698 			"filter_addons", addonNameIndex, &addonNamePntr);
699 			addonNameIndex++) {
700 			if (strstr (addonNamePntr, "Spam Filter") != NULL) {
701 				fShowSpamGUI = true; // Found it!
702 				return;
703 			}
704 		}
705 	}
706 }
707 
708 
709 void
710 TMailApp::SetPrintSettings(const BMessage* printSettings)
711 {
712 	BAutolock _(this);
713 
714 	if (printSettings == fPrintSettings)
715 		return;
716 
717 	delete fPrintSettings;
718 	if (printSettings)
719 		fPrintSettings = new BMessage(*printSettings);
720 	else
721 		fPrintSettings = NULL;
722 }
723 
724 
725 bool
726 TMailApp::HasPrintSettings()
727 {
728 	BAutolock _(this);
729 	return fPrintSettings != NULL;
730 }
731 
732 
733 BMessage
734 TMailApp::PrintSettings()
735 {
736 	BAutolock _(this);
737 	return BMessage(*fPrintSettings);
738 }
739 
740 
741 void
742 TMailApp::_ClearPrintSettings()
743 {
744 	delete fPrintSettings;
745 	fPrintSettings = NULL;
746 }
747 
748 
749 void
750 TMailApp::SetLastWindowFrame(BRect frame)
751 {
752 	BAutolock _(this);
753 	fLastMailWindowFrame = frame;
754 }
755 
756 
757 status_t
758 TMailApp::GetSettingsPath(BPath &path)
759 {
760 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
761 	if (status != B_OK)
762 		return status;
763 
764 	path.Append("Mail");
765 	return create_directory(path.Path(), 0755);
766 }
767 
768 
769 status_t
770 TMailApp::LoadOldSettings()
771 {
772 	BPath path;
773 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
774 	if (status != B_OK)
775 		return status;
776 
777 	path.Append("Mail_data");
778 
779 	BFile file;
780 	status = file.SetTo(path.Path(), B_READ_ONLY);
781 	if (status != B_OK)
782 		return status;
783 
784 	file.Read(&fMailWindowFrame, sizeof(BRect));
785 //	file.Read(&level, sizeof(level));
786 
787 	font_family	fontFamily;
788 	font_style	fontStyle;
789 	float size;
790 	file.Read(&fontFamily, sizeof(font_family));
791 	file.Read(&fontStyle, sizeof(font_style));
792 	file.Read(&size, sizeof(float));
793 	if (size >= 9)
794 		fContentFont.SetSize(size);
795 
796 	if (fontFamily[0] && fontStyle[0])
797 		fContentFont.SetFamilyAndStyle(fontFamily, fontStyle);
798 
799 	file.Read(&fSignatureWindowFrame, sizeof(BRect));
800 	file.Seek(1, SEEK_CUR);	// ignore (bool) show header
801 	file.Read(&fWrapMode, sizeof(bool));
802 	file.Read(&fPrefsWindowPos, sizeof(BPoint));
803 
804 	int32 length;
805 	if (file.Read(&length, sizeof(int32)) < (ssize_t)sizeof(int32))
806 		return B_IO_ERROR;
807 
808 	free(fSignature);
809 	fSignature = NULL;
810 
811 	if (length > 0) {
812 		fSignature = (char *)malloc(length);
813 		if (fSignature == NULL)
814 			return B_NO_MEMORY;
815 
816 		file.Read(fSignature, length);
817 	}
818 
819 	file.Read(&fMailCharacterSet, sizeof(int32));
820 	if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION
821 		&& fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION
822 		&& BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL)
823 		fMailCharacterSet = B_MS_WINDOWS_CONVERSION;
824 
825 	if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) {
826 		char *findString = (char *)malloc(length + 1);
827 		if (findString == NULL)
828 			return B_NO_MEMORY;
829 
830 		file.Read(findString, length);
831 		findString[length] = '\0';
832 		FindWindow::SetFindString(findString);
833 		free(findString);
834 	}
835 	if (file.Read(&fShowButtonBar, sizeof(uint8)) < (ssize_t)sizeof(uint8))
836 		fShowButtonBar = true;
837 	if (file.Read(&fUseAccountFrom, sizeof(int32)) < (ssize_t)sizeof(int32)
838 		|| fUseAccountFrom < ACCOUNT_USE_DEFAULT
839 		|| fUseAccountFrom > ACCOUNT_FROM_MAIL)
840 		fUseAccountFrom = ACCOUNT_USE_DEFAULT;
841 	if (file.Read(&fColoredQuotes, sizeof(bool)) < (ssize_t)sizeof(bool))
842 		fColoredQuotes = true;
843 
844 	if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) {
845 		free(fReplyPreamble);
846 		fReplyPreamble = (char *)malloc(length + 1);
847 		if (fReplyPreamble == NULL)
848 			return B_NO_MEMORY;
849 
850 		file.Read(fReplyPreamble, length);
851 		fReplyPreamble[length] = '\0';
852 	}
853 
854 	file.Read(&fAttachAttributes, sizeof(bool));
855 	file.Read(&fWarnAboutUnencodableCharacters, sizeof(bool));
856 
857 	return B_OK;
858 }
859 
860 
861 status_t
862 TMailApp::SaveSettings()
863 {
864 	BMailSettings accountSettings;
865 
866 	if (fDefaultAccount != ~0L) {
867 		accountSettings.SetDefaultOutboundAccount(fDefaultAccount);
868 		accountSettings.Save();
869 	}
870 
871 	BPath path;
872 	status_t status = GetSettingsPath(path);
873 	if (status != B_OK)
874 		return status;
875 
876 	path.Append("BeMail Settings~");
877 
878 	BFile file;
879 	status = file.SetTo(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
880 	if (status != B_OK)
881 		return status;
882 
883 	BMessage settings('BeMl');
884 	settings.AddRect("MailWindowSize", fMailWindowFrame);
885 //	settings.AddInt32("ExperienceLevel", level);
886 
887 	font_family fontFamily;
888 	font_style fontStyle;
889 	fContentFont.GetFamilyAndStyle(&fontFamily, &fontStyle);
890 
891 	settings.AddString("FontFamily", fontFamily);
892 	settings.AddString("FontStyle", fontStyle);
893 	settings.AddFloat("FontSize", fContentFont.Size());
894 
895 	settings.AddRect("SignatureWindowSize", fSignatureWindowFrame);
896 	settings.AddBool("WordWrapMode", fWrapMode);
897 	settings.AddPoint("PreferencesWindowLocation", fPrefsWindowPos);
898 	settings.AddBool("AutoMarkRead", fAutoMarkRead);
899 	settings.AddString("SignatureText", fSignature);
900 	settings.AddInt32("CharacterSet", fMailCharacterSet);
901 	settings.AddString("FindString", FindWindow::GetFindString());
902 	settings.AddInt8("ShowButtonBar", fShowButtonBar);
903 	settings.AddInt32("UseAccountFrom", fUseAccountFrom);
904 	settings.AddBool("ColoredQuotes", fColoredQuotes);
905 	settings.AddString("ReplyPreamble", fReplyPreamble);
906 	settings.AddBool("AttachAttributes", fAttachAttributes);
907 	settings.AddBool("WarnAboutUnencodableCharacters", fWarnAboutUnencodableCharacters);
908 	settings.AddBool("StartWithSpellCheck", fStartWithSpellCheckOn);
909 
910 	BEntry entry;
911 	status = entry.SetTo(path.Path());
912 	if (status != B_OK)
913 		return status;
914 
915 	status = settings.Flatten(&file);
916 	if (status == B_OK) {
917 		// replace original settings file
918 		status = entry.Rename("BeMail Settings", true);
919 	} else
920 		entry.Remove();
921 
922 	return status;
923 }
924 
925 
926 status_t
927 TMailApp::LoadSettings()
928 {
929 	BMailSettings accountSettings;
930 	fDefaultAccount = accountSettings.DefaultOutboundAccount();
931 
932 	BPath path;
933 	status_t status = GetSettingsPath(path);
934 	if (status != B_OK)
935 		return status;
936 
937 	path.Append("BeMail Settings");
938 
939 	BFile file;
940 	status = file.SetTo(path.Path(), B_READ_ONLY);
941 	if (status != B_OK)
942 		return LoadOldSettings();
943 
944 	BMessage settings;
945 	status = settings.Unflatten(&file);
946 	if (status < B_OK || settings.what != 'BeMl') {
947 		// the current settings are corrupted, try old ones
948 		return LoadOldSettings();
949 	}
950 
951 	BRect rect;
952 	if (settings.FindRect("MailWindowSize", &rect) == B_OK)
953 		fMailWindowFrame = rect;
954 
955 	int32 int32Value;
956 //	if (settings.FindInt32("ExperienceLevel", &int32Value) == B_OK)
957 //		level = int32Value;
958 
959 	const char *fontFamily;
960 	if (settings.FindString("FontFamily", &fontFamily) == B_OK) {
961 		const char *fontStyle;
962 		if (settings.FindString("FontStyle", &fontStyle) == B_OK) {
963 			float size;
964 			if (settings.FindFloat("FontSize", &size) == B_OK) {
965 				if (size >= 7)
966 					fContentFont.SetSize(size);
967 
968 				if (fontFamily[0] && fontStyle[0]) {
969 					fContentFont.SetFamilyAndStyle(fontFamily[0] ? fontFamily : NULL,
970 						fontStyle[0] ? fontStyle : NULL);
971 				}
972 			}
973 		}
974 	}
975 
976 	if (settings.FindRect("SignatureWindowSize", &rect) == B_OK)
977 		fSignatureWindowFrame = rect;
978 
979 	bool boolValue;
980 	if (settings.FindBool("WordWrapMode", &boolValue) == B_OK)
981 		fWrapMode = boolValue;
982 
983 	BPoint point;
984 	if (settings.FindPoint("PreferencesWindowLocation", &point) == B_OK)
985 		fPrefsWindowPos = point;
986 
987 	if (settings.FindBool("AutoMarkRead", &boolValue) == B_OK)
988 		fAutoMarkRead = boolValue;
989 
990 	const char *string;
991 	if (settings.FindString("SignatureText", &string) == B_OK) {
992 		free(fSignature);
993 		fSignature = strdup(string);
994 	}
995 
996 	if (settings.FindInt32("CharacterSet", &int32Value) == B_OK)
997 		fMailCharacterSet = int32Value;
998 	if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION
999 		&& fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION
1000 		&& BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL)
1001 		fMailCharacterSet = B_MS_WINDOWS_CONVERSION;
1002 
1003 	if (settings.FindString("FindString", &string) == B_OK)
1004 		FindWindow::SetFindString(string);
1005 
1006 	int8 int8Value;
1007 	if (settings.FindInt8("ShowButtonBar", &int8Value) == B_OK)
1008 		fShowButtonBar = int8Value;
1009 
1010 	if (settings.FindInt32("UseAccountFrom", &int32Value) == B_OK)
1011 		fUseAccountFrom = int32Value;
1012 	if (fUseAccountFrom < ACCOUNT_USE_DEFAULT
1013 		|| fUseAccountFrom > ACCOUNT_FROM_MAIL)
1014 		fUseAccountFrom = ACCOUNT_USE_DEFAULT;
1015 
1016 	if (settings.FindBool("ColoredQuotes", &boolValue) == B_OK)
1017 		fColoredQuotes = boolValue;
1018 
1019 	if (settings.FindString("ReplyPreamble", &string) == B_OK) {
1020 		free(fReplyPreamble);
1021 		fReplyPreamble = strdup(string);
1022 	}
1023 
1024 	if (settings.FindBool("AttachAttributes", &boolValue) == B_OK)
1025 		fAttachAttributes = boolValue;
1026 
1027 	if (settings.FindBool("WarnAboutUnencodableCharacters", &boolValue) == B_OK)
1028 		fWarnAboutUnencodableCharacters = boolValue;
1029 
1030 	if (settings.FindBool("StartWithSpellCheck", &boolValue) == B_OK)
1031 		fStartWithSpellCheckOn = boolValue;
1032 
1033 	return B_OK;
1034 }
1035 
1036 
1037 void
1038 TMailApp::FontChange()
1039 {
1040 	int32		index = 0;
1041 	BMessage	msg;
1042 	BWindow		*window;
1043 
1044 	msg.what = CHANGE_FONT;
1045 	msg.AddPointer("font", &fContentFont);
1046 
1047 	for (;;) {
1048 		window = WindowAt(index++);
1049 		if (!window)
1050 			break;
1051 
1052 		window->PostMessage(&msg);
1053 	}
1054 }
1055 
1056 
1057 TMailWindow*
1058 TMailApp::NewWindow(const entry_ref* ref, const char* to, bool resend,
1059 	BMessenger* trackerMessenger)
1060 {
1061 	BScreen screen(B_MAIN_SCREEN_ID);
1062 	BRect screenFrame = screen.Frame();
1063 
1064 	BRect r;
1065 	if (fMailWindowFrame.Width() < 64 || fMailWindowFrame.Height() < 20) {
1066 		// default size
1067 		r.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH,
1068 			TITLE_BAR_HEIGHT + WIND_HEIGHT);
1069 	} else
1070 		r = fMailWindowFrame;
1071 
1072 	// make sure the window is not larger than the screen space
1073 	if (r.Height() > screenFrame.Height())
1074 		r.bottom = r.top + screenFrame.Height();
1075 	if (r.Width() > screenFrame.Width())
1076 		r.bottom = r.top + screenFrame.Width();
1077 
1078 	// cascading windows
1079 	r.OffsetBy(((fWindowCount + 5) % 10) * 15 - 75,
1080 		((fWindowCount + 5) % 10) * 15 - 75);
1081 
1082 	// make sure the window is still on screen
1083 	if (r.left - 6 < screenFrame.left)
1084 		r.OffsetTo(screenFrame.left + 8, r.top);
1085 
1086 	if (r.left + 20 > screenFrame.right)
1087 		r.OffsetTo(6, r.top);
1088 
1089 	if (r.top - 26 < screenFrame.top)
1090 		r.OffsetTo(r.left, screenFrame.top + 26);
1091 
1092 	if (r.top + 20 > screenFrame.bottom)
1093 		r.OffsetTo(r.left, TITLE_BAR_HEIGHT);
1094 
1095 	if (r.Width() < WIND_WIDTH)
1096 		r.right = r.left + WIND_WIDTH;
1097 
1098 	fWindowCount++;
1099 
1100 	BString title;
1101 	BFile file;
1102 	if (!resend && ref && file.SetTo(ref, O_RDONLY) == B_OK) {
1103 		BString name;
1104 		if (file.ReadAttrString(B_MAIL_ATTR_NAME, &name) == B_OK) {
1105 			title << name;
1106 			BString subject;
1107 			if (file.ReadAttrString(B_MAIL_ATTR_SUBJECT, &subject) == B_OK)
1108 				title << " -> " << subject;
1109 		}
1110 	}
1111 	if (title == "")
1112 		title = B_TRANSLATE_SYSTEM_NAME("Mail");
1113 
1114 	TMailWindow* window = new TMailWindow(r, title.String(), this, ref, to,
1115 		&fContentFont, resend, trackerMessenger);
1116 	fWindowList.AddItem(window);
1117 
1118 	return window;
1119 }
1120 
1121 
1122 // #pragma mark - settings
1123 
1124 
1125 bool
1126 TMailApp::AutoMarkRead()
1127 {
1128 	BAutolock _(this);
1129 	return fAutoMarkRead;
1130 }
1131 
1132 
1133 BString
1134 TMailApp::Signature()
1135 {
1136 	BAutolock _(this);
1137 	return BString(fSignature);
1138 }
1139 
1140 
1141 BString
1142 TMailApp::ReplyPreamble()
1143 {
1144 	BAutolock _(this);
1145 	return BString(fReplyPreamble);
1146 }
1147 
1148 
1149 bool
1150 TMailApp::WrapMode()
1151 {
1152 	BAutolock _(this);
1153 	return fWrapMode;
1154 }
1155 
1156 
1157 bool
1158 TMailApp::AttachAttributes()
1159 {
1160 	BAutolock _(this);
1161 	return fAttachAttributes;
1162 }
1163 
1164 
1165 bool
1166 TMailApp::ColoredQuotes()
1167 {
1168 	BAutolock _(this);
1169 	return fColoredQuotes;
1170 }
1171 
1172 
1173 uint8
1174 TMailApp::ShowButtonBar()
1175 {
1176 	BAutolock _(this);
1177 	return fShowButtonBar;
1178 }
1179 
1180 
1181 bool
1182 TMailApp::WarnAboutUnencodableCharacters()
1183 {
1184 	BAutolock _(this);
1185 	return fWarnAboutUnencodableCharacters;
1186 }
1187 
1188 
1189 bool
1190 TMailApp::StartWithSpellCheckOn()
1191 {
1192 	BAutolock _(this);
1193 	return fStartWithSpellCheckOn;
1194 }
1195 
1196 
1197 void
1198 TMailApp::SetDefaultAccount(int32 account)
1199 {
1200 	BAutolock _(this);
1201 	fDefaultAccount = account;
1202 }
1203 
1204 
1205 int32
1206 TMailApp::DefaultAccount()
1207 {
1208 	BAutolock _(this);
1209 	return fDefaultAccount;
1210 }
1211 
1212 
1213 int32
1214 TMailApp::UseAccountFrom()
1215 {
1216 	BAutolock _(this);
1217 	return fUseAccountFrom;
1218 }
1219 
1220 
1221 uint32
1222 TMailApp::MailCharacterSet()
1223 {
1224 	BAutolock _(this);
1225 	return fMailCharacterSet;
1226 }
1227 
1228 
1229 BFont
1230 TMailApp::ContentFont()
1231 {
1232 	BAutolock _(this);
1233 	return fContentFont;
1234 }
1235 
1236 
1237 //	#pragma mark -
1238 
1239 
1240 int
1241 main()
1242 {
1243 	tzset();
1244 
1245 	TMailApp().Run();
1246 	return B_OK;
1247 }
1248 
1249