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