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