xref: /haiku/src/apps/mail/MailApp.cpp (revision 89755088d790ff4fe36f8aa77dacb2bd15507108)
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_ACCOUNTS:
378 			be_roster->Launch("application/x-vnd.Haiku-Mail");
379 			break;
380 
381 		case M_EDIT_SIGNATURE:
382 			if (fSigWindow)
383 				fSigWindow->Activate(true);
384 			else {
385 				fSigWindow = new TSignatureWindow(fSignatureWindowFrame);
386 				fSigWindow->Show();
387 			}
388 			break;
389 
390 		case M_FONT:
391 			FontChange();
392 			break;
393 
394 		case REFS_RECEIVED:
395 			if (msg->HasPointer("window"))
396 			{
397 				msg->FindPointer("window", (void **)&window);
398 				BMessage message(*msg);
399 				window->PostMessage(&message, window);
400 			}
401 			break;
402 
403 		case WINDOW_CLOSED:
404 			switch (msg->FindInt32("kind")) {
405 				case MAIL_WINDOW:
406 				{
407 					TMailWindow	*window;
408 					if( msg->FindPointer( "window", (void **)&window ) == B_OK )
409 						fWindowList.RemoveItem(window);
410 					fWindowCount--;
411 					break;
412 				}
413 
414 				case PREFS_WINDOW:
415 					fPrefsWindow = NULL;
416 					msg->FindPoint("window pos", &fPrefsWindowPos);
417 					break;
418 
419 				case SIG_WINDOW:
420 					fSigWindow = NULL;
421 					msg->FindRect("window frame", &fSignatureWindowFrame);
422 					break;
423 			}
424 
425 			if (!fWindowCount && !fSigWindow && !fPrefsWindow)
426 				be_app->PostMessage(B_QUIT_REQUESTED);
427 			break;
428 
429 		case B_REFS_RECEIVED:
430 			RefsReceived(msg);
431 			break;
432 
433 		case B_PRINTER_CHANGED:
434 			_ClearPrintSettings();
435 			break;
436 
437 		default:
438 			BApplication::MessageReceived(msg);
439 	}
440 }
441 
442 
443 bool
444 TMailApp::QuitRequested()
445 {
446 	if (!BApplication::QuitRequested())
447 		return false;
448 
449     fMailWindowFrame = fLastMailWindowFrame;
450     	// Last closed window becomes standard window size.
451 
452 	// Shut down the spam server if it's still running. If the user has trained it on a message, it will stay
453 	// open. This is actually a good thing if there's quite a bit of spam -- no waiting for the thing to start
454 	// up for each message, but it has no business staying that way if the user isn't doing anything with e-mail. :)
455 	if (be_roster->IsRunning(kSpamServerSignature)) {
456 		team_id serverTeam = be_roster->TeamFor(kSpamServerSignature);
457 		if (serverTeam >= 0) {
458 			int32 errorCode = B_SERVER_NOT_FOUND;
459 			BMessenger messengerToSpamServer(kSpamServerSignature, serverTeam, &errorCode);
460 			if (messengerToSpamServer.IsValid()) {
461 				BMessage quitMessage(B_QUIT_REQUESTED);
462 				messengerToSpamServer.SendMessage(&quitMessage);
463 			}
464 		}
465 
466 	}
467 
468 	SaveSettings();
469 	return true;
470 }
471 
472 
473 void
474 TMailApp::ReadyToRun()
475 {
476 	// Create needed indices for META:group, META:email, MAIL:draft,
477 	// INDEX_SIGNATURE, INDEX_STATUS on the boot volume
478 
479 	BVolume volume;
480 	BVolumeRoster().GetBootVolume(&volume);
481 
482 	fs_create_index(volume.Device(), "META:group", B_STRING_TYPE, 0);
483 	fs_create_index(volume.Device(), "META:email", B_STRING_TYPE, 0);
484 	fs_create_index(volume.Device(), "MAIL:draft", B_INT32_TYPE, 0);
485 	fs_create_index(volume.Device(), INDEX_SIGNATURE, B_STRING_TYPE, 0);
486 	fs_create_index(volume.Device(), INDEX_STATUS, B_STRING_TYPE, 0);
487 
488 	// Load dictionaries
489 	BPath indexDir;
490 	BPath dictionaryDir;
491 	BPath dataPath;
492 	BPath indexPath;
493 	BDirectory directory;
494 	BEntry entry;
495 
496 	// Locate user settings directory
497 	find_directory(B_BEOS_ETC_DIRECTORY, &indexDir, true);
498 	dictionaryDir = indexDir;
499 
500 	// Setup directory paths
501 	indexDir.Append(kIndexDirectory);
502 	dictionaryDir.Append(kDictDirectory);
503 
504 	// Create directories if needed
505 	directory.CreateDirectory(indexDir.Path(), NULL);
506 	directory.CreateDirectory(dictionaryDir.Path(), NULL);
507 
508 	dataPath = dictionaryDir;
509 	dataPath.Append("words");
510 
511 	// Only Load if Words Dictionary
512 	if (BEntry(kWordsPath).Exists() || BEntry(dataPath.Path()).Exists()) {
513 		// If "/boot/optional/goodies/words" exists but there is no
514 		// system dictionary, copy words
515 		if (!BEntry(dataPath.Path()).Exists() && BEntry(kWordsPath).Exists()) {
516 			BFile words(kWordsPath, B_READ_ONLY);
517 			BFile copy(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE);
518 			char buffer[4096];
519 			ssize_t size;
520 
521 			while ((size = words.Read( buffer, 4096)) > 0)
522 				copy.Write(buffer, size);
523 			BNodeInfo(&copy).SetType("text/plain");
524 		}
525 
526 		// Create user dictionary if it does not exist
527 		dataPath = dictionaryDir;
528 		dataPath.Append("user");
529 		if (!BEntry(dataPath.Path()).Exists()) {
530 			BFile user(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE);
531 			BNodeInfo(&user).SetType("text/plain");
532 		}
533 
534 		// Load dictionaries
535 		directory.SetTo(dictionaryDir.Path());
536 
537 		BString leafName;
538 		gUserDict = -1;
539 
540 		while (gDictCount < MAX_DICTIONARIES
541 			&& directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) {
542 			dataPath.SetTo(&entry);
543 
544 			// Identify the user dictionary
545 			if (strcmp("user", dataPath.Leaf()) == 0) {
546 				gUserDictFile = new BFile(dataPath.Path(), B_WRITE_ONLY | B_OPEN_AT_END);
547 				gUserDict = gDictCount;
548 			}
549 
550 			indexPath = indexDir;
551 			leafName.SetTo(dataPath.Leaf());
552 			leafName.Append(kMetaphone);
553 			indexPath.Append(leafName.String());
554 			gWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), true);
555 
556 			indexPath = indexDir;
557 			leafName.SetTo(dataPath.Leaf());
558 			leafName.Append(kExact);
559 			indexPath.Append(leafName.String());
560 			gExactWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), false);
561 			gDictCount++;
562 		}
563 	}
564 
565 	// Create a new window if starting up without any extra arguments.
566 
567 	if (!fPrintHelpAndExit && !fWindowCount) {
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