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