xref: /haiku/src/apps/mail/MailApp.cpp (revision c9060eb991e10e477ece52478d6743fc7691c143)
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 	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 		case M_NEW:
275 		{
276 			int32 type;
277 			msg->FindInt32("type", &type);
278 			switch (type) {
279 				case M_NEW:
280 					window = NewWindow();
281 					break;
282 
283 				case M_RESEND:
284 				{
285 					msg->FindRef("ref", &ref);
286 					BNode file(&ref);
287 					BString string = "";
288 
289 					if (file.InitCheck() == B_OK)
290 						ReadAttrString(&file, B_MAIL_ATTR_TO, &string);
291 
292 					window = NewWindow(&ref, string.String(), true);
293 					break;
294 				}
295 				case M_FORWARD:
296 				case M_FORWARD_WITHOUT_ATTACHMENTS:
297 				{
298 					TMailWindow	*sourceWindow;
299 					if (msg->FindPointer("window", (void **)&sourceWindow) < B_OK
300 						|| !sourceWindow->Lock())
301 						break;
302 
303 					msg->FindRef("ref", &ref);
304 					window = NewWindow();
305 					if (window->Lock()) {
306 						window->Forward(&ref, sourceWindow, type == M_FORWARD);
307 						window->Unlock();
308 					}
309 					sourceWindow->Unlock();
310 					break;
311 				}
312 
313 				case M_REPLY:
314 				case M_REPLY_TO_SENDER:
315 				case M_REPLY_ALL:
316 				case M_COPY_TO_NEW:
317 				{
318 					TMailWindow	*sourceWindow;
319 					if (msg->FindPointer("window", (void **)&sourceWindow) < B_OK
320 						|| !sourceWindow->Lock())
321 						break;
322 					msg->FindRef("ref", &ref);
323 					window = NewWindow();
324 					if (window->Lock()) {
325 						if (type == M_COPY_TO_NEW)
326 							window->CopyMessage(&ref, sourceWindow);
327 						else
328 							window->Reply(&ref, sourceWindow, type);
329 						window->Unlock();
330 					}
331 					sourceWindow->Unlock();
332 					break;
333 				}
334 			}
335 			if (window)
336 				window->Show();
337 			break;
338 		}
339 
340 		case M_PREFS:
341 			if (fPrefsWindow)
342 				fPrefsWindow->Activate(true);
343 			else {
344 				fPrefsWindow = new TPrefsWindow(BRect(fPrefsWindowPos.x,
345 						fPrefsWindowPos.y, fPrefsWindowPos.x + PREF_WIDTH,
346 						fPrefsWindowPos.y + PREF_HEIGHT),
347 						&fContentFont, NULL, &fWrapMode, &fAttachAttributes,
348 						&fColoredQuotes, &fDefaultChain, &fUseAccountFrom,
349 						&fReplyPreamble, &fSignature, &fMailCharacterSet,
350 						&fWarnAboutUnencodableCharacters,
351 						&fStartWithSpellCheckOn, &fShowButtonBar);
352 				fPrefsWindow->Show();
353 				fPreviousShowButtonBar = fShowButtonBar;
354 			}
355 			break;
356 
357 		case PREFS_CHANGED:
358 		{
359 			// Do we need to update the state of the button bars?
360 			if (fPreviousShowButtonBar != fShowButtonBar) {
361 				// Notify all Mail windows
362 				TMailWindow	*window;
363 				for (int32 i = 0; (window=(TMailWindow *)fWindowList.ItemAt(i)) != NULL; i++) {
364 					window->Lock();
365 					window->UpdateViews();
366 					window->Unlock();
367 				}
368 				fPreviousShowButtonBar = fShowButtonBar;
369 			}
370 			break;
371 		}
372 
373 		case M_ACCOUNTS:
374 			be_roster->Launch("application/x-vnd.Haiku-Mail");
375 			break;
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 				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 		// If "/boot/optional/goodies/words" exists but there is no
509 		// system dictionary, copy words
510 		if (!BEntry(dataPath.Path()).Exists() && BEntry(kWordsPath).Exists()) {
511 			BFile words(kWordsPath, B_READ_ONLY);
512 			BFile copy(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE);
513 			char buffer[4096];
514 			ssize_t size;
515 
516 			while ((size = words.Read( buffer, 4096)) > 0)
517 				copy.Write(buffer, size);
518 			BNodeInfo(&copy).SetType("text/plain");
519 		}
520 
521 		// Create user dictionary if it does not exist
522 		dataPath = dictionaryDir;
523 		dataPath.Append("user");
524 		if (!BEntry(dataPath.Path()).Exists()) {
525 			BFile user(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE);
526 			BNodeInfo(&user).SetType("text/plain");
527 		}
528 
529 		// Load dictionaries
530 		directory.SetTo(dictionaryDir.Path());
531 
532 		BString leafName;
533 		gUserDict = -1;
534 
535 		while (gDictCount < MAX_DICTIONARIES
536 			&& directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) {
537 			dataPath.SetTo(&entry);
538 
539 			// Identify the user dictionary
540 			if (strcmp("user", dataPath.Leaf()) == 0) {
541 				gUserDictFile = new BFile(dataPath.Path(), B_WRITE_ONLY | B_OPEN_AT_END);
542 				gUserDict = gDictCount;
543 			}
544 
545 			indexPath = indexDir;
546 			leafName.SetTo(dataPath.Leaf());
547 			leafName.Append(kMetaphone);
548 			indexPath.Append(leafName.String());
549 			gWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), true);
550 
551 			indexPath = indexDir;
552 			leafName.SetTo(dataPath.Leaf());
553 			leafName.Append(kExact);
554 			indexPath.Append(leafName.String());
555 			gExactWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), false);
556 			gDictCount++;
557 		}
558 	}
559 
560 	// Create a new window if starting up without any extra arguments.
561 
562 	if (!fPrintHelpAndExit && !fWindowCount) {
563 		TMailWindow	*window;
564 		window = NewWindow();
565 		window->Show();
566 	}
567 }
568 
569 
570 void
571 TMailApp::RefsReceived(BMessage *msg)
572 {
573 	bool		have_names = false;
574 	BString		names;
575 	char		type[B_FILE_NAME_LENGTH];
576 	int32		item = 0;
577 	BFile		file;
578 	TMailWindow	*window;
579 	entry_ref	ref;
580 
581 	//
582 	// If a tracker window opened me, get a messenger from it.
583 	//
584 	BMessenger messenger;
585 	if (msg->HasMessenger("TrackerViewToken"))
586 		msg->FindMessenger("TrackerViewToken", &messenger);
587 
588 	while (msg->HasRef("refs", item)) {
589 		msg->FindRef("refs", item++, &ref);
590 		if ((window = FindWindow(ref)) != NULL)
591 			window->Activate(true);
592 		else {
593 			file.SetTo(&ref, O_RDONLY);
594 			if (file.InitCheck() == B_NO_ERROR) {
595 				BNodeInfo	node(&file);
596 				node.GetType(type);
597 				if (!strcmp(type, B_MAIL_TYPE)) {
598 					window = NewWindow(&ref, NULL, false, &messenger);
599 					window->Show();
600 				} else if(!strcmp(type, "application/x-person")) {
601 					/* Got a People contact info file, see if it has an Email address. */
602 					BString name;
603 					BString email;
604 					attr_info	info;
605 					char *attrib;
606 
607 					if (file.GetAttrInfo("META:email", &info) == B_NO_ERROR) {
608 						attrib = (char *) malloc(info.size + 1);
609 						file.ReadAttr("META:email", B_STRING_TYPE, 0, attrib, info.size);
610 						attrib[info.size] = 0; // Just in case it wasn't NUL terminated.
611 						email << attrib;
612 						free(attrib);
613 
614 						/* we got something... */
615 						if (email.Length() > 0) {
616 							/* see if we can get a username as well */
617 							if(file.GetAttrInfo("META:name", &info) == B_NO_ERROR) {
618 								attrib = (char *) malloc(info.size + 1);
619 								file.ReadAttr("META:name", B_STRING_TYPE, 0, attrib, info.size);
620 								attrib[info.size] = 0; // Just in case it wasn't NUL terminated.
621 								name << "\"" << attrib << "\" ";
622 								email.Prepend("<");
623 								email.Append(">");
624 								free(attrib);
625 							}
626 
627 							if (names.Length() == 0) {
628 								names << name << email;
629 							} else {
630 								names << ", " << name << email;
631 							}
632 							have_names = true;
633 							email.SetTo("");
634 							name.SetTo("");
635 						}
636 					}
637 				}
638 				else if (!strcmp(type, kDraftType))
639 				{
640 					window = NewWindow();
641 
642 					// If it's a draft message, open it
643 					window->OpenMessage(&ref);
644 					window->Show();
645 				}
646 			} /* end of else(file.InitCheck() == B_NO_ERROR */
647 		}
648 	}
649 
650 	if (have_names) {
651 		window = NewWindow(NULL, names.String());
652 		window->Show();
653 	}
654 }
655 
656 
657 TMailWindow *
658 TMailApp::FindWindow(const entry_ref &ref)
659 {
660 	BEntry entry(&ref);
661 	if (entry.InitCheck() < B_OK)
662 		return NULL;
663 
664 	node_ref nodeRef;
665 	if (entry.GetNodeRef(&nodeRef) < B_OK)
666 		return NULL;
667 
668 	BWindow	*window;
669 	int32 index = 0;
670 	while ((window = WindowAt(index++)) != NULL) {
671 		TMailWindow *mailWindow = dynamic_cast<TMailWindow *>(window);
672 		if (mailWindow == NULL)
673 			continue;
674 
675 		node_ref mailNodeRef;
676 		if (mailWindow->GetMailNodeRef(mailNodeRef) == B_OK
677 			&& mailNodeRef == nodeRef)
678 			return mailWindow;
679 	}
680 
681 	return NULL;
682 }
683 
684 
685 void
686 TMailApp::_CheckForSpamFilterExistence()
687 {
688 	// Looks at the filter settings to see if the user is using a spam filter.
689 	// If there is one there, set fShowSpamGUI to TRUE, otherwise to FALSE.
690 
691 	int32 addonNameIndex;
692 	const char *addonNamePntr;
693 	BDirectory inChainDir;
694 	BPath path;
695 	BEntry settingsEntry;
696 	BFile settingsFile;
697 	BMessage settingsMessage;
698 
699 	fShowSpamGUI = false;
700 
701 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
702 		return;
703 	path.Append("Mail/chains/inbound");
704 	if (inChainDir.SetTo(path.Path()) != B_OK)
705 		return;
706 
707 	while (inChainDir.GetNextEntry (&settingsEntry, true /* traverse */) == B_OK) {
708 		if (!settingsEntry.IsFile())
709 			continue;
710 		if (settingsFile.SetTo (&settingsEntry, B_READ_ONLY) != B_OK)
711 			continue;
712 		if (settingsMessage.Unflatten (&settingsFile) != B_OK)
713 			continue;
714 		for (addonNameIndex = 0; B_OK == settingsMessage.FindString (
715 			"filter_addons", addonNameIndex, &addonNamePntr);
716 			addonNameIndex++) {
717 			if (strstr (addonNamePntr, "Spam Filter") != NULL) {
718 				fShowSpamGUI = true; // Found it!
719 				return;
720 			}
721 		}
722 	}
723 }
724 
725 
726 void
727 TMailApp::SetPrintSettings(const BMessage* printSettings)
728 {
729 	BAutolock _(this);
730 
731 	if (printSettings == fPrintSettings)
732 		return;
733 
734 	delete fPrintSettings;
735 	if (printSettings)
736 		fPrintSettings = new BMessage(*printSettings);
737 	else
738 		fPrintSettings = NULL;
739 }
740 
741 
742 bool
743 TMailApp::HasPrintSettings()
744 {
745 	BAutolock _(this);
746 	return fPrintSettings != NULL;
747 }
748 
749 
750 BMessage
751 TMailApp::PrintSettings()
752 {
753 	BAutolock _(this);
754 	return BMessage(*fPrintSettings);
755 }
756 
757 
758 void
759 TMailApp::_ClearPrintSettings()
760 {
761 	delete fPrintSettings;
762 	fPrintSettings = NULL;
763 }
764 
765 
766 void
767 TMailApp::SetLastWindowFrame(BRect frame)
768 {
769 	BAutolock _(this);
770 	fLastMailWindowFrame = frame;
771 }
772 
773 
774 status_t
775 TMailApp::GetSettingsPath(BPath &path)
776 {
777 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
778 	if (status != B_OK)
779 		return status;
780 
781 	path.Append("Mail");
782 	return create_directory(path.Path(), 0755);
783 }
784 
785 
786 status_t
787 TMailApp::LoadOldSettings()
788 {
789 	BPath path;
790 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
791 	if (status != B_OK)
792 		return status;
793 
794 	path.Append("Mail_data");
795 
796 	BFile file;
797 	status = file.SetTo(path.Path(), B_READ_ONLY);
798 	if (status != B_OK)
799 		return status;
800 
801 	file.Read(&fMailWindowFrame, sizeof(BRect));
802 //	file.Read(&level, sizeof(level));
803 
804 	font_family	fontFamily;
805 	font_style	fontStyle;
806 	float size;
807 	file.Read(&fontFamily, sizeof(font_family));
808 	file.Read(&fontStyle, sizeof(font_style));
809 	file.Read(&size, sizeof(float));
810 	if (size >= 9)
811 		fContentFont.SetSize(size);
812 
813 	if (fontFamily[0] && fontStyle[0])
814 		fContentFont.SetFamilyAndStyle(fontFamily, fontStyle);
815 
816 	file.Read(&fSignatureWindowFrame, sizeof(BRect));
817 	file.Seek(1, SEEK_CUR);	// ignore (bool) show header
818 	file.Read(&fWrapMode, sizeof(bool));
819 	file.Read(&fPrefsWindowPos, sizeof(BPoint));
820 
821 	int32 length;
822 	if (file.Read(&length, sizeof(int32)) < (ssize_t)sizeof(int32))
823 		return B_IO_ERROR;
824 
825 	free(fSignature);
826 	fSignature = NULL;
827 
828 	if (length > 0) {
829 		fSignature = (char *)malloc(length);
830 		if (fSignature == NULL)
831 			return B_NO_MEMORY;
832 
833 		file.Read(fSignature, length);
834 	}
835 
836 	file.Read(&fMailCharacterSet, sizeof(int32));
837 	if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION
838 		&& fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION
839 		&& BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL)
840 		fMailCharacterSet = B_MS_WINDOWS_CONVERSION;
841 
842 	if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) {
843 		char *findString = (char *)malloc(length + 1);
844 		if (findString == NULL)
845 			return B_NO_MEMORY;
846 
847 		file.Read(findString, length);
848 		findString[length] = '\0';
849 		FindWindow::SetFindString(findString);
850 		free(findString);
851 	}
852 	if (file.Read(&fShowButtonBar, sizeof(uint8)) < (ssize_t)sizeof(uint8))
853 		fShowButtonBar = true;
854 	if (file.Read(&fUseAccountFrom, sizeof(int32)) < (ssize_t)sizeof(int32)
855 		|| fUseAccountFrom < ACCOUNT_USE_DEFAULT
856 		|| fUseAccountFrom > ACCOUNT_FROM_MAIL)
857 		fUseAccountFrom = ACCOUNT_USE_DEFAULT;
858 	if (file.Read(&fColoredQuotes, sizeof(bool)) < (ssize_t)sizeof(bool))
859 		fColoredQuotes = true;
860 
861 	if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) {
862 		free(fReplyPreamble);
863 		fReplyPreamble = (char *)malloc(length + 1);
864 		if (fReplyPreamble == NULL)
865 			return B_NO_MEMORY;
866 
867 		file.Read(fReplyPreamble, length);
868 		fReplyPreamble[length] = '\0';
869 	}
870 
871 	file.Read(&fAttachAttributes, sizeof(bool));
872 	file.Read(&fWarnAboutUnencodableCharacters, sizeof(bool));
873 
874 	return B_OK;
875 }
876 
877 
878 status_t
879 TMailApp::SaveSettings()
880 {
881 	BMailSettings chainSettings;
882 
883 	if (fDefaultChain != ~0UL) {
884 		chainSettings.SetDefaultOutboundChainID(fDefaultChain);
885 		chainSettings.Save();
886 	}
887 
888 	BPath path;
889 	status_t status = GetSettingsPath(path);
890 	if (status != B_OK)
891 		return status;
892 
893 	path.Append("BeMail Settings~");
894 
895 	BFile file;
896 	status = file.SetTo(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
897 	if (status != B_OK)
898 		return status;
899 
900 	BMessage settings('BeMl');
901 	settings.AddRect("MailWindowSize", fMailWindowFrame);
902 //	settings.AddInt32("ExperienceLevel", level);
903 
904 	font_family fontFamily;
905 	font_style fontStyle;
906 	fContentFont.GetFamilyAndStyle(&fontFamily, &fontStyle);
907 
908 	settings.AddString("FontFamily", fontFamily);
909 	settings.AddString("FontStyle", fontStyle);
910 	settings.AddFloat("FontSize", fContentFont.Size());
911 
912 	settings.AddRect("SignatureWindowSize", fSignatureWindowFrame);
913 	settings.AddBool("WordWrapMode", fWrapMode);
914 	settings.AddPoint("PreferencesWindowLocation", fPrefsWindowPos);
915 	settings.AddString("SignatureText", fSignature);
916 	settings.AddInt32("CharacterSet", fMailCharacterSet);
917 	settings.AddString("FindString", FindWindow::GetFindString());
918 	settings.AddInt8("ShowButtonBar", fShowButtonBar);
919 	settings.AddInt32("UseAccountFrom", fUseAccountFrom);
920 	settings.AddBool("ColoredQuotes", fColoredQuotes);
921 	settings.AddString("ReplyPreamble", fReplyPreamble);
922 	settings.AddBool("AttachAttributes", fAttachAttributes);
923 	settings.AddBool("WarnAboutUnencodableCharacters", fWarnAboutUnencodableCharacters);
924 	settings.AddBool("StartWithSpellCheck", fStartWithSpellCheckOn);
925 
926 	BEntry entry;
927 	status = entry.SetTo(path.Path());
928 	if (status != B_OK)
929 		return status;
930 
931 	status = settings.Flatten(&file);
932 	if (status == B_OK) {
933 		// replace original settings file
934 		status = entry.Rename("BeMail Settings", true);
935 	} else
936 		entry.Remove();
937 
938 	return status;
939 }
940 
941 
942 status_t
943 TMailApp::LoadSettings()
944 {
945 	BMailSettings chainSettings;
946 	fDefaultChain = chainSettings.DefaultOutboundChainID();
947 
948 	BPath path;
949 	status_t status = GetSettingsPath(path);
950 	if (status != B_OK)
951 		return status;
952 
953 	path.Append("BeMail Settings");
954 
955 	BFile file;
956 	status = file.SetTo(path.Path(), B_READ_ONLY);
957 	if (status != B_OK)
958 		return LoadOldSettings();
959 
960 	BMessage settings;
961 	status = settings.Unflatten(&file);
962 	if (status < B_OK || settings.what != 'BeMl') {
963 		// the current settings are corrupted, try old ones
964 		return LoadOldSettings();
965 	}
966 
967 	BRect rect;
968 	if (settings.FindRect("MailWindowSize", &rect) == B_OK)
969 		fMailWindowFrame = rect;
970 
971 	int32 int32Value;
972 //	if (settings.FindInt32("ExperienceLevel", &int32Value) == B_OK)
973 //		level = int32Value;
974 
975 	const char *fontFamily;
976 	if (settings.FindString("FontFamily", &fontFamily) == B_OK) {
977 		const char *fontStyle;
978 		if (settings.FindString("FontStyle", &fontStyle) == B_OK) {
979 			float size;
980 			if (settings.FindFloat("FontSize", &size) == B_OK) {
981 				if (size >= 7)
982 					fContentFont.SetSize(size);
983 
984 				if (fontFamily[0] && fontStyle[0]) {
985 					fContentFont.SetFamilyAndStyle(fontFamily[0] ? fontFamily : NULL,
986 						fontStyle[0] ? fontStyle : NULL);
987 				}
988 			}
989 		}
990 	}
991 
992 	if (settings.FindRect("SignatureWindowSize", &rect) == B_OK)
993 		fSignatureWindowFrame = rect;
994 
995 	bool boolValue;
996 	if (settings.FindBool("WordWrapMode", &boolValue) == B_OK)
997 		fWrapMode = boolValue;
998 
999 	BPoint point;
1000 	if (settings.FindPoint("PreferencesWindowLocation", &point) == B_OK)
1001 		fPrefsWindowPos = point;
1002 
1003 	const char *string;
1004 	if (settings.FindString("SignatureText", &string) == B_OK) {
1005 		free(fSignature);
1006 		fSignature = strdup(string);
1007 	}
1008 
1009 	if (settings.FindInt32("CharacterSet", &int32Value) == B_OK)
1010 		fMailCharacterSet = int32Value;
1011 	if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION
1012 		&& fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION
1013 		&& BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL)
1014 		fMailCharacterSet = B_MS_WINDOWS_CONVERSION;
1015 
1016 	if (settings.FindString("FindString", &string) == B_OK)
1017 		FindWindow::SetFindString(string);
1018 
1019 	int8 int8Value;
1020 	if (settings.FindInt8("ShowButtonBar", &int8Value) == B_OK)
1021 		fShowButtonBar = int8Value;
1022 
1023 	if (settings.FindInt32("UseAccountFrom", &int32Value) == B_OK)
1024 		fUseAccountFrom = int32Value;
1025 	if (fUseAccountFrom < ACCOUNT_USE_DEFAULT
1026 		|| fUseAccountFrom > ACCOUNT_FROM_MAIL)
1027 		fUseAccountFrom = ACCOUNT_USE_DEFAULT;
1028 
1029 	if (settings.FindBool("ColoredQuotes", &boolValue) == B_OK)
1030 		fColoredQuotes = boolValue;
1031 
1032 	if (settings.FindString("ReplyPreamble", &string) == B_OK) {
1033 		free(fReplyPreamble);
1034 		fReplyPreamble = strdup(string);
1035 	}
1036 
1037 	if (settings.FindBool("AttachAttributes", &boolValue) == B_OK)
1038 		fAttachAttributes = boolValue;
1039 
1040 	if (settings.FindBool("WarnAboutUnencodableCharacters", &boolValue) == B_OK)
1041 		fWarnAboutUnencodableCharacters = boolValue;
1042 
1043 	if (settings.FindBool("StartWithSpellCheck", &boolValue) == B_OK)
1044 		fStartWithSpellCheckOn = boolValue;
1045 
1046 	return B_OK;
1047 }
1048 
1049 
1050 void
1051 TMailApp::FontChange()
1052 {
1053 	int32		index = 0;
1054 	BMessage	msg;
1055 	BWindow		*window;
1056 
1057 	msg.what = CHANGE_FONT;
1058 	msg.AddPointer("font", &fContentFont);
1059 
1060 	for (;;) {
1061 		window = WindowAt(index++);
1062 		if (!window)
1063 			break;
1064 
1065 		window->PostMessage(&msg);
1066 	}
1067 }
1068 
1069 
1070 TMailWindow*
1071 TMailApp::NewWindow(const entry_ref* ref, const char* to, bool resend,
1072 	BMessenger* trackerMessenger)
1073 {
1074 	BScreen screen(B_MAIN_SCREEN_ID);
1075 	BRect screenFrame = screen.Frame();
1076 
1077 	BRect r;
1078 	if (fMailWindowFrame.Width() < 64 || fMailWindowFrame.Height() < 20) {
1079 		// default size
1080 		r.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH,
1081 			TITLE_BAR_HEIGHT + WIND_HEIGHT);
1082 	} else
1083 		r = fMailWindowFrame;
1084 
1085 	// make sure the window is not larger than the screen space
1086 	if (r.Height() > screenFrame.Height())
1087 		r.bottom = r.top + screenFrame.Height();
1088 	if (r.Width() > screenFrame.Width())
1089 		r.bottom = r.top + screenFrame.Width();
1090 
1091 	// cascading windows
1092 	if (fWindowCount < 6)
1093 		r.OffsetBy(fWindowCount * 20, fWindowCount * 20);
1094 	else
1095 		r.OffsetBy((fWindowCount % 10) * 10 - 50, (fWindowCount % 10) * 10 - 50);
1096 
1097 	// make sure the window is still on screen
1098 	if (r.left - 6 < screenFrame.left)
1099 		r.OffsetTo(screenFrame.left + 8, r.top);
1100 
1101 	if (r.left + 20 > screenFrame.right)
1102 		r.OffsetTo(6, r.top);
1103 
1104 	if (r.top - 26 < screenFrame.top)
1105 		r.OffsetTo(r.left, screenFrame.top + 26);
1106 
1107 	if (r.top + 20 > screenFrame.bottom)
1108 		r.OffsetTo(r.left, TITLE_BAR_HEIGHT);
1109 
1110 	if (r.Width() < WIND_WIDTH)
1111 		r.right = r.left + WIND_WIDTH;
1112 
1113 	fWindowCount++;
1114 
1115 	BString title;
1116 	BFile file;
1117 	if (!resend && ref && file.SetTo(ref, O_RDONLY) == B_OK) {
1118 		BString name;
1119 		if (ReadAttrString(&file, B_MAIL_ATTR_NAME, &name) == B_OK) {
1120 			title << name;
1121 			BString subject;
1122 			if (ReadAttrString(&file, B_MAIL_ATTR_SUBJECT, &subject) == B_OK)
1123 				title << " -> " << subject;
1124 		}
1125 	}
1126 	if (title == "")
1127 		title = "Mail";
1128 
1129 	TMailWindow* window = new TMailWindow(r, title.String(), this, ref, to,
1130 		&fContentFont, resend, trackerMessenger);
1131 	fWindowList.AddItem(window);
1132 
1133 	return window;
1134 }
1135 
1136 
1137 // #pragma mark - settings
1138 
1139 
1140 BString
1141 TMailApp::Signature()
1142 {
1143 	BAutolock _(this);
1144 	return BString(fSignature);
1145 }
1146 
1147 
1148 BString
1149 TMailApp::ReplyPreamble()
1150 {
1151 	BAutolock _(this);
1152 	return BString(fReplyPreamble);
1153 }
1154 
1155 
1156 bool
1157 TMailApp::WrapMode()
1158 {
1159 	BAutolock _(this);
1160 	return fWrapMode;
1161 }
1162 
1163 
1164 bool
1165 TMailApp::AttachAttributes()
1166 {
1167 	BAutolock _(this);
1168 	return fAttachAttributes;
1169 }
1170 
1171 
1172 bool
1173 TMailApp::ColoredQuotes()
1174 {
1175 	BAutolock _(this);
1176 	return fColoredQuotes;
1177 }
1178 
1179 
1180 uint8
1181 TMailApp::ShowButtonBar()
1182 {
1183 	BAutolock _(this);
1184 	return fShowButtonBar;
1185 }
1186 
1187 
1188 bool
1189 TMailApp::WarnAboutUnencodableCharacters()
1190 {
1191 	BAutolock _(this);
1192 	return fWarnAboutUnencodableCharacters;
1193 }
1194 
1195 
1196 bool
1197 TMailApp::StartWithSpellCheckOn()
1198 {
1199 	BAutolock _(this);
1200 	return fStartWithSpellCheckOn;
1201 }
1202 
1203 
1204 void
1205 TMailApp::SetDefaultChain(uint32 chain)
1206 {
1207 	BAutolock _(this);
1208 	fDefaultChain = chain;
1209 }
1210 
1211 
1212 uint32
1213 TMailApp::DefaultChain()
1214 {
1215 	BAutolock _(this);
1216 	return fDefaultChain;
1217 }
1218 
1219 
1220 int32
1221 TMailApp::UseAccountFrom()
1222 {
1223 	BAutolock _(this);
1224 	return fUseAccountFrom;
1225 }
1226 
1227 
1228 uint32
1229 TMailApp::MailCharacterSet()
1230 {
1231 	BAutolock _(this);
1232 	return fMailCharacterSet;
1233 }
1234 
1235 
1236 BFont
1237 TMailApp::ContentFont()
1238 {
1239 	BAutolock _(this);
1240 	return fContentFont;
1241 }
1242 
1243