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