xref: /haiku/src/apps/mail/MailApp.cpp (revision e8cd7007416a323259791ac09c013dcce2956976)
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, &fDefaultAccount, &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 	// TODO use new settings
700 	path.Append("Mail/chains/inbound");
701 	if (inChainDir.SetTo(path.Path()) != B_OK)
702 		return;
703 
704 	while (inChainDir.GetNextEntry (&settingsEntry, true /* traverse */) == B_OK) {
705 		if (!settingsEntry.IsFile())
706 			continue;
707 		if (settingsFile.SetTo (&settingsEntry, B_READ_ONLY) != B_OK)
708 			continue;
709 		if (settingsMessage.Unflatten (&settingsFile) != B_OK)
710 			continue;
711 		for (addonNameIndex = 0; B_OK == settingsMessage.FindString (
712 			"filter_addons", addonNameIndex, &addonNamePntr);
713 			addonNameIndex++) {
714 			if (strstr (addonNamePntr, "Spam Filter") != NULL) {
715 				fShowSpamGUI = true; // Found it!
716 				return;
717 			}
718 		}
719 	}
720 }
721 
722 
723 void
724 TMailApp::SetPrintSettings(const BMessage* printSettings)
725 {
726 	BAutolock _(this);
727 
728 	if (printSettings == fPrintSettings)
729 		return;
730 
731 	delete fPrintSettings;
732 	if (printSettings)
733 		fPrintSettings = new BMessage(*printSettings);
734 	else
735 		fPrintSettings = NULL;
736 }
737 
738 
739 bool
740 TMailApp::HasPrintSettings()
741 {
742 	BAutolock _(this);
743 	return fPrintSettings != NULL;
744 }
745 
746 
747 BMessage
748 TMailApp::PrintSettings()
749 {
750 	BAutolock _(this);
751 	return BMessage(*fPrintSettings);
752 }
753 
754 
755 void
756 TMailApp::_ClearPrintSettings()
757 {
758 	delete fPrintSettings;
759 	fPrintSettings = NULL;
760 }
761 
762 
763 void
764 TMailApp::SetLastWindowFrame(BRect frame)
765 {
766 	BAutolock _(this);
767 	fLastMailWindowFrame = frame;
768 }
769 
770 
771 status_t
772 TMailApp::GetSettingsPath(BPath &path)
773 {
774 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
775 	if (status != B_OK)
776 		return status;
777 
778 	path.Append("Mail");
779 	return create_directory(path.Path(), 0755);
780 }
781 
782 
783 status_t
784 TMailApp::LoadOldSettings()
785 {
786 	BPath path;
787 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
788 	if (status != B_OK)
789 		return status;
790 
791 	path.Append("Mail_data");
792 
793 	BFile file;
794 	status = file.SetTo(path.Path(), B_READ_ONLY);
795 	if (status != B_OK)
796 		return status;
797 
798 	file.Read(&fMailWindowFrame, sizeof(BRect));
799 //	file.Read(&level, sizeof(level));
800 
801 	font_family	fontFamily;
802 	font_style	fontStyle;
803 	float size;
804 	file.Read(&fontFamily, sizeof(font_family));
805 	file.Read(&fontStyle, sizeof(font_style));
806 	file.Read(&size, sizeof(float));
807 	if (size >= 9)
808 		fContentFont.SetSize(size);
809 
810 	if (fontFamily[0] && fontStyle[0])
811 		fContentFont.SetFamilyAndStyle(fontFamily, fontStyle);
812 
813 	file.Read(&fSignatureWindowFrame, sizeof(BRect));
814 	file.Seek(1, SEEK_CUR);	// ignore (bool) show header
815 	file.Read(&fWrapMode, sizeof(bool));
816 	file.Read(&fPrefsWindowPos, sizeof(BPoint));
817 
818 	int32 length;
819 	if (file.Read(&length, sizeof(int32)) < (ssize_t)sizeof(int32))
820 		return B_IO_ERROR;
821 
822 	free(fSignature);
823 	fSignature = NULL;
824 
825 	if (length > 0) {
826 		fSignature = (char *)malloc(length);
827 		if (fSignature == NULL)
828 			return B_NO_MEMORY;
829 
830 		file.Read(fSignature, length);
831 	}
832 
833 	file.Read(&fMailCharacterSet, sizeof(int32));
834 	if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION
835 		&& fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION
836 		&& BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL)
837 		fMailCharacterSet = B_MS_WINDOWS_CONVERSION;
838 
839 	if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) {
840 		char *findString = (char *)malloc(length + 1);
841 		if (findString == NULL)
842 			return B_NO_MEMORY;
843 
844 		file.Read(findString, length);
845 		findString[length] = '\0';
846 		FindWindow::SetFindString(findString);
847 		free(findString);
848 	}
849 	if (file.Read(&fShowButtonBar, sizeof(uint8)) < (ssize_t)sizeof(uint8))
850 		fShowButtonBar = true;
851 	if (file.Read(&fUseAccountFrom, sizeof(int32)) < (ssize_t)sizeof(int32)
852 		|| fUseAccountFrom < ACCOUNT_USE_DEFAULT
853 		|| fUseAccountFrom > ACCOUNT_FROM_MAIL)
854 		fUseAccountFrom = ACCOUNT_USE_DEFAULT;
855 	if (file.Read(&fColoredQuotes, sizeof(bool)) < (ssize_t)sizeof(bool))
856 		fColoredQuotes = true;
857 
858 	if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) {
859 		free(fReplyPreamble);
860 		fReplyPreamble = (char *)malloc(length + 1);
861 		if (fReplyPreamble == NULL)
862 			return B_NO_MEMORY;
863 
864 		file.Read(fReplyPreamble, length);
865 		fReplyPreamble[length] = '\0';
866 	}
867 
868 	file.Read(&fAttachAttributes, sizeof(bool));
869 	file.Read(&fWarnAboutUnencodableCharacters, sizeof(bool));
870 
871 	return B_OK;
872 }
873 
874 
875 status_t
876 TMailApp::SaveSettings()
877 {
878 	BMailSettings accountSettings;
879 
880 	if (fDefaultAccount != ~0L) {
881 		accountSettings.SetDefaultOutboundAccount(fDefaultAccount);
882 		accountSettings.Save();
883 	}
884 
885 	BPath path;
886 	status_t status = GetSettingsPath(path);
887 	if (status != B_OK)
888 		return status;
889 
890 	path.Append("BeMail Settings~");
891 
892 	BFile file;
893 	status = file.SetTo(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
894 	if (status != B_OK)
895 		return status;
896 
897 	BMessage settings('BeMl');
898 	settings.AddRect("MailWindowSize", fMailWindowFrame);
899 //	settings.AddInt32("ExperienceLevel", level);
900 
901 	font_family fontFamily;
902 	font_style fontStyle;
903 	fContentFont.GetFamilyAndStyle(&fontFamily, &fontStyle);
904 
905 	settings.AddString("FontFamily", fontFamily);
906 	settings.AddString("FontStyle", fontStyle);
907 	settings.AddFloat("FontSize", fContentFont.Size());
908 
909 	settings.AddRect("SignatureWindowSize", fSignatureWindowFrame);
910 	settings.AddBool("WordWrapMode", fWrapMode);
911 	settings.AddPoint("PreferencesWindowLocation", fPrefsWindowPos);
912 	settings.AddBool("AutoMarkRead", fAutoMarkRead);
913 	settings.AddString("SignatureText", fSignature);
914 	settings.AddInt32("CharacterSet", fMailCharacterSet);
915 	settings.AddString("FindString", FindWindow::GetFindString());
916 	settings.AddInt8("ShowButtonBar", fShowButtonBar);
917 	settings.AddInt32("UseAccountFrom", fUseAccountFrom);
918 	settings.AddBool("ColoredQuotes", fColoredQuotes);
919 	settings.AddString("ReplyPreamble", fReplyPreamble);
920 	settings.AddBool("AttachAttributes", fAttachAttributes);
921 	settings.AddBool("WarnAboutUnencodableCharacters", fWarnAboutUnencodableCharacters);
922 	settings.AddBool("StartWithSpellCheck", fStartWithSpellCheckOn);
923 
924 	BEntry entry;
925 	status = entry.SetTo(path.Path());
926 	if (status != B_OK)
927 		return status;
928 
929 	status = settings.Flatten(&file);
930 	if (status == B_OK) {
931 		// replace original settings file
932 		status = entry.Rename("BeMail Settings", true);
933 	} else
934 		entry.Remove();
935 
936 	return status;
937 }
938 
939 
940 status_t
941 TMailApp::LoadSettings()
942 {
943 	BMailSettings accountSettings;
944 	fDefaultAccount = accountSettings.DefaultOutboundAccount();
945 
946 	BPath path;
947 	status_t status = GetSettingsPath(path);
948 	if (status != B_OK)
949 		return status;
950 
951 	path.Append("BeMail Settings");
952 
953 	BFile file;
954 	status = file.SetTo(path.Path(), B_READ_ONLY);
955 	if (status != B_OK)
956 		return LoadOldSettings();
957 
958 	BMessage settings;
959 	status = settings.Unflatten(&file);
960 	if (status < B_OK || settings.what != 'BeMl') {
961 		// the current settings are corrupted, try old ones
962 		return LoadOldSettings();
963 	}
964 
965 	BRect rect;
966 	if (settings.FindRect("MailWindowSize", &rect) == B_OK)
967 		fMailWindowFrame = rect;
968 
969 	int32 int32Value;
970 //	if (settings.FindInt32("ExperienceLevel", &int32Value) == B_OK)
971 //		level = int32Value;
972 
973 	const char *fontFamily;
974 	if (settings.FindString("FontFamily", &fontFamily) == B_OK) {
975 		const char *fontStyle;
976 		if (settings.FindString("FontStyle", &fontStyle) == B_OK) {
977 			float size;
978 			if (settings.FindFloat("FontSize", &size) == B_OK) {
979 				if (size >= 7)
980 					fContentFont.SetSize(size);
981 
982 				if (fontFamily[0] && fontStyle[0]) {
983 					fContentFont.SetFamilyAndStyle(fontFamily[0] ? fontFamily : NULL,
984 						fontStyle[0] ? fontStyle : NULL);
985 				}
986 			}
987 		}
988 	}
989 
990 	if (settings.FindRect("SignatureWindowSize", &rect) == B_OK)
991 		fSignatureWindowFrame = rect;
992 
993 	bool boolValue;
994 	if (settings.FindBool("WordWrapMode", &boolValue) == B_OK)
995 		fWrapMode = boolValue;
996 
997 	BPoint point;
998 	if (settings.FindPoint("PreferencesWindowLocation", &point) == B_OK)
999 		fPrefsWindowPos = point;
1000 
1001 	if (settings.FindBool("AutoMarkRead", &boolValue) == B_OK)
1002 		fAutoMarkRead = boolValue;
1003 
1004 	const char *string;
1005 	if (settings.FindString("SignatureText", &string) == B_OK) {
1006 		free(fSignature);
1007 		fSignature = strdup(string);
1008 	}
1009 
1010 	if (settings.FindInt32("CharacterSet", &int32Value) == B_OK)
1011 		fMailCharacterSet = int32Value;
1012 	if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION
1013 		&& fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION
1014 		&& BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL)
1015 		fMailCharacterSet = B_MS_WINDOWS_CONVERSION;
1016 
1017 	if (settings.FindString("FindString", &string) == B_OK)
1018 		FindWindow::SetFindString(string);
1019 
1020 	int8 int8Value;
1021 	if (settings.FindInt8("ShowButtonBar", &int8Value) == B_OK)
1022 		fShowButtonBar = int8Value;
1023 
1024 	if (settings.FindInt32("UseAccountFrom", &int32Value) == B_OK)
1025 		fUseAccountFrom = int32Value;
1026 	if (fUseAccountFrom < ACCOUNT_USE_DEFAULT
1027 		|| fUseAccountFrom > ACCOUNT_FROM_MAIL)
1028 		fUseAccountFrom = ACCOUNT_USE_DEFAULT;
1029 
1030 	if (settings.FindBool("ColoredQuotes", &boolValue) == B_OK)
1031 		fColoredQuotes = boolValue;
1032 
1033 	if (settings.FindString("ReplyPreamble", &string) == B_OK) {
1034 		free(fReplyPreamble);
1035 		fReplyPreamble = strdup(string);
1036 	}
1037 
1038 	if (settings.FindBool("AttachAttributes", &boolValue) == B_OK)
1039 		fAttachAttributes = boolValue;
1040 
1041 	if (settings.FindBool("WarnAboutUnencodableCharacters", &boolValue) == B_OK)
1042 		fWarnAboutUnencodableCharacters = boolValue;
1043 
1044 	if (settings.FindBool("StartWithSpellCheck", &boolValue) == B_OK)
1045 		fStartWithSpellCheckOn = boolValue;
1046 
1047 	return B_OK;
1048 }
1049 
1050 
1051 void
1052 TMailApp::FontChange()
1053 {
1054 	int32		index = 0;
1055 	BMessage	msg;
1056 	BWindow		*window;
1057 
1058 	msg.what = CHANGE_FONT;
1059 	msg.AddPointer("font", &fContentFont);
1060 
1061 	for (;;) {
1062 		window = WindowAt(index++);
1063 		if (!window)
1064 			break;
1065 
1066 		window->PostMessage(&msg);
1067 	}
1068 }
1069 
1070 
1071 TMailWindow*
1072 TMailApp::NewWindow(const entry_ref* ref, const char* to, bool resend,
1073 	BMessenger* trackerMessenger)
1074 {
1075 	BScreen screen(B_MAIN_SCREEN_ID);
1076 	BRect screenFrame = screen.Frame();
1077 
1078 	BRect r;
1079 	if (fMailWindowFrame.Width() < 64 || fMailWindowFrame.Height() < 20) {
1080 		// default size
1081 		r.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH,
1082 			TITLE_BAR_HEIGHT + WIND_HEIGHT);
1083 	} else
1084 		r = fMailWindowFrame;
1085 
1086 	// make sure the window is not larger than the screen space
1087 	if (r.Height() > screenFrame.Height())
1088 		r.bottom = r.top + screenFrame.Height();
1089 	if (r.Width() > screenFrame.Width())
1090 		r.bottom = r.top + screenFrame.Width();
1091 
1092 	// cascading windows
1093 	r.OffsetBy(((fWindowCount + 5) % 10) * 15 - 75,
1094 		((fWindowCount + 5) % 10) * 15 - 75);
1095 
1096 	// make sure the window is still on screen
1097 	if (r.left - 6 < screenFrame.left)
1098 		r.OffsetTo(screenFrame.left + 8, r.top);
1099 
1100 	if (r.left + 20 > screenFrame.right)
1101 		r.OffsetTo(6, r.top);
1102 
1103 	if (r.top - 26 < screenFrame.top)
1104 		r.OffsetTo(r.left, screenFrame.top + 26);
1105 
1106 	if (r.top + 20 > screenFrame.bottom)
1107 		r.OffsetTo(r.left, TITLE_BAR_HEIGHT);
1108 
1109 	if (r.Width() < WIND_WIDTH)
1110 		r.right = r.left + WIND_WIDTH;
1111 
1112 	fWindowCount++;
1113 
1114 	BString title;
1115 	BFile file;
1116 	if (!resend && ref && file.SetTo(ref, O_RDONLY) == B_OK) {
1117 		BString name;
1118 		if (ReadAttrString(&file, B_MAIL_ATTR_NAME, &name) == B_OK) {
1119 			title << name;
1120 			BString subject;
1121 			if (ReadAttrString(&file, B_MAIL_ATTR_SUBJECT, &subject) == B_OK)
1122 				title << " -> " << subject;
1123 		}
1124 	}
1125 	if (title == "")
1126 		title = B_TRANSLATE("Mail");
1127 
1128 	TMailWindow* window = new TMailWindow(r, title.String(), this, ref, to,
1129 		&fContentFont, resend, trackerMessenger);
1130 	fWindowList.AddItem(window);
1131 
1132 	return window;
1133 }
1134 
1135 
1136 // #pragma mark - settings
1137 
1138 
1139 bool
1140 TMailApp::AutoMarkRead()
1141 {
1142 	BAutolock _(this);
1143 	return fAutoMarkRead;
1144 }
1145 
1146 
1147 BString
1148 TMailApp::Signature()
1149 {
1150 	BAutolock _(this);
1151 	return BString(fSignature);
1152 }
1153 
1154 
1155 BString
1156 TMailApp::ReplyPreamble()
1157 {
1158 	BAutolock _(this);
1159 	return BString(fReplyPreamble);
1160 }
1161 
1162 
1163 bool
1164 TMailApp::WrapMode()
1165 {
1166 	BAutolock _(this);
1167 	return fWrapMode;
1168 }
1169 
1170 
1171 bool
1172 TMailApp::AttachAttributes()
1173 {
1174 	BAutolock _(this);
1175 	return fAttachAttributes;
1176 }
1177 
1178 
1179 bool
1180 TMailApp::ColoredQuotes()
1181 {
1182 	BAutolock _(this);
1183 	return fColoredQuotes;
1184 }
1185 
1186 
1187 uint8
1188 TMailApp::ShowButtonBar()
1189 {
1190 	BAutolock _(this);
1191 	return fShowButtonBar;
1192 }
1193 
1194 
1195 bool
1196 TMailApp::WarnAboutUnencodableCharacters()
1197 {
1198 	BAutolock _(this);
1199 	return fWarnAboutUnencodableCharacters;
1200 }
1201 
1202 
1203 bool
1204 TMailApp::StartWithSpellCheckOn()
1205 {
1206 	BAutolock _(this);
1207 	return fStartWithSpellCheckOn;
1208 }
1209 
1210 
1211 void
1212 TMailApp::SetDefaultAccount(int32 account)
1213 {
1214 	BAutolock _(this);
1215 	fDefaultAccount = account;
1216 }
1217 
1218 
1219 int32
1220 TMailApp::DefaultAccount()
1221 {
1222 	BAutolock _(this);
1223 	return fDefaultAccount;
1224 }
1225 
1226 
1227 int32
1228 TMailApp::UseAccountFrom()
1229 {
1230 	BAutolock _(this);
1231 	return fUseAccountFrom;
1232 }
1233 
1234 
1235 uint32
1236 TMailApp::MailCharacterSet()
1237 {
1238 	BAutolock _(this);
1239 	return fMailCharacterSet;
1240 }
1241 
1242 
1243 BFont
1244 TMailApp::ContentFont()
1245 {
1246 	BAutolock _(this);
1247 	return fContentFont;
1248 }
1249 
1250 
1251 //	#pragma mark -
1252 
1253 
1254 int
1255 main()
1256 {
1257 	tzset();
1258 
1259 	TMailApp().Run();
1260 	return B_OK;
1261 }
1262 
1263