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