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 fs_create_index(volume.Device(), B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0); 464 465 // Load dictionaries 466 BPath indexDir; 467 BPath dictionaryDir; 468 BPath userDictionaryDir; 469 BPath userIndexDir; 470 BPath dataPath; 471 BPath indexPath; 472 BDirectory directory; 473 BEntry entry; 474 475 // Locate dictionaries directory 476 find_directory(B_SYSTEM_DATA_DIRECTORY, &indexDir, true); 477 indexDir.Append("spell_check"); 478 dictionaryDir = indexDir; 479 480 //Locate user dictionary directory 481 find_directory(B_USER_CONFIG_DIRECTORY, &userIndexDir, true); 482 userIndexDir.Append("data/spell_check"); 483 userDictionaryDir = userIndexDir; 484 485 // Create directory if needed 486 directory.CreateDirectory(userIndexDir.Path(), NULL); 487 488 // Setup directory paths 489 indexDir.Append(kIndexDirectory); 490 dictionaryDir.Append(kDictDirectory); 491 userIndexDir.Append(kIndexDirectory); 492 userDictionaryDir.Append(kDictDirectory); 493 494 // Create directories if needed 495 directory.CreateDirectory(indexDir.Path(), NULL); 496 directory.CreateDirectory(dictionaryDir.Path(), NULL); 497 directory.CreateDirectory(userIndexDir.Path(), NULL); 498 directory.CreateDirectory(userDictionaryDir.Path(), NULL); 499 500 dataPath = dictionaryDir; 501 dataPath.Append("words"); 502 503 // Only Load if Words Dictionary 504 if (BEntry(kWordsPath).Exists() || BEntry(dataPath.Path()).Exists()) { 505 // If "/boot/optional/goodies/words" exists but there is no 506 // system dictionary, copy words 507 if (!BEntry(dataPath.Path()).Exists() && BEntry(kWordsPath).Exists()) { 508 BFile words(kWordsPath, B_READ_ONLY); 509 BFile copy(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE); 510 char buffer[4096]; 511 ssize_t size; 512 513 while ((size = words.Read( buffer, 4096)) > 0) 514 copy.Write(buffer, size); 515 BNodeInfo(©).SetType("text/plain"); 516 } 517 518 // Load dictionaries 519 directory.SetTo(dictionaryDir.Path()); 520 521 BString leafName; 522 gUserDict = -1; 523 524 while (gDictCount < MAX_DICTIONARIES 525 && directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) { 526 dataPath.SetTo(&entry); 527 528 indexPath = indexDir; 529 leafName.SetTo(dataPath.Leaf()); 530 leafName.Append(kMetaphone); 531 indexPath.Append(leafName.String()); 532 gWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), true); 533 534 indexPath = indexDir; 535 leafName.SetTo(dataPath.Leaf()); 536 leafName.Append(kExact); 537 indexPath.Append(leafName.String()); 538 gExactWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), false); 539 gDictCount++; 540 } 541 542 // Create user dictionary if it does not exist 543 dataPath = userDictionaryDir; 544 dataPath.Append("user"); 545 if (!BEntry(dataPath.Path()).Exists()) { 546 BFile user(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE); 547 BNodeInfo(&user).SetType("text/plain"); 548 } 549 550 // Load user dictionary 551 if (BEntry(userDictionaryDir.Path()).Exists()) { 552 gUserDictFile = new BFile(dataPath.Path(), B_WRITE_ONLY | B_OPEN_AT_END); 553 gUserDict = gDictCount; 554 555 indexPath = userIndexDir; 556 leafName.SetTo(dataPath.Leaf()); 557 leafName.Append(kMetaphone); 558 indexPath.Append(leafName.String()); 559 gWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), true); 560 561 indexPath = userIndexDir; 562 leafName.SetTo(dataPath.Leaf()); 563 leafName.Append(kExact); 564 indexPath.Append(leafName.String()); 565 gExactWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), false); 566 gDictCount++; 567 } 568 } 569 570 // Create a new window if starting up without any extra arguments. 571 572 if (!fPrintHelpAndExit && !fWindowCount) { 573 TMailWindow *window; 574 window = NewWindow(); 575 window->Show(); 576 } 577 } 578 579 580 void 581 TMailApp::RefsReceived(BMessage *msg) 582 { 583 bool have_names = false; 584 BString names; 585 char type[B_FILE_NAME_LENGTH]; 586 int32 item = 0; 587 BFile file; 588 TMailWindow *window; 589 entry_ref ref; 590 591 // 592 // If a tracker window opened me, get a messenger from it. 593 // 594 BMessenger messenger; 595 if (msg->HasMessenger("TrackerViewToken")) 596 msg->FindMessenger("TrackerViewToken", &messenger); 597 598 while (msg->HasRef("refs", item)) { 599 msg->FindRef("refs", item++, &ref); 600 if ((window = FindWindow(ref)) != NULL) 601 window->Activate(true); 602 else { 603 file.SetTo(&ref, O_RDONLY); 604 if (file.InitCheck() == B_NO_ERROR) { 605 BNodeInfo node(&file); 606 node.GetType(type); 607 if (strcmp(type, B_MAIL_TYPE) == 0 608 || strcmp(type, B_PARTIAL_MAIL_TYPE) == 0) { 609 window = NewWindow(&ref, NULL, false, &messenger); 610 window->Show(); 611 } else if(strcmp(type, "application/x-person") == 0) { 612 /* Got a People contact info file, see if it has an Email address. */ 613 BString name; 614 BString email; 615 attr_info info; 616 char *attrib; 617 618 if (file.GetAttrInfo("META:email", &info) == B_NO_ERROR) { 619 attrib = (char *) malloc(info.size + 1); 620 file.ReadAttr("META:email", B_STRING_TYPE, 0, attrib, info.size); 621 attrib[info.size] = 0; // Just in case it wasn't NUL terminated. 622 email << attrib; 623 free(attrib); 624 625 /* we got something... */ 626 if (email.Length() > 0) { 627 /* see if we can get a username as well */ 628 if(file.GetAttrInfo("META:name", &info) == B_NO_ERROR) { 629 attrib = (char *) malloc(info.size + 1); 630 file.ReadAttr("META:name", B_STRING_TYPE, 0, attrib, info.size); 631 attrib[info.size] = 0; // Just in case it wasn't NUL terminated. 632 name << "\"" << attrib << "\" "; 633 email.Prepend("<"); 634 email.Append(">"); 635 free(attrib); 636 } 637 638 if (names.Length() == 0) { 639 names << name << email; 640 } else { 641 names << ", " << name << email; 642 } 643 have_names = true; 644 email.SetTo(""); 645 name.SetTo(""); 646 } 647 } 648 } 649 else if (strcmp(type, kDraftType) == 0) { 650 window = NewWindow(); 651 652 // If it's a draft message, open it 653 window->OpenMessage(&ref); 654 window->Show(); 655 } 656 } /* end of else(file.InitCheck() == B_NO_ERROR */ 657 } 658 } 659 660 if (have_names) { 661 window = NewWindow(NULL, names.String()); 662 window->Show(); 663 } 664 } 665 666 667 TMailWindow * 668 TMailApp::FindWindow(const entry_ref &ref) 669 { 670 BEntry entry(&ref); 671 if (entry.InitCheck() < B_OK) 672 return NULL; 673 674 node_ref nodeRef; 675 if (entry.GetNodeRef(&nodeRef) < B_OK) 676 return NULL; 677 678 BWindow *window; 679 int32 index = 0; 680 while ((window = WindowAt(index++)) != NULL) { 681 TMailWindow *mailWindow = dynamic_cast<TMailWindow *>(window); 682 if (mailWindow == NULL) 683 continue; 684 685 node_ref mailNodeRef; 686 if (mailWindow->GetMailNodeRef(mailNodeRef) == B_OK 687 && mailNodeRef == nodeRef) 688 return mailWindow; 689 } 690 691 return NULL; 692 } 693 694 695 void 696 TMailApp::_CheckForSpamFilterExistence() 697 { 698 // Looks at the filter settings to see if the user is using a spam filter. 699 // If there is one there, set fShowSpamGUI to TRUE, otherwise to FALSE. 700 701 int32 addonNameIndex; 702 const char *addonNamePntr; 703 BDirectory inChainDir; 704 BPath path; 705 BEntry settingsEntry; 706 BFile settingsFile; 707 BMessage settingsMessage; 708 709 fShowSpamGUI = false; 710 711 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 712 return; 713 // TODO use new settings 714 path.Append("Mail/chains/inbound"); 715 if (inChainDir.SetTo(path.Path()) != B_OK) 716 return; 717 718 while (inChainDir.GetNextEntry (&settingsEntry, true /* traverse */) == B_OK) { 719 if (!settingsEntry.IsFile()) 720 continue; 721 if (settingsFile.SetTo (&settingsEntry, B_READ_ONLY) != B_OK) 722 continue; 723 if (settingsMessage.Unflatten (&settingsFile) != B_OK) 724 continue; 725 for (addonNameIndex = 0; B_OK == settingsMessage.FindString ( 726 "filter_addons", addonNameIndex, &addonNamePntr); 727 addonNameIndex++) { 728 if (strstr (addonNamePntr, "Spam Filter") != NULL) { 729 fShowSpamGUI = true; // Found it! 730 return; 731 } 732 } 733 } 734 } 735 736 737 void 738 TMailApp::SetPrintSettings(const BMessage* printSettings) 739 { 740 BAutolock _(this); 741 742 if (printSettings == fPrintSettings) 743 return; 744 745 delete fPrintSettings; 746 if (printSettings) 747 fPrintSettings = new BMessage(*printSettings); 748 else 749 fPrintSettings = NULL; 750 } 751 752 753 bool 754 TMailApp::HasPrintSettings() 755 { 756 BAutolock _(this); 757 return fPrintSettings != NULL; 758 } 759 760 761 BMessage 762 TMailApp::PrintSettings() 763 { 764 BAutolock _(this); 765 return BMessage(*fPrintSettings); 766 } 767 768 769 void 770 TMailApp::_ClearPrintSettings() 771 { 772 delete fPrintSettings; 773 fPrintSettings = NULL; 774 } 775 776 777 void 778 TMailApp::SetLastWindowFrame(BRect frame) 779 { 780 BAutolock _(this); 781 fLastMailWindowFrame = frame; 782 } 783 784 785 status_t 786 TMailApp::GetSettingsPath(BPath &path) 787 { 788 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 789 if (status != B_OK) 790 return status; 791 792 path.Append("Mail"); 793 return create_directory(path.Path(), 0755); 794 } 795 796 797 status_t 798 TMailApp::LoadOldSettings() 799 { 800 BPath path; 801 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 802 if (status != B_OK) 803 return status; 804 805 path.Append("Mail_data"); 806 807 BFile file; 808 status = file.SetTo(path.Path(), B_READ_ONLY); 809 if (status != B_OK) 810 return status; 811 812 file.Read(&fMailWindowFrame, sizeof(BRect)); 813 // file.Read(&level, sizeof(level)); 814 815 font_family fontFamily; 816 font_style fontStyle; 817 float size; 818 file.Read(&fontFamily, sizeof(font_family)); 819 file.Read(&fontStyle, sizeof(font_style)); 820 file.Read(&size, sizeof(float)); 821 if (size >= 9) 822 fContentFont.SetSize(size); 823 824 if (fontFamily[0] && fontStyle[0]) 825 fContentFont.SetFamilyAndStyle(fontFamily, fontStyle); 826 827 file.Read(&fSignatureWindowFrame, sizeof(BRect)); 828 file.Seek(1, SEEK_CUR); // ignore (bool) show header 829 file.Read(&fWrapMode, sizeof(bool)); 830 file.Read(&fPrefsWindowPos, sizeof(BPoint)); 831 832 int32 length; 833 if (file.Read(&length, sizeof(int32)) < (ssize_t)sizeof(int32)) 834 return B_IO_ERROR; 835 836 free(fSignature); 837 fSignature = NULL; 838 839 if (length > 0) { 840 fSignature = (char *)malloc(length); 841 if (fSignature == NULL) 842 return B_NO_MEMORY; 843 844 file.Read(fSignature, length); 845 } 846 847 file.Read(&fMailCharacterSet, sizeof(int32)); 848 if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION 849 && fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION 850 && BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL) 851 fMailCharacterSet = B_MS_WINDOWS_CONVERSION; 852 853 if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) { 854 char *findString = (char *)malloc(length + 1); 855 if (findString == NULL) 856 return B_NO_MEMORY; 857 858 file.Read(findString, length); 859 findString[length] = '\0'; 860 FindWindow::SetFindString(findString); 861 free(findString); 862 } 863 if (file.Read(&fShowButtonBar, sizeof(uint8)) < (ssize_t)sizeof(uint8)) 864 fShowButtonBar = true; 865 if (file.Read(&fUseAccountFrom, sizeof(int32)) < (ssize_t)sizeof(int32) 866 || fUseAccountFrom < ACCOUNT_USE_DEFAULT 867 || fUseAccountFrom > ACCOUNT_FROM_MAIL) 868 fUseAccountFrom = ACCOUNT_USE_DEFAULT; 869 if (file.Read(&fColoredQuotes, sizeof(bool)) < (ssize_t)sizeof(bool)) 870 fColoredQuotes = true; 871 872 if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) { 873 free(fReplyPreamble); 874 fReplyPreamble = (char *)malloc(length + 1); 875 if (fReplyPreamble == NULL) 876 return B_NO_MEMORY; 877 878 file.Read(fReplyPreamble, length); 879 fReplyPreamble[length] = '\0'; 880 } 881 882 file.Read(&fAttachAttributes, sizeof(bool)); 883 file.Read(&fWarnAboutUnencodableCharacters, sizeof(bool)); 884 885 return B_OK; 886 } 887 888 889 status_t 890 TMailApp::SaveSettings() 891 { 892 BMailSettings accountSettings; 893 894 if (fDefaultAccount != ~0L) { 895 accountSettings.SetDefaultOutboundAccount(fDefaultAccount); 896 accountSettings.Save(); 897 } 898 899 BPath path; 900 status_t status = GetSettingsPath(path); 901 if (status != B_OK) 902 return status; 903 904 path.Append("BeMail Settings~"); 905 906 BFile file; 907 status = file.SetTo(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 908 if (status != B_OK) 909 return status; 910 911 BMessage settings('BeMl'); 912 settings.AddRect("MailWindowSize", fMailWindowFrame); 913 // settings.AddInt32("ExperienceLevel", level); 914 915 font_family fontFamily; 916 font_style fontStyle; 917 fContentFont.GetFamilyAndStyle(&fontFamily, &fontStyle); 918 919 settings.AddString("FontFamily", fontFamily); 920 settings.AddString("FontStyle", fontStyle); 921 settings.AddFloat("FontSize", fContentFont.Size()); 922 923 settings.AddRect("SignatureWindowSize", fSignatureWindowFrame); 924 settings.AddBool("WordWrapMode", fWrapMode); 925 settings.AddPoint("PreferencesWindowLocation", fPrefsWindowPos); 926 settings.AddBool("AutoMarkRead", fAutoMarkRead); 927 settings.AddString("SignatureText", fSignature); 928 settings.AddInt32("CharacterSet", fMailCharacterSet); 929 settings.AddString("FindString", FindWindow::GetFindString()); 930 settings.AddInt8("ShowButtonBar", fShowButtonBar); 931 settings.AddInt32("UseAccountFrom", fUseAccountFrom); 932 settings.AddBool("ColoredQuotes", fColoredQuotes); 933 settings.AddString("ReplyPreamble", fReplyPreamble); 934 settings.AddBool("AttachAttributes", fAttachAttributes); 935 settings.AddBool("WarnAboutUnencodableCharacters", fWarnAboutUnencodableCharacters); 936 settings.AddBool("StartWithSpellCheck", fStartWithSpellCheckOn); 937 938 BEntry entry; 939 status = entry.SetTo(path.Path()); 940 if (status != B_OK) 941 return status; 942 943 status = settings.Flatten(&file); 944 if (status == B_OK) { 945 // replace original settings file 946 status = entry.Rename("BeMail Settings", true); 947 } else 948 entry.Remove(); 949 950 return status; 951 } 952 953 954 status_t 955 TMailApp::LoadSettings() 956 { 957 BMailSettings accountSettings; 958 fDefaultAccount = accountSettings.DefaultOutboundAccount(); 959 960 BPath path; 961 status_t status = GetSettingsPath(path); 962 if (status != B_OK) 963 return status; 964 965 path.Append("BeMail Settings"); 966 967 BFile file; 968 status = file.SetTo(path.Path(), B_READ_ONLY); 969 if (status != B_OK) 970 return LoadOldSettings(); 971 972 BMessage settings; 973 status = settings.Unflatten(&file); 974 if (status < B_OK || settings.what != 'BeMl') { 975 // the current settings are corrupted, try old ones 976 return LoadOldSettings(); 977 } 978 979 BRect rect; 980 if (settings.FindRect("MailWindowSize", &rect) == B_OK) 981 fMailWindowFrame = rect; 982 983 int32 int32Value; 984 // if (settings.FindInt32("ExperienceLevel", &int32Value) == B_OK) 985 // level = int32Value; 986 987 const char *fontFamily; 988 if (settings.FindString("FontFamily", &fontFamily) == B_OK) { 989 const char *fontStyle; 990 if (settings.FindString("FontStyle", &fontStyle) == B_OK) { 991 float size; 992 if (settings.FindFloat("FontSize", &size) == B_OK) { 993 if (size >= 7) 994 fContentFont.SetSize(size); 995 996 if (fontFamily[0] && fontStyle[0]) { 997 fContentFont.SetFamilyAndStyle(fontFamily[0] ? fontFamily : NULL, 998 fontStyle[0] ? fontStyle : NULL); 999 } 1000 } 1001 } 1002 } 1003 1004 if (settings.FindRect("SignatureWindowSize", &rect) == B_OK) 1005 fSignatureWindowFrame = rect; 1006 1007 bool boolValue; 1008 if (settings.FindBool("WordWrapMode", &boolValue) == B_OK) 1009 fWrapMode = boolValue; 1010 1011 BPoint point; 1012 if (settings.FindPoint("PreferencesWindowLocation", &point) == B_OK) 1013 fPrefsWindowPos = point; 1014 1015 if (settings.FindBool("AutoMarkRead", &boolValue) == B_OK) 1016 fAutoMarkRead = boolValue; 1017 1018 const char *string; 1019 if (settings.FindString("SignatureText", &string) == B_OK) { 1020 free(fSignature); 1021 fSignature = strdup(string); 1022 } 1023 1024 if (settings.FindInt32("CharacterSet", &int32Value) == B_OK) 1025 fMailCharacterSet = int32Value; 1026 if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION 1027 && fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION 1028 && BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL) 1029 fMailCharacterSet = B_MS_WINDOWS_CONVERSION; 1030 1031 if (settings.FindString("FindString", &string) == B_OK) 1032 FindWindow::SetFindString(string); 1033 1034 int8 int8Value; 1035 if (settings.FindInt8("ShowButtonBar", &int8Value) == B_OK) 1036 fShowButtonBar = int8Value; 1037 1038 if (settings.FindInt32("UseAccountFrom", &int32Value) == B_OK) 1039 fUseAccountFrom = int32Value; 1040 if (fUseAccountFrom < ACCOUNT_USE_DEFAULT 1041 || fUseAccountFrom > ACCOUNT_FROM_MAIL) 1042 fUseAccountFrom = ACCOUNT_USE_DEFAULT; 1043 1044 if (settings.FindBool("ColoredQuotes", &boolValue) == B_OK) 1045 fColoredQuotes = boolValue; 1046 1047 if (settings.FindString("ReplyPreamble", &string) == B_OK) { 1048 free(fReplyPreamble); 1049 fReplyPreamble = strdup(string); 1050 } 1051 1052 if (settings.FindBool("AttachAttributes", &boolValue) == B_OK) 1053 fAttachAttributes = boolValue; 1054 1055 if (settings.FindBool("WarnAboutUnencodableCharacters", &boolValue) == B_OK) 1056 fWarnAboutUnencodableCharacters = boolValue; 1057 1058 if (settings.FindBool("StartWithSpellCheck", &boolValue) == B_OK) 1059 fStartWithSpellCheckOn = boolValue; 1060 1061 return B_OK; 1062 } 1063 1064 1065 void 1066 TMailApp::FontChange() 1067 { 1068 int32 index = 0; 1069 BMessage msg; 1070 BWindow *window; 1071 1072 msg.what = CHANGE_FONT; 1073 msg.AddPointer("font", &fContentFont); 1074 1075 for (;;) { 1076 window = WindowAt(index++); 1077 if (!window) 1078 break; 1079 1080 window->PostMessage(&msg); 1081 } 1082 } 1083 1084 1085 TMailWindow* 1086 TMailApp::NewWindow(const entry_ref* ref, const char* to, bool resend, 1087 BMessenger* trackerMessenger) 1088 { 1089 BScreen screen(B_MAIN_SCREEN_ID); 1090 BRect screenFrame = screen.Frame(); 1091 1092 BRect r; 1093 if (fMailWindowFrame.Width() < 64 || fMailWindowFrame.Height() < 20) { 1094 // default size 1095 r.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH, 1096 TITLE_BAR_HEIGHT + WIND_HEIGHT); 1097 } else 1098 r = fMailWindowFrame; 1099 1100 // make sure the window is not larger than the screen space 1101 if (r.Height() > screenFrame.Height()) 1102 r.bottom = r.top + screenFrame.Height(); 1103 if (r.Width() > screenFrame.Width()) 1104 r.bottom = r.top + screenFrame.Width(); 1105 1106 // cascading windows 1107 r.OffsetBy(((fWindowCount + 5) % 10) * 15 - 75, 1108 ((fWindowCount + 5) % 10) * 15 - 75); 1109 1110 // make sure the window is still on screen 1111 if (r.left - 6 < screenFrame.left) 1112 r.OffsetTo(screenFrame.left + 8, r.top); 1113 1114 if (r.left + 20 > screenFrame.right) 1115 r.OffsetTo(6, r.top); 1116 1117 if (r.top - 26 < screenFrame.top) 1118 r.OffsetTo(r.left, screenFrame.top + 26); 1119 1120 if (r.top + 20 > screenFrame.bottom) 1121 r.OffsetTo(r.left, TITLE_BAR_HEIGHT); 1122 1123 if (r.Width() < WIND_WIDTH) 1124 r.right = r.left + WIND_WIDTH; 1125 1126 fWindowCount++; 1127 1128 BString title; 1129 BFile file; 1130 if (!resend && ref && file.SetTo(ref, O_RDONLY) == B_OK) { 1131 BString name; 1132 if (file.ReadAttrString(B_MAIL_ATTR_NAME, &name) == B_OK) { 1133 title << name; 1134 BString subject; 1135 if (file.ReadAttrString(B_MAIL_ATTR_SUBJECT, &subject) == B_OK) 1136 title << " -> " << subject; 1137 } 1138 } 1139 if (title == "") 1140 title = B_TRANSLATE_SYSTEM_NAME("Mail"); 1141 1142 TMailWindow* window = new TMailWindow(r, title.String(), this, ref, to, 1143 &fContentFont, resend, trackerMessenger); 1144 fWindowList.AddItem(window); 1145 1146 return window; 1147 } 1148 1149 1150 // #pragma mark - settings 1151 1152 1153 bool 1154 TMailApp::AutoMarkRead() 1155 { 1156 BAutolock _(this); 1157 return fAutoMarkRead; 1158 } 1159 1160 1161 BString 1162 TMailApp::Signature() 1163 { 1164 BAutolock _(this); 1165 return BString(fSignature); 1166 } 1167 1168 1169 BString 1170 TMailApp::ReplyPreamble() 1171 { 1172 BAutolock _(this); 1173 return BString(fReplyPreamble); 1174 } 1175 1176 1177 bool 1178 TMailApp::WrapMode() 1179 { 1180 BAutolock _(this); 1181 return fWrapMode; 1182 } 1183 1184 1185 bool 1186 TMailApp::AttachAttributes() 1187 { 1188 BAutolock _(this); 1189 return fAttachAttributes; 1190 } 1191 1192 1193 bool 1194 TMailApp::ColoredQuotes() 1195 { 1196 BAutolock _(this); 1197 return fColoredQuotes; 1198 } 1199 1200 1201 uint8 1202 TMailApp::ShowButtonBar() 1203 { 1204 BAutolock _(this); 1205 return fShowButtonBar; 1206 } 1207 1208 1209 bool 1210 TMailApp::WarnAboutUnencodableCharacters() 1211 { 1212 BAutolock _(this); 1213 return fWarnAboutUnencodableCharacters; 1214 } 1215 1216 1217 bool 1218 TMailApp::StartWithSpellCheckOn() 1219 { 1220 BAutolock _(this); 1221 return fStartWithSpellCheckOn; 1222 } 1223 1224 1225 void 1226 TMailApp::SetDefaultAccount(int32 account) 1227 { 1228 BAutolock _(this); 1229 fDefaultAccount = account; 1230 } 1231 1232 1233 int32 1234 TMailApp::DefaultAccount() 1235 { 1236 BAutolock _(this); 1237 return fDefaultAccount; 1238 } 1239 1240 1241 int32 1242 TMailApp::UseAccountFrom() 1243 { 1244 BAutolock _(this); 1245 return fUseAccountFrom; 1246 } 1247 1248 1249 uint32 1250 TMailApp::MailCharacterSet() 1251 { 1252 BAutolock _(this); 1253 return fMailCharacterSet; 1254 } 1255 1256 1257 BFont 1258 TMailApp::ContentFont() 1259 { 1260 BAutolock _(this); 1261 return fContentFont; 1262 } 1263 1264 1265 // #pragma mark - 1266 1267 1268 int 1269 main() 1270 { 1271 tzset(); 1272 1273 TMailApp().Run(); 1274 return B_OK; 1275 } 1276 1277