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