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, &fDefaultAccount, &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 // TODO use new settings 700 path.Append("Mail/chains/inbound"); 701 if (inChainDir.SetTo(path.Path()) != B_OK) 702 return; 703 704 while (inChainDir.GetNextEntry (&settingsEntry, true /* traverse */) == B_OK) { 705 if (!settingsEntry.IsFile()) 706 continue; 707 if (settingsFile.SetTo (&settingsEntry, B_READ_ONLY) != B_OK) 708 continue; 709 if (settingsMessage.Unflatten (&settingsFile) != B_OK) 710 continue; 711 for (addonNameIndex = 0; B_OK == settingsMessage.FindString ( 712 "filter_addons", addonNameIndex, &addonNamePntr); 713 addonNameIndex++) { 714 if (strstr (addonNamePntr, "Spam Filter") != NULL) { 715 fShowSpamGUI = true; // Found it! 716 return; 717 } 718 } 719 } 720 } 721 722 723 void 724 TMailApp::SetPrintSettings(const BMessage* printSettings) 725 { 726 BAutolock _(this); 727 728 if (printSettings == fPrintSettings) 729 return; 730 731 delete fPrintSettings; 732 if (printSettings) 733 fPrintSettings = new BMessage(*printSettings); 734 else 735 fPrintSettings = NULL; 736 } 737 738 739 bool 740 TMailApp::HasPrintSettings() 741 { 742 BAutolock _(this); 743 return fPrintSettings != NULL; 744 } 745 746 747 BMessage 748 TMailApp::PrintSettings() 749 { 750 BAutolock _(this); 751 return BMessage(*fPrintSettings); 752 } 753 754 755 void 756 TMailApp::_ClearPrintSettings() 757 { 758 delete fPrintSettings; 759 fPrintSettings = NULL; 760 } 761 762 763 void 764 TMailApp::SetLastWindowFrame(BRect frame) 765 { 766 BAutolock _(this); 767 fLastMailWindowFrame = frame; 768 } 769 770 771 status_t 772 TMailApp::GetSettingsPath(BPath &path) 773 { 774 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 775 if (status != B_OK) 776 return status; 777 778 path.Append("Mail"); 779 return create_directory(path.Path(), 0755); 780 } 781 782 783 status_t 784 TMailApp::LoadOldSettings() 785 { 786 BPath path; 787 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 788 if (status != B_OK) 789 return status; 790 791 path.Append("Mail_data"); 792 793 BFile file; 794 status = file.SetTo(path.Path(), B_READ_ONLY); 795 if (status != B_OK) 796 return status; 797 798 file.Read(&fMailWindowFrame, sizeof(BRect)); 799 // file.Read(&level, sizeof(level)); 800 801 font_family fontFamily; 802 font_style fontStyle; 803 float size; 804 file.Read(&fontFamily, sizeof(font_family)); 805 file.Read(&fontStyle, sizeof(font_style)); 806 file.Read(&size, sizeof(float)); 807 if (size >= 9) 808 fContentFont.SetSize(size); 809 810 if (fontFamily[0] && fontStyle[0]) 811 fContentFont.SetFamilyAndStyle(fontFamily, fontStyle); 812 813 file.Read(&fSignatureWindowFrame, sizeof(BRect)); 814 file.Seek(1, SEEK_CUR); // ignore (bool) show header 815 file.Read(&fWrapMode, sizeof(bool)); 816 file.Read(&fPrefsWindowPos, sizeof(BPoint)); 817 818 int32 length; 819 if (file.Read(&length, sizeof(int32)) < (ssize_t)sizeof(int32)) 820 return B_IO_ERROR; 821 822 free(fSignature); 823 fSignature = NULL; 824 825 if (length > 0) { 826 fSignature = (char *)malloc(length); 827 if (fSignature == NULL) 828 return B_NO_MEMORY; 829 830 file.Read(fSignature, length); 831 } 832 833 file.Read(&fMailCharacterSet, sizeof(int32)); 834 if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION 835 && fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION 836 && BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL) 837 fMailCharacterSet = B_MS_WINDOWS_CONVERSION; 838 839 if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) { 840 char *findString = (char *)malloc(length + 1); 841 if (findString == NULL) 842 return B_NO_MEMORY; 843 844 file.Read(findString, length); 845 findString[length] = '\0'; 846 FindWindow::SetFindString(findString); 847 free(findString); 848 } 849 if (file.Read(&fShowButtonBar, sizeof(uint8)) < (ssize_t)sizeof(uint8)) 850 fShowButtonBar = true; 851 if (file.Read(&fUseAccountFrom, sizeof(int32)) < (ssize_t)sizeof(int32) 852 || fUseAccountFrom < ACCOUNT_USE_DEFAULT 853 || fUseAccountFrom > ACCOUNT_FROM_MAIL) 854 fUseAccountFrom = ACCOUNT_USE_DEFAULT; 855 if (file.Read(&fColoredQuotes, sizeof(bool)) < (ssize_t)sizeof(bool)) 856 fColoredQuotes = true; 857 858 if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) { 859 free(fReplyPreamble); 860 fReplyPreamble = (char *)malloc(length + 1); 861 if (fReplyPreamble == NULL) 862 return B_NO_MEMORY; 863 864 file.Read(fReplyPreamble, length); 865 fReplyPreamble[length] = '\0'; 866 } 867 868 file.Read(&fAttachAttributes, sizeof(bool)); 869 file.Read(&fWarnAboutUnencodableCharacters, sizeof(bool)); 870 871 return B_OK; 872 } 873 874 875 status_t 876 TMailApp::SaveSettings() 877 { 878 BMailSettings accountSettings; 879 880 if (fDefaultAccount != ~0L) { 881 accountSettings.SetDefaultOutboundAccount(fDefaultAccount); 882 accountSettings.Save(); 883 } 884 885 BPath path; 886 status_t status = GetSettingsPath(path); 887 if (status != B_OK) 888 return status; 889 890 path.Append("BeMail Settings~"); 891 892 BFile file; 893 status = file.SetTo(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 894 if (status != B_OK) 895 return status; 896 897 BMessage settings('BeMl'); 898 settings.AddRect("MailWindowSize", fMailWindowFrame); 899 // settings.AddInt32("ExperienceLevel", level); 900 901 font_family fontFamily; 902 font_style fontStyle; 903 fContentFont.GetFamilyAndStyle(&fontFamily, &fontStyle); 904 905 settings.AddString("FontFamily", fontFamily); 906 settings.AddString("FontStyle", fontStyle); 907 settings.AddFloat("FontSize", fContentFont.Size()); 908 909 settings.AddRect("SignatureWindowSize", fSignatureWindowFrame); 910 settings.AddBool("WordWrapMode", fWrapMode); 911 settings.AddPoint("PreferencesWindowLocation", fPrefsWindowPos); 912 settings.AddBool("AutoMarkRead", fAutoMarkRead); 913 settings.AddString("SignatureText", fSignature); 914 settings.AddInt32("CharacterSet", fMailCharacterSet); 915 settings.AddString("FindString", FindWindow::GetFindString()); 916 settings.AddInt8("ShowButtonBar", fShowButtonBar); 917 settings.AddInt32("UseAccountFrom", fUseAccountFrom); 918 settings.AddBool("ColoredQuotes", fColoredQuotes); 919 settings.AddString("ReplyPreamble", fReplyPreamble); 920 settings.AddBool("AttachAttributes", fAttachAttributes); 921 settings.AddBool("WarnAboutUnencodableCharacters", fWarnAboutUnencodableCharacters); 922 settings.AddBool("StartWithSpellCheck", fStartWithSpellCheckOn); 923 924 BEntry entry; 925 status = entry.SetTo(path.Path()); 926 if (status != B_OK) 927 return status; 928 929 status = settings.Flatten(&file); 930 if (status == B_OK) { 931 // replace original settings file 932 status = entry.Rename("BeMail Settings", true); 933 } else 934 entry.Remove(); 935 936 return status; 937 } 938 939 940 status_t 941 TMailApp::LoadSettings() 942 { 943 BMailSettings accountSettings; 944 fDefaultAccount = accountSettings.DefaultOutboundAccount(); 945 946 BPath path; 947 status_t status = GetSettingsPath(path); 948 if (status != B_OK) 949 return status; 950 951 path.Append("BeMail Settings"); 952 953 BFile file; 954 status = file.SetTo(path.Path(), B_READ_ONLY); 955 if (status != B_OK) 956 return LoadOldSettings(); 957 958 BMessage settings; 959 status = settings.Unflatten(&file); 960 if (status < B_OK || settings.what != 'BeMl') { 961 // the current settings are corrupted, try old ones 962 return LoadOldSettings(); 963 } 964 965 BRect rect; 966 if (settings.FindRect("MailWindowSize", &rect) == B_OK) 967 fMailWindowFrame = rect; 968 969 int32 int32Value; 970 // if (settings.FindInt32("ExperienceLevel", &int32Value) == B_OK) 971 // level = int32Value; 972 973 const char *fontFamily; 974 if (settings.FindString("FontFamily", &fontFamily) == B_OK) { 975 const char *fontStyle; 976 if (settings.FindString("FontStyle", &fontStyle) == B_OK) { 977 float size; 978 if (settings.FindFloat("FontSize", &size) == B_OK) { 979 if (size >= 7) 980 fContentFont.SetSize(size); 981 982 if (fontFamily[0] && fontStyle[0]) { 983 fContentFont.SetFamilyAndStyle(fontFamily[0] ? fontFamily : NULL, 984 fontStyle[0] ? fontStyle : NULL); 985 } 986 } 987 } 988 } 989 990 if (settings.FindRect("SignatureWindowSize", &rect) == B_OK) 991 fSignatureWindowFrame = rect; 992 993 bool boolValue; 994 if (settings.FindBool("WordWrapMode", &boolValue) == B_OK) 995 fWrapMode = boolValue; 996 997 BPoint point; 998 if (settings.FindPoint("PreferencesWindowLocation", &point) == B_OK) 999 fPrefsWindowPos = point; 1000 1001 if (settings.FindBool("AutoMarkRead", &boolValue) == B_OK) 1002 fAutoMarkRead = boolValue; 1003 1004 const char *string; 1005 if (settings.FindString("SignatureText", &string) == B_OK) { 1006 free(fSignature); 1007 fSignature = strdup(string); 1008 } 1009 1010 if (settings.FindInt32("CharacterSet", &int32Value) == B_OK) 1011 fMailCharacterSet = int32Value; 1012 if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION 1013 && fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION 1014 && BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL) 1015 fMailCharacterSet = B_MS_WINDOWS_CONVERSION; 1016 1017 if (settings.FindString("FindString", &string) == B_OK) 1018 FindWindow::SetFindString(string); 1019 1020 int8 int8Value; 1021 if (settings.FindInt8("ShowButtonBar", &int8Value) == B_OK) 1022 fShowButtonBar = int8Value; 1023 1024 if (settings.FindInt32("UseAccountFrom", &int32Value) == B_OK) 1025 fUseAccountFrom = int32Value; 1026 if (fUseAccountFrom < ACCOUNT_USE_DEFAULT 1027 || fUseAccountFrom > ACCOUNT_FROM_MAIL) 1028 fUseAccountFrom = ACCOUNT_USE_DEFAULT; 1029 1030 if (settings.FindBool("ColoredQuotes", &boolValue) == B_OK) 1031 fColoredQuotes = boolValue; 1032 1033 if (settings.FindString("ReplyPreamble", &string) == B_OK) { 1034 free(fReplyPreamble); 1035 fReplyPreamble = strdup(string); 1036 } 1037 1038 if (settings.FindBool("AttachAttributes", &boolValue) == B_OK) 1039 fAttachAttributes = boolValue; 1040 1041 if (settings.FindBool("WarnAboutUnencodableCharacters", &boolValue) == B_OK) 1042 fWarnAboutUnencodableCharacters = boolValue; 1043 1044 if (settings.FindBool("StartWithSpellCheck", &boolValue) == B_OK) 1045 fStartWithSpellCheckOn = boolValue; 1046 1047 return B_OK; 1048 } 1049 1050 1051 void 1052 TMailApp::FontChange() 1053 { 1054 int32 index = 0; 1055 BMessage msg; 1056 BWindow *window; 1057 1058 msg.what = CHANGE_FONT; 1059 msg.AddPointer("font", &fContentFont); 1060 1061 for (;;) { 1062 window = WindowAt(index++); 1063 if (!window) 1064 break; 1065 1066 window->PostMessage(&msg); 1067 } 1068 } 1069 1070 1071 TMailWindow* 1072 TMailApp::NewWindow(const entry_ref* ref, const char* to, bool resend, 1073 BMessenger* trackerMessenger) 1074 { 1075 BScreen screen(B_MAIN_SCREEN_ID); 1076 BRect screenFrame = screen.Frame(); 1077 1078 BRect r; 1079 if (fMailWindowFrame.Width() < 64 || fMailWindowFrame.Height() < 20) { 1080 // default size 1081 r.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH, 1082 TITLE_BAR_HEIGHT + WIND_HEIGHT); 1083 } else 1084 r = fMailWindowFrame; 1085 1086 // make sure the window is not larger than the screen space 1087 if (r.Height() > screenFrame.Height()) 1088 r.bottom = r.top + screenFrame.Height(); 1089 if (r.Width() > screenFrame.Width()) 1090 r.bottom = r.top + screenFrame.Width(); 1091 1092 // cascading windows 1093 r.OffsetBy(((fWindowCount + 5) % 10) * 15 - 75, 1094 ((fWindowCount + 5) % 10) * 15 - 75); 1095 1096 // make sure the window is still on screen 1097 if (r.left - 6 < screenFrame.left) 1098 r.OffsetTo(screenFrame.left + 8, r.top); 1099 1100 if (r.left + 20 > screenFrame.right) 1101 r.OffsetTo(6, r.top); 1102 1103 if (r.top - 26 < screenFrame.top) 1104 r.OffsetTo(r.left, screenFrame.top + 26); 1105 1106 if (r.top + 20 > screenFrame.bottom) 1107 r.OffsetTo(r.left, TITLE_BAR_HEIGHT); 1108 1109 if (r.Width() < WIND_WIDTH) 1110 r.right = r.left + WIND_WIDTH; 1111 1112 fWindowCount++; 1113 1114 BString title; 1115 BFile file; 1116 if (!resend && ref && file.SetTo(ref, O_RDONLY) == B_OK) { 1117 BString name; 1118 if (ReadAttrString(&file, B_MAIL_ATTR_NAME, &name) == B_OK) { 1119 title << name; 1120 BString subject; 1121 if (ReadAttrString(&file, B_MAIL_ATTR_SUBJECT, &subject) == B_OK) 1122 title << " -> " << subject; 1123 } 1124 } 1125 if (title == "") 1126 title = B_TRANSLATE("Mail"); 1127 1128 TMailWindow* window = new TMailWindow(r, title.String(), this, ref, to, 1129 &fContentFont, resend, trackerMessenger); 1130 fWindowList.AddItem(window); 1131 1132 return window; 1133 } 1134 1135 1136 // #pragma mark - settings 1137 1138 1139 bool 1140 TMailApp::AutoMarkRead() 1141 { 1142 BAutolock _(this); 1143 return fAutoMarkRead; 1144 } 1145 1146 1147 BString 1148 TMailApp::Signature() 1149 { 1150 BAutolock _(this); 1151 return BString(fSignature); 1152 } 1153 1154 1155 BString 1156 TMailApp::ReplyPreamble() 1157 { 1158 BAutolock _(this); 1159 return BString(fReplyPreamble); 1160 } 1161 1162 1163 bool 1164 TMailApp::WrapMode() 1165 { 1166 BAutolock _(this); 1167 return fWrapMode; 1168 } 1169 1170 1171 bool 1172 TMailApp::AttachAttributes() 1173 { 1174 BAutolock _(this); 1175 return fAttachAttributes; 1176 } 1177 1178 1179 bool 1180 TMailApp::ColoredQuotes() 1181 { 1182 BAutolock _(this); 1183 return fColoredQuotes; 1184 } 1185 1186 1187 uint8 1188 TMailApp::ShowButtonBar() 1189 { 1190 BAutolock _(this); 1191 return fShowButtonBar; 1192 } 1193 1194 1195 bool 1196 TMailApp::WarnAboutUnencodableCharacters() 1197 { 1198 BAutolock _(this); 1199 return fWarnAboutUnencodableCharacters; 1200 } 1201 1202 1203 bool 1204 TMailApp::StartWithSpellCheckOn() 1205 { 1206 BAutolock _(this); 1207 return fStartWithSpellCheckOn; 1208 } 1209 1210 1211 void 1212 TMailApp::SetDefaultAccount(int32 account) 1213 { 1214 BAutolock _(this); 1215 fDefaultAccount = account; 1216 } 1217 1218 1219 int32 1220 TMailApp::DefaultAccount() 1221 { 1222 BAutolock _(this); 1223 return fDefaultAccount; 1224 } 1225 1226 1227 int32 1228 TMailApp::UseAccountFrom() 1229 { 1230 BAutolock _(this); 1231 return fUseAccountFrom; 1232 } 1233 1234 1235 uint32 1236 TMailApp::MailCharacterSet() 1237 { 1238 BAutolock _(this); 1239 return fMailCharacterSet; 1240 } 1241 1242 1243 BFont 1244 TMailApp::ContentFont() 1245 { 1246 BAutolock _(this); 1247 return fContentFont; 1248 } 1249 1250 1251 // #pragma mark - 1252 1253 1254 int 1255 main() 1256 { 1257 tzset(); 1258 1259 TMailApp().Run(); 1260 return B_OK; 1261 } 1262 1263