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 } 427 } 428 429 430 bool 431 TMailApp::QuitRequested() 432 { 433 if (!BApplication::QuitRequested()) 434 return false; 435 436 fMailWindowFrame = fLastMailWindowFrame; 437 // Last closed window becomes standard window size. 438 439 // Shut down the spam server if it's still running. If the user has trained it on a message, it will stay 440 // open. This is actually a good thing if there's quite a bit of spam -- no waiting for the thing to start 441 // up for each message, but it has no business staying that way if the user isn't doing anything with e-mail. :) 442 if (be_roster->IsRunning(kSpamServerSignature)) { 443 team_id serverTeam = be_roster->TeamFor(kSpamServerSignature); 444 if (serverTeam >= 0) { 445 int32 errorCode = B_SERVER_NOT_FOUND; 446 BMessenger messengerToSpamServer(kSpamServerSignature, serverTeam, &errorCode); 447 if (messengerToSpamServer.IsValid()) { 448 BMessage quitMessage(B_QUIT_REQUESTED); 449 messengerToSpamServer.SendMessage(&quitMessage); 450 } 451 } 452 453 } 454 455 SaveSettings(); 456 return true; 457 } 458 459 460 void 461 TMailApp::ReadyToRun() 462 { 463 // Create needed indices for META:group, META:email, MAIL:draft, 464 // INDEX_SIGNATURE, INDEX_STATUS on the boot volume 465 466 BVolume volume; 467 BVolumeRoster().GetBootVolume(&volume); 468 469 fs_create_index(volume.Device(), "META:group", B_STRING_TYPE, 0); 470 fs_create_index(volume.Device(), "META:email", B_STRING_TYPE, 0); 471 fs_create_index(volume.Device(), "MAIL:draft", B_INT32_TYPE, 0); 472 fs_create_index(volume.Device(), INDEX_SIGNATURE, B_STRING_TYPE, 0); 473 fs_create_index(volume.Device(), INDEX_STATUS, B_STRING_TYPE, 0); 474 fs_create_index(volume.Device(), B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0); 475 476 // Start people queries 477 fPeopleQueryList.Init("META:email=**"); 478 479 // Load dictionaries 480 BPath indexDir; 481 BPath dictionaryDir; 482 BPath userDictionaryDir; 483 BPath userIndexDir; 484 BPath dataPath; 485 BPath indexPath; 486 BDirectory directory; 487 BEntry entry; 488 489 // Locate dictionaries directory 490 find_directory(B_SYSTEM_DATA_DIRECTORY, &indexDir, true); 491 indexDir.Append("spell_check"); 492 dictionaryDir = indexDir; 493 494 //Locate user dictionary directory 495 find_directory(B_USER_CONFIG_DIRECTORY, &userIndexDir, true); 496 userIndexDir.Append("data/spell_check"); 497 userDictionaryDir = userIndexDir; 498 499 // Create directory if needed 500 directory.CreateDirectory(userIndexDir.Path(), NULL); 501 502 // Setup directory paths 503 indexDir.Append(kIndexDirectory); 504 dictionaryDir.Append(kDictDirectory); 505 userIndexDir.Append(kIndexDirectory); 506 userDictionaryDir.Append(kDictDirectory); 507 508 // Create directories if needed 509 directory.CreateDirectory(indexDir.Path(), NULL); 510 directory.CreateDirectory(dictionaryDir.Path(), NULL); 511 directory.CreateDirectory(userIndexDir.Path(), NULL); 512 directory.CreateDirectory(userDictionaryDir.Path(), NULL); 513 514 dataPath = dictionaryDir; 515 dataPath.Append("words"); 516 517 // Only Load if Words Dictionary 518 if (BEntry(kWordsPath).Exists() || BEntry(dataPath.Path()).Exists()) { 519 // If "/boot/optional/goodies/words" exists but there is no 520 // system dictionary, copy words 521 if (!BEntry(dataPath.Path()).Exists() && BEntry(kWordsPath).Exists()) { 522 BFile words(kWordsPath, B_READ_ONLY); 523 BFile copy(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE); 524 char buffer[4096]; 525 ssize_t size; 526 527 while ((size = words.Read( buffer, 4096)) > 0) 528 copy.Write(buffer, size); 529 BNodeInfo(©).SetType("text/plain"); 530 } 531 532 // Load dictionaries 533 directory.SetTo(dictionaryDir.Path()); 534 535 BString leafName; 536 gUserDict = -1; 537 538 while (gDictCount < MAX_DICTIONARIES 539 && directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) { 540 dataPath.SetTo(&entry); 541 542 indexPath = indexDir; 543 leafName.SetTo(dataPath.Leaf()); 544 leafName.Append(kMetaphone); 545 indexPath.Append(leafName.String()); 546 gWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), true); 547 548 indexPath = indexDir; 549 leafName.SetTo(dataPath.Leaf()); 550 leafName.Append(kExact); 551 indexPath.Append(leafName.String()); 552 gExactWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), false); 553 gDictCount++; 554 } 555 556 // Create user dictionary if it does not exist 557 dataPath = userDictionaryDir; 558 dataPath.Append("user"); 559 if (!BEntry(dataPath.Path()).Exists()) { 560 BFile user(dataPath.Path(), B_WRITE_ONLY | B_CREATE_FILE); 561 BNodeInfo(&user).SetType("text/plain"); 562 } 563 564 // Load user dictionary 565 if (BEntry(userDictionaryDir.Path()).Exists()) { 566 gUserDictFile = new BFile(dataPath.Path(), B_WRITE_ONLY | B_OPEN_AT_END); 567 gUserDict = gDictCount; 568 569 indexPath = userIndexDir; 570 leafName.SetTo(dataPath.Leaf()); 571 leafName.Append(kMetaphone); 572 indexPath.Append(leafName.String()); 573 gWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), true); 574 575 indexPath = userIndexDir; 576 leafName.SetTo(dataPath.Leaf()); 577 leafName.Append(kExact); 578 indexPath.Append(leafName.String()); 579 gExactWords[gDictCount] = new Words(dataPath.Path(), indexPath.Path(), false); 580 gDictCount++; 581 } 582 } 583 584 // Create a new window if starting up without any extra arguments. 585 586 if (!fPrintHelpAndExit && !fWindowCount) { 587 TMailWindow *window; 588 window = NewWindow(); 589 window->Show(); 590 } 591 } 592 593 594 void 595 TMailApp::RefsReceived(BMessage *msg) 596 { 597 bool have_names = false; 598 BString names; 599 char type[B_FILE_NAME_LENGTH]; 600 int32 item = 0; 601 BFile file; 602 TMailWindow *window; 603 entry_ref ref; 604 605 // 606 // If a tracker window opened me, get a messenger from it. 607 // 608 BMessenger messenger; 609 if (msg->HasMessenger("TrackerViewToken")) 610 msg->FindMessenger("TrackerViewToken", &messenger); 611 612 while (msg->HasRef("refs", item)) { 613 msg->FindRef("refs", item++, &ref); 614 if ((window = FindWindow(ref)) != NULL) 615 window->Activate(true); 616 else { 617 file.SetTo(&ref, O_RDONLY); 618 if (file.InitCheck() == B_NO_ERROR) { 619 BNodeInfo node(&file); 620 node.GetType(type); 621 if (strcmp(type, B_MAIL_TYPE) == 0 622 || strcmp(type, B_PARTIAL_MAIL_TYPE) == 0) { 623 window = NewWindow(&ref, NULL, false, &messenger); 624 window->Show(); 625 } else if(strcmp(type, "application/x-person") == 0) { 626 /* Got a People contact info file, see if it has an Email address. */ 627 BString name; 628 BString email; 629 attr_info info; 630 char *attrib; 631 632 if (file.GetAttrInfo("META:email", &info) == B_NO_ERROR) { 633 attrib = (char *) malloc(info.size + 1); 634 file.ReadAttr("META:email", B_STRING_TYPE, 0, attrib, info.size); 635 attrib[info.size] = 0; // Just in case it wasn't NUL terminated. 636 email << attrib; 637 free(attrib); 638 639 /* we got something... */ 640 if (email.Length() > 0) { 641 /* see if we can get a username as well */ 642 if(file.GetAttrInfo("META:name", &info) == B_NO_ERROR) { 643 attrib = (char *) malloc(info.size + 1); 644 file.ReadAttr("META:name", B_STRING_TYPE, 0, attrib, info.size); 645 attrib[info.size] = 0; // Just in case it wasn't NUL terminated. 646 name << "\"" << attrib << "\" "; 647 email.Prepend("<"); 648 email.Append(">"); 649 free(attrib); 650 } 651 652 if (names.Length() == 0) { 653 names << name << email; 654 } else { 655 names << ", " << name << email; 656 } 657 have_names = true; 658 email.SetTo(""); 659 name.SetTo(""); 660 } 661 } 662 } 663 else if (strcmp(type, kDraftType) == 0) { 664 window = NewWindow(); 665 666 // If it's a draft message, open it 667 window->OpenMessage(&ref); 668 window->Show(); 669 } 670 } /* end of else(file.InitCheck() == B_NO_ERROR */ 671 } 672 } 673 674 if (have_names) { 675 window = NewWindow(NULL, names.String()); 676 window->Show(); 677 } 678 } 679 680 681 TMailWindow * 682 TMailApp::FindWindow(const entry_ref &ref) 683 { 684 BEntry entry(&ref); 685 if (entry.InitCheck() < B_OK) 686 return NULL; 687 688 node_ref nodeRef; 689 if (entry.GetNodeRef(&nodeRef) < B_OK) 690 return NULL; 691 692 BWindow *window; 693 int32 index = 0; 694 while ((window = WindowAt(index++)) != NULL) { 695 TMailWindow *mailWindow = dynamic_cast<TMailWindow *>(window); 696 if (mailWindow == NULL) 697 continue; 698 699 node_ref mailNodeRef; 700 if (mailWindow->GetMailNodeRef(mailNodeRef) == B_OK 701 && mailNodeRef == nodeRef) 702 return mailWindow; 703 } 704 705 return NULL; 706 } 707 708 709 void 710 TMailApp::_CheckForSpamFilterExistence() 711 { 712 // Looks at the filter settings to see if the user is using a spam filter. 713 // If there is one there, set fShowSpamGUI to TRUE, otherwise to FALSE. 714 715 int32 addonNameIndex; 716 const char *addonNamePntr; 717 BDirectory inChainDir; 718 BPath path; 719 BEntry settingsEntry; 720 BFile settingsFile; 721 BMessage settingsMessage; 722 723 fShowSpamGUI = false; 724 725 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 726 return; 727 // TODO use new settings 728 path.Append("Mail/chains/inbound"); 729 if (inChainDir.SetTo(path.Path()) != B_OK) 730 return; 731 732 while (inChainDir.GetNextEntry (&settingsEntry, true /* traverse */) == B_OK) { 733 if (!settingsEntry.IsFile()) 734 continue; 735 if (settingsFile.SetTo (&settingsEntry, B_READ_ONLY) != B_OK) 736 continue; 737 if (settingsMessage.Unflatten (&settingsFile) != B_OK) 738 continue; 739 for (addonNameIndex = 0; B_OK == settingsMessage.FindString ( 740 "filter_addons", addonNameIndex, &addonNamePntr); 741 addonNameIndex++) { 742 if (strstr (addonNamePntr, "Spam Filter") != NULL) { 743 fShowSpamGUI = true; // Found it! 744 return; 745 } 746 } 747 } 748 } 749 750 751 TMailWindow* 752 TMailApp::_ActiveWindow() 753 { 754 for (int32 i = 0; i < fWindowList.CountItems(); i++) { 755 TMailWindow* window = (TMailWindow*)fWindowList.ItemAt(i); 756 if (window->IsActive()) 757 return window; 758 } 759 return NULL; 760 } 761 762 763 void 764 TMailApp::SetPrintSettings(const BMessage* printSettings) 765 { 766 BAutolock _(this); 767 768 if (printSettings == fPrintSettings) 769 return; 770 771 delete fPrintSettings; 772 if (printSettings) 773 fPrintSettings = new BMessage(*printSettings); 774 else 775 fPrintSettings = NULL; 776 } 777 778 779 bool 780 TMailApp::HasPrintSettings() 781 { 782 BAutolock _(this); 783 return fPrintSettings != NULL; 784 } 785 786 787 BMessage 788 TMailApp::PrintSettings() 789 { 790 BAutolock _(this); 791 return BMessage(*fPrintSettings); 792 } 793 794 795 void 796 TMailApp::_ClearPrintSettings() 797 { 798 delete fPrintSettings; 799 fPrintSettings = NULL; 800 } 801 802 803 void 804 TMailApp::SetLastWindowFrame(BRect frame) 805 { 806 BAutolock _(this); 807 fLastMailWindowFrame = frame; 808 } 809 810 811 status_t 812 TMailApp::GetSettingsPath(BPath &path) 813 { 814 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 815 if (status != B_OK) 816 return status; 817 818 path.Append("Mail"); 819 return create_directory(path.Path(), 0755); 820 } 821 822 823 status_t 824 TMailApp::LoadOldSettings() 825 { 826 BPath path; 827 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 828 if (status != B_OK) 829 return status; 830 831 path.Append("Mail_data"); 832 833 BFile file; 834 status = file.SetTo(path.Path(), B_READ_ONLY); 835 if (status != B_OK) 836 return status; 837 838 file.Read(&fMailWindowFrame, sizeof(BRect)); 839 // file.Read(&level, sizeof(level)); 840 841 font_family fontFamily; 842 font_style fontStyle; 843 float size; 844 file.Read(&fontFamily, sizeof(font_family)); 845 file.Read(&fontStyle, sizeof(font_style)); 846 file.Read(&size, sizeof(float)); 847 if (size >= 9) 848 fContentFont.SetSize(size); 849 850 if (fontFamily[0] && fontStyle[0]) 851 fContentFont.SetFamilyAndStyle(fontFamily, fontStyle); 852 853 file.Read(&fSignatureWindowFrame, sizeof(BRect)); 854 file.Seek(1, SEEK_CUR); // ignore (bool) show header 855 file.Read(&fWrapMode, sizeof(bool)); 856 file.Read(&fPrefsWindowPos, sizeof(BPoint)); 857 858 int32 length; 859 if (file.Read(&length, sizeof(int32)) < (ssize_t)sizeof(int32)) 860 return B_IO_ERROR; 861 862 free(fSignature); 863 fSignature = NULL; 864 865 if (length > 0) { 866 fSignature = (char *)malloc(length); 867 if (fSignature == NULL) 868 return B_NO_MEMORY; 869 870 file.Read(fSignature, length); 871 } 872 873 file.Read(&fMailCharacterSet, sizeof(int32)); 874 if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION 875 && fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION 876 && BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL) 877 fMailCharacterSet = B_MS_WINDOWS_CONVERSION; 878 879 if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) { 880 char *findString = (char *)malloc(length + 1); 881 if (findString == NULL) 882 return B_NO_MEMORY; 883 884 file.Read(findString, length); 885 findString[length] = '\0'; 886 FindWindow::SetFindString(findString); 887 free(findString); 888 } 889 if (file.Read(&fShowToolBar, sizeof(uint8)) < (ssize_t)sizeof(uint8)) 890 fShowToolBar = true; 891 if (file.Read(&fUseAccountFrom, sizeof(int32)) < (ssize_t)sizeof(int32) 892 || fUseAccountFrom < ACCOUNT_USE_DEFAULT 893 || fUseAccountFrom > ACCOUNT_FROM_MAIL) 894 fUseAccountFrom = ACCOUNT_USE_DEFAULT; 895 if (file.Read(&fColoredQuotes, sizeof(bool)) < (ssize_t)sizeof(bool)) 896 fColoredQuotes = true; 897 898 if (file.Read(&length, sizeof(int32)) == (ssize_t)sizeof(int32)) { 899 free(fReplyPreamble); 900 fReplyPreamble = (char *)malloc(length + 1); 901 if (fReplyPreamble == NULL) 902 return B_NO_MEMORY; 903 904 file.Read(fReplyPreamble, length); 905 fReplyPreamble[length] = '\0'; 906 } 907 908 file.Read(&fAttachAttributes, sizeof(bool)); 909 file.Read(&fWarnAboutUnencodableCharacters, sizeof(bool)); 910 911 return B_OK; 912 } 913 914 915 status_t 916 TMailApp::SaveSettings() 917 { 918 BMailSettings accountSettings; 919 920 if (fDefaultAccount != ~0L) { 921 accountSettings.SetDefaultOutboundAccount(fDefaultAccount); 922 accountSettings.Save(); 923 } 924 925 BPath path; 926 status_t status = GetSettingsPath(path); 927 if (status != B_OK) 928 return status; 929 930 path.Append("BeMail Settings~"); 931 932 BFile file; 933 status = file.SetTo(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 934 if (status != B_OK) 935 return status; 936 937 BMessage settings('BeMl'); 938 settings.AddRect("MailWindowSize", fMailWindowFrame); 939 // settings.AddInt32("ExperienceLevel", level); 940 941 font_family fontFamily; 942 font_style fontStyle; 943 fContentFont.GetFamilyAndStyle(&fontFamily, &fontStyle); 944 945 settings.AddString("FontFamily", fontFamily); 946 settings.AddString("FontStyle", fontStyle); 947 settings.AddFloat("FontSize", fContentFont.Size()); 948 949 settings.AddRect("SignatureWindowSize", fSignatureWindowFrame); 950 settings.AddBool("WordWrapMode", fWrapMode); 951 settings.AddPoint("PreferencesWindowLocation", fPrefsWindowPos); 952 settings.AddBool("AutoMarkRead", fAutoMarkRead); 953 settings.AddString("SignatureText", fSignature); 954 settings.AddInt32("CharacterSet", fMailCharacterSet); 955 settings.AddString("FindString", FindWindow::GetFindString()); 956 settings.AddInt8("ShowButtonBar", fShowToolBar); 957 settings.AddInt32("UseAccountFrom", fUseAccountFrom); 958 settings.AddBool("ColoredQuotes", fColoredQuotes); 959 settings.AddString("ReplyPreamble", fReplyPreamble); 960 settings.AddBool("AttachAttributes", fAttachAttributes); 961 settings.AddBool("WarnAboutUnencodableCharacters", fWarnAboutUnencodableCharacters); 962 settings.AddBool("StartWithSpellCheck", fStartWithSpellCheckOn); 963 964 BEntry entry; 965 status = entry.SetTo(path.Path()); 966 if (status != B_OK) 967 return status; 968 969 status = settings.Flatten(&file); 970 if (status == B_OK) { 971 // replace original settings file 972 status = entry.Rename("BeMail Settings", true); 973 } else 974 entry.Remove(); 975 976 return status; 977 } 978 979 980 status_t 981 TMailApp::LoadSettings() 982 { 983 BMailSettings accountSettings; 984 fDefaultAccount = accountSettings.DefaultOutboundAccount(); 985 986 BPath path; 987 status_t status = GetSettingsPath(path); 988 if (status != B_OK) 989 return status; 990 991 path.Append("BeMail Settings"); 992 993 BFile file; 994 status = file.SetTo(path.Path(), B_READ_ONLY); 995 if (status != B_OK) 996 return LoadOldSettings(); 997 998 BMessage settings; 999 status = settings.Unflatten(&file); 1000 if (status < B_OK || settings.what != 'BeMl') { 1001 // the current settings are corrupted, try old ones 1002 return LoadOldSettings(); 1003 } 1004 1005 BRect rect; 1006 if (settings.FindRect("MailWindowSize", &rect) == B_OK) 1007 fMailWindowFrame = rect; 1008 1009 int32 int32Value; 1010 // if (settings.FindInt32("ExperienceLevel", &int32Value) == B_OK) 1011 // level = int32Value; 1012 1013 const char *fontFamily; 1014 if (settings.FindString("FontFamily", &fontFamily) == B_OK) { 1015 const char *fontStyle; 1016 if (settings.FindString("FontStyle", &fontStyle) == B_OK) { 1017 float size; 1018 if (settings.FindFloat("FontSize", &size) == B_OK) { 1019 if (size >= 7) 1020 fContentFont.SetSize(size); 1021 1022 if (fontFamily[0] && fontStyle[0]) { 1023 fContentFont.SetFamilyAndStyle(fontFamily[0] ? fontFamily : NULL, 1024 fontStyle[0] ? fontStyle : NULL); 1025 } 1026 } 1027 } 1028 } 1029 1030 if (settings.FindRect("SignatureWindowSize", &rect) == B_OK) 1031 fSignatureWindowFrame = rect; 1032 1033 bool boolValue; 1034 if (settings.FindBool("WordWrapMode", &boolValue) == B_OK) 1035 fWrapMode = boolValue; 1036 1037 BPoint point; 1038 if (settings.FindPoint("PreferencesWindowLocation", &point) == B_OK) 1039 fPrefsWindowPos = point; 1040 1041 if (settings.FindBool("AutoMarkRead", &boolValue) == B_OK) 1042 fAutoMarkRead = boolValue; 1043 1044 const char *string; 1045 if (settings.FindString("SignatureText", &string) == B_OK) { 1046 free(fSignature); 1047 fSignature = strdup(string); 1048 } 1049 1050 if (settings.FindInt32("CharacterSet", &int32Value) == B_OK) 1051 fMailCharacterSet = int32Value; 1052 if (fMailCharacterSet != B_MAIL_UTF8_CONVERSION 1053 && fMailCharacterSet != B_MAIL_US_ASCII_CONVERSION 1054 && BCharacterSetRoster::GetCharacterSetByConversionID(fMailCharacterSet) == NULL) 1055 fMailCharacterSet = B_MS_WINDOWS_CONVERSION; 1056 1057 if (settings.FindString("FindString", &string) == B_OK) 1058 FindWindow::SetFindString(string); 1059 1060 int8 int8Value; 1061 if (settings.FindInt8("ShowButtonBar", &int8Value) == B_OK) 1062 fShowToolBar = int8Value; 1063 1064 if (settings.FindInt32("UseAccountFrom", &int32Value) == B_OK) 1065 fUseAccountFrom = int32Value; 1066 if (fUseAccountFrom < ACCOUNT_USE_DEFAULT 1067 || fUseAccountFrom > ACCOUNT_FROM_MAIL) 1068 fUseAccountFrom = ACCOUNT_USE_DEFAULT; 1069 1070 if (settings.FindBool("ColoredQuotes", &boolValue) == B_OK) 1071 fColoredQuotes = boolValue; 1072 1073 if (settings.FindString("ReplyPreamble", &string) == B_OK) { 1074 free(fReplyPreamble); 1075 fReplyPreamble = strdup(string); 1076 } 1077 1078 if (settings.FindBool("AttachAttributes", &boolValue) == B_OK) 1079 fAttachAttributes = boolValue; 1080 1081 if (settings.FindBool("WarnAboutUnencodableCharacters", &boolValue) == B_OK) 1082 fWarnAboutUnencodableCharacters = boolValue; 1083 1084 if (settings.FindBool("StartWithSpellCheck", &boolValue) == B_OK) 1085 fStartWithSpellCheckOn = boolValue; 1086 1087 return B_OK; 1088 } 1089 1090 1091 void 1092 TMailApp::FontChange() 1093 { 1094 int32 index = 0; 1095 BMessage msg; 1096 BWindow *window; 1097 1098 msg.what = CHANGE_FONT; 1099 msg.AddPointer("font", &fContentFont); 1100 1101 for (;;) { 1102 window = WindowAt(index++); 1103 if (!window) 1104 break; 1105 1106 window->PostMessage(&msg); 1107 } 1108 } 1109 1110 1111 TMailWindow* 1112 TMailApp::NewWindow(const entry_ref* ref, const char* to, bool resend, 1113 BMessenger* trackerMessenger) 1114 { 1115 float fontFactor = be_plain_font->Size() / 12.0f; 1116 BRect r; 1117 if (fMailWindowFrame.Width() < 64 || fMailWindowFrame.Height() < 20) { 1118 // default size 1119 r.Set(40 * fontFactor, 40 * fontFactor, fontFactor * (40 + WIND_WIDTH), 1120 fontFactor * (40 + WIND_HEIGHT)); 1121 } else 1122 r = fMailWindowFrame; 1123 1124 // cascading windows 1125 r.OffsetBy(fontFactor * (((fWindowCount + 5) % 10) * 15 - 75), 1126 fontFactor * (((fWindowCount + 5) % 10) * 15 - 75)); 1127 1128 fWindowCount++; 1129 1130 BString title; 1131 BFile file; 1132 if (!resend && ref && file.SetTo(ref, O_RDONLY) == B_OK) { 1133 BString name; 1134 if (file.ReadAttrString(B_MAIL_ATTR_NAME, &name) == B_OK) { 1135 title << name; 1136 BString subject; 1137 if (file.ReadAttrString(B_MAIL_ATTR_SUBJECT, &subject) == B_OK) 1138 title << " -> " << subject; 1139 } 1140 } 1141 if (title == "") 1142 title = B_TRANSLATE_SYSTEM_NAME("Mail"); 1143 1144 TMailWindow* window = new TMailWindow(r, title.String(), this, ref, to, 1145 &fContentFont, resend, trackerMessenger); 1146 fWindowList.AddItem(window); 1147 1148 window->MoveOnScreen(); 1149 return window; 1150 } 1151 1152 1153 // #pragma mark - settings 1154 1155 1156 bool 1157 TMailApp::AutoMarkRead() 1158 { 1159 BAutolock _(this); 1160 return fAutoMarkRead; 1161 } 1162 1163 1164 BString 1165 TMailApp::Signature() 1166 { 1167 BAutolock _(this); 1168 return BString(fSignature); 1169 } 1170 1171 1172 BString 1173 TMailApp::ReplyPreamble() 1174 { 1175 BAutolock _(this); 1176 return BString(fReplyPreamble); 1177 } 1178 1179 1180 bool 1181 TMailApp::WrapMode() 1182 { 1183 BAutolock _(this); 1184 return fWrapMode; 1185 } 1186 1187 1188 bool 1189 TMailApp::AttachAttributes() 1190 { 1191 BAutolock _(this); 1192 return fAttachAttributes; 1193 } 1194 1195 1196 bool 1197 TMailApp::ColoredQuotes() 1198 { 1199 BAutolock _(this); 1200 return fColoredQuotes; 1201 } 1202 1203 1204 uint8 1205 TMailApp::ShowToolBar() 1206 { 1207 BAutolock _(this); 1208 return fShowToolBar; 1209 } 1210 1211 1212 bool 1213 TMailApp::WarnAboutUnencodableCharacters() 1214 { 1215 BAutolock _(this); 1216 return fWarnAboutUnencodableCharacters; 1217 } 1218 1219 1220 bool 1221 TMailApp::StartWithSpellCheckOn() 1222 { 1223 BAutolock _(this); 1224 return fStartWithSpellCheckOn; 1225 } 1226 1227 1228 void 1229 TMailApp::SetDefaultAccount(int32 account) 1230 { 1231 BAutolock _(this); 1232 fDefaultAccount = account; 1233 } 1234 1235 1236 int32 1237 TMailApp::DefaultAccount() 1238 { 1239 BAutolock _(this); 1240 return fDefaultAccount; 1241 } 1242 1243 1244 int32 1245 TMailApp::UseAccountFrom() 1246 { 1247 BAutolock _(this); 1248 return fUseAccountFrom; 1249 } 1250 1251 1252 uint32 1253 TMailApp::MailCharacterSet() 1254 { 1255 BAutolock _(this); 1256 return fMailCharacterSet; 1257 } 1258 1259 1260 BFont 1261 TMailApp::ContentFont() 1262 { 1263 BAutolock _(this); 1264 return fContentFont; 1265 } 1266 1267 1268 // #pragma mark - 1269 1270 1271 int 1272 main() 1273 { 1274 tzset(); 1275 1276 TMailApp().Run(); 1277 return B_OK; 1278 } 1279 1280