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