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