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