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(©).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