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