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