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