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