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