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