1 /* main - the daemon's inner workings 2 ** 3 ** Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved. 4 */ 5 6 7 #include <Application.h> 8 #include <Message.h> 9 #include <File.h> 10 #include <MessageRunner.h> 11 #include <Deskbar.h> 12 #include <Roster.h> 13 #include <Button.h> 14 #include <StringView.h> 15 #include <Mime.h> 16 #include <Beep.h> 17 #include <fs_index.h> 18 #include <fs_info.h> 19 #include <String.h> 20 #include <VolumeRoster.h> 21 #include <Query.h> 22 #include <ChainRunner.h> 23 #include <NodeMonitor.h> 24 #include <Path.h> 25 26 #include <string.h> 27 #include <stdio.h> 28 #include <errno.h> 29 #include <sys/socket.h> 30 #include <map> 31 32 #include <E-mail.h> 33 #include <MailSettings.h> 34 #include <MailMessage.h> 35 #include <status.h> 36 #include <StringList.h> 37 38 #include "deskbarview.h" 39 #include "LEDAnimation.h" 40 41 #include <MDRLanguage.h> 42 43 #ifdef BONE 44 #define BONE_SERIAL_PPP_GET_STATUS 0xbe230501 45 #define BSPPP_CONNECTED 4 46 typedef struct { 47 char if_name[32]; 48 int connection_status; 49 status_t last_error; 50 int connect_speed; 51 } bsppp_status_t; 52 #include <unistd.h> 53 #endif 54 55 typedef struct glorbal { 56 size_t bytes; 57 BStringList msgs; 58 } snuzzwut; 59 60 BMailStatusWindow *status; 61 62 class MailDaemonApp : public BApplication { 63 public: 64 MailDaemonApp(void); 65 virtual ~MailDaemonApp(); 66 67 virtual void MessageReceived(BMessage *msg); 68 virtual void RefsReceived(BMessage *a_message); 69 70 virtual void Pulse(); 71 virtual bool QuitRequested(); 72 virtual void ReadyToRun(); 73 74 void InstallDeskbarIcon(); 75 void RemoveDeskbarIcon(); 76 77 void RunChains(BList &list,BMessage *msg); 78 void SendPendingMessages(BMessage *msg); 79 void GetNewMessages(BMessage *msg); 80 81 private: 82 void UpdateAutoCheck(bigtime_t interval); 83 84 BMessageRunner *auto_check; 85 BMailSettings settings_file; 86 87 int32 new_messages; 88 bool central_beep; 89 // TRUE to do a beep when the status window closes. This happens 90 // when all mail has been received, so you get one beep for 91 // everything rather than individual beeps for each mail account. 92 // Set to TRUE by the 'mcbp' message that the mail Notification 93 // filter sends us, cleared when the beep is done. 94 BList fetch_done_respondents; 95 96 BList queries; 97 LEDAnimation *led; 98 99 BString alert_string; 100 }; 101 102 103 MailDaemonApp::MailDaemonApp(void) 104 : BApplication("application/x-vnd.Be-POST") 105 { 106 status = new BMailStatusWindow(BRect(40, 400, 360, 400), "Mail Status", 107 settings_file.ShowStatusWindow()); 108 auto_check = NULL; 109 } 110 111 112 MailDaemonApp::~MailDaemonApp() 113 { 114 delete auto_check; 115 116 for (int32 i = 0; i < queries.CountItems(); i++) 117 delete ((BQuery *)(queries.ItemAt(i))); 118 119 delete led; 120 } 121 122 123 void 124 MailDaemonApp::ReadyToRun() 125 { 126 InstallDeskbarIcon(); 127 128 UpdateAutoCheck(settings_file.AutoCheckInterval()); 129 130 BVolume volume; 131 BVolumeRoster roster; 132 133 new_messages = 0; 134 135 while (roster.GetNextVolume(&volume) == B_OK) { 136 //{char name[255];volume.GetName(name);printf("Volume: %s\n",name);} 137 138 BQuery *query = new BQuery; 139 140 query->SetTarget(this); 141 query->SetVolume(&volume); 142 query->PushAttr(B_MAIL_ATTR_STATUS); 143 query->PushString("New"); 144 query->PushOp(B_EQ); 145 query->PushAttr("BEOS:TYPE"); 146 query->PushString("text/x-email"); 147 query->PushOp(B_EQ); 148 query->PushAttr("BEOS:TYPE"); 149 query->PushString("text/x-partial-email"); 150 query->PushOp(B_EQ); 151 query->PushOp(B_OR); 152 query->PushOp(B_AND); 153 query->Fetch(); 154 155 BEntry entry; 156 for (; query->GetNextEntry(&entry) == B_OK; new_messages++); 157 158 queries.AddItem(query); 159 } 160 161 BString string; 162 MDR_DIALECT_CHOICE( 163 if (new_messages > 0) 164 string << new_messages; 165 else 166 string << "No"; 167 if (new_messages != 1) 168 string << " new messages."; 169 else 170 string << " new message.";, 171 if (new_messages > 0) 172 string << new_messages << " 通の未読メッセージがあります "; 173 else 174 string << "未読メッセージはありません"; 175 ); 176 central_beep = false; 177 status->SetDefaultMessage(string); 178 179 led = new LEDAnimation; 180 SetPulseRate(1000000); 181 } 182 183 184 void 185 MailDaemonApp::RefsReceived(BMessage *message) 186 { 187 status->Activate(true); 188 189 entry_ref ref; 190 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 191 BNode node(&ref); 192 if (node.InitCheck() < B_OK) 193 continue; 194 195 BString uid; 196 if (node.ReadAttrString("MAIL:unique_id", &uid) < 0) 197 continue; 198 199 int32 id; 200 if (node.ReadAttr("MAIL:chain", B_INT32_TYPE, 0, &id, sizeof(id)) < 0) 201 continue; 202 203 int32 size; 204 if (node.ReadAttr("MAIL:fullsize", B_SIZE_T_TYPE, 0, &size, sizeof(size)) < 0) 205 size = -1; 206 207 BPath path(&ref); 208 BMailChainRunner *runner = GetMailChainRunner(id, status); 209 runner->GetSingleMessage(uid.String(), size, &path); 210 } 211 } 212 213 214 void 215 MailDaemonApp::UpdateAutoCheck(bigtime_t interval) 216 { 217 if (interval > 0) { 218 if (auto_check != NULL) { 219 auto_check->SetInterval(interval); 220 auto_check->SetCount(-1); 221 } else 222 auto_check = new BMessageRunner(be_app_messenger,new BMessage('moto'),interval); 223 } else { 224 delete auto_check; 225 auto_check = NULL; 226 } 227 } 228 229 230 void 231 MailDaemonApp::MessageReceived(BMessage *msg) 232 { 233 switch (msg->what) { 234 case 'moto': 235 if (settings_file.CheckOnlyIfPPPUp()) { 236 #ifdef BONE 237 int s = socket(AF_INET, SOCK_DGRAM, 0); 238 bsppp_status_t status; 239 240 strcpy(status.if_name, "ppp0"); 241 if (ioctl(s, BONE_SERIAL_PPP_GET_STATUS, &status, sizeof(status)) != 0) { 242 close(s); 243 break; 244 } else { 245 if (status.connection_status != BSPPP_CONNECTED) { 246 close(s); 247 break; 248 } 249 } 250 close (s); 251 #else 252 if (find_thread("tty_thread") <= 0) 253 break; 254 #endif 255 } 256 // supposed to fall through 257 case 'mbth': // check & send messages 258 msg->what = 'msnd'; 259 PostMessage(msg); //'msnd'); 260 // supposed to fall trough 261 case 'mnow': // check messages 262 GetNewMessages(msg); 263 break; 264 case 'msnd': // send messages 265 SendPendingMessages(msg); 266 break; 267 268 case 'mrrs': 269 settings_file.Reload(); 270 UpdateAutoCheck(settings_file.AutoCheckInterval()); 271 status->SetShowCriterion(settings_file.ShowStatusWindow()); 272 break; 273 case 'shst': // when to show the status window 274 { 275 int32 mode; 276 if (msg->FindInt32("ShowStatusWindow",&mode) == B_OK) 277 status->SetShowCriterion(mode); 278 break; 279 } 280 case 'lkch': // status window look changed 281 case 'wsch': // workspace changed 282 status->PostMessage(msg); 283 break; 284 case 'stwg': //----StaT Window Gone 285 { 286 BMessage *msg, reply('mnuc'); 287 reply.AddInt32("num_new_messages",new_messages); 288 289 while((msg = (BMessage *)fetch_done_respondents.RemoveItem(0L))) { 290 msg->SendReply(&reply); 291 delete msg; 292 } 293 } 294 if (alert_string != B_EMPTY_STRING) { 295 alert_string.Truncate(alert_string.Length()-1); 296 BAlert *alert = new BAlert(MDR_DIALECT_CHOICE ("New Messages","新着メッセージ"), alert_string.String(), "OK", NULL, NULL, B_WIDTH_AS_USUAL); 297 alert->SetFeel(B_NORMAL_WINDOW_FEEL); 298 alert->Go(NULL); 299 alert_string = B_EMPTY_STRING; 300 } 301 if (central_beep) { 302 system_beep("New E-mail"); 303 central_beep = false; 304 } 305 break; 306 case 'mcbp': 307 if (new_messages > 0) { 308 central_beep = true; 309 } 310 break; 311 case 'mnum': //----Number of new messages 312 { 313 BMessage reply('mnuc' /* Mail NU message Count */); 314 if (msg->FindBool("wait_for_fetch_done")) { 315 fetch_done_respondents.AddItem(DetachCurrentMessage()); 316 break; 317 } 318 319 reply.AddInt32("num_new_messages",new_messages); 320 msg->SendReply(&reply); 321 } 322 break; 323 case 'mblk': //-----Mail BLinK 324 if (new_messages > 0) 325 led->Start(); 326 break; 327 case 'enda': //-----End Auto Check 328 delete auto_check; 329 auto_check = NULL; 330 break; 331 case 'numg': 332 { 333 int32 num_messages = msg->FindInt32("num_messages"); 334 MDR_DIALECT_CHOICE ( 335 alert_string << num_messages << " new message"; 336 if (num_messages > 1) 337 alert_string << 's'; 338 339 alert_string << " for " << msg->FindString("chain_name") << '\n';, 340 341 alert_string << msg->FindString("chain_name") << "より\n" << num_messages 342 << " 通のメッセージが届きました "; 343 ); 344 345 } 346 break; 347 case B_QUERY_UPDATE: 348 { 349 int32 what; 350 msg->FindInt32("opcode",&what); 351 switch (what) { 352 case B_ENTRY_CREATED: 353 new_messages++; 354 break; 355 case B_ENTRY_REMOVED: 356 new_messages--; 357 break; 358 } 359 360 BString string; 361 362 MDR_DIALECT_CHOICE ( 363 if (new_messages > 0) 364 string << new_messages; 365 else 366 string << "No"; 367 if (new_messages != 1) 368 string << " new messages."; 369 else 370 string << " new message.";, 371 if (new_messages > 0) 372 string << new_messages << " 通の未読メッセージがあります"; 373 else 374 string << "未読メッセージはありません"; 375 ); 376 377 status->SetDefaultMessage(string.String()); 378 379 } 380 break; 381 } 382 BApplication::MessageReceived(msg); 383 } 384 385 386 void 387 MailDaemonApp::InstallDeskbarIcon() 388 { 389 BDeskbar deskbar; 390 391 if (!deskbar.HasItem("mail_daemon")) { 392 BRoster roster; 393 entry_ref ref; 394 395 status_t status = roster.FindApp("application/x-vnd.Be-POST", &ref); 396 if (status < B_OK) { 397 fprintf(stderr, "Can't find application to tell deskbar: %s\n", strerror(status)); 398 return; 399 } 400 401 status = deskbar.AddItem(&ref); 402 if (status < B_OK) { 403 fprintf(stderr, "Can't add deskbar replicant: %s\n", strerror(status)); 404 return; 405 } 406 } 407 } 408 409 410 void 411 MailDaemonApp::RemoveDeskbarIcon() 412 { 413 BDeskbar deskbar; 414 if (deskbar.HasItem("mail_daemon")) 415 deskbar.RemoveItem("mail_daemon"); 416 } 417 418 419 bool 420 MailDaemonApp::QuitRequested() 421 { 422 RemoveDeskbarIcon(); 423 424 return true; 425 } 426 427 428 void 429 MailDaemonApp::RunChains(BList &list, BMessage *msg) 430 { 431 BMailChain *chain; 432 433 int32 index = 0, id; 434 for (; msg->FindInt32("chain", index, &id) == B_OK; index++) { 435 for (int32 i = 0; i < list.CountItems(); i++) { 436 chain = (BMailChain *)list.ItemAt(i); 437 438 if (chain->ID() == (unsigned)id) { 439 chain->RunChain(status, true, false, true); 440 list.RemoveItem(i); // the chain runner deletes the chain 441 break; 442 } 443 } 444 } 445 446 if (index == 0) { 447 // invoke all chains 448 for (int32 i = 0; i < list.CountItems(); i++) { 449 chain = (BMailChain *)list.ItemAt(i); 450 451 chain->RunChain(status, true, false, true); 452 } 453 } else { 454 // delete unused chains 455 for (int32 i = list.CountItems(); i-- > 0;) 456 delete (BMailChain *)list.RemoveItem(i); 457 } 458 } 459 460 461 void 462 MailDaemonApp::GetNewMessages(BMessage *msg) 463 { 464 BList list; 465 GetInboundMailChains(&list); 466 467 RunChains(list,msg); 468 } 469 470 471 void 472 MailDaemonApp::SendPendingMessages(BMessage *msg) 473 { 474 BVolumeRoster roster; 475 BVolume volume; 476 477 while (roster.GetNextVolume(&volume) == B_OK) { 478 BQuery query; 479 query.SetVolume(&volume); 480 query.PushAttr(B_MAIL_ATTR_FLAGS); 481 query.PushInt32(B_MAIL_PENDING); 482 query.PushOp(B_EQ); 483 484 query.PushAttr(B_MAIL_ATTR_FLAGS); 485 query.PushInt32(B_MAIL_PENDING | B_MAIL_SAVE); 486 query.PushOp(B_EQ); 487 488 query.PushOp(B_OR); 489 490 int32 chain_id = -1; 491 492 if (msg->FindInt32("chain",&chain_id) == B_OK) { 493 query.PushAttr("MAIL:chain"); 494 query.PushInt32(chain_id); 495 query.PushOp(B_EQ); 496 query.PushOp(B_AND); 497 } else { 498 chain_id = -1; 499 } 500 501 if (!msg->HasString("message_path")) { 502 if (chain_id == -1) { 503 map <int32, snuzzwut *> messages; 504 505 query.Fetch(); 506 BEntry entry; 507 BPath path; 508 BNode node; 509 int32 chain, default_chain(BMailSettings().DefaultOutboundChainID()); 510 off_t size; 511 512 while (query.GetNextEntry(&entry) == B_OK) { 513 while (node.SetTo(&entry) == B_BUSY) snooze(100); 514 if (node.ReadAttr("MAIL:chain",B_INT32_TYPE,0,&chain,4) < B_OK) 515 chain = default_chain; 516 entry.GetPath(&path); 517 node.GetSize(&size); 518 if (messages[chain] == NULL) { 519 messages[chain] = new snuzzwut; 520 messages[chain]->bytes = 0; 521 } 522 523 messages[chain]->msgs += path.Path(); 524 messages[chain]->bytes += size; 525 } 526 527 map<int32,snuzzwut *>::iterator iter = messages.begin(); 528 map<int32,snuzzwut *>::iterator end = messages.end(); 529 while (iter != end) { 530 if ((iter->first > 0) && (BMailChain(iter->first).ChainDirection() == outbound)) { 531 BMailChainRunner *runner = GetMailChainRunner(iter->first,status); 532 runner->GetMessages(&messages[iter->first]->msgs,messages[iter->first]->bytes); 533 delete messages[iter->first]; 534 runner->Stop(); 535 } 536 537 iter++; 538 } 539 } else { 540 BStringList ids; 541 size_t bytes = 0; 542 543 query.Fetch(); 544 BEntry entry; 545 BPath path; 546 BNode node; 547 off_t size; 548 549 while (query.GetNextEntry(&entry) == B_OK) { 550 node.SetTo(&entry); 551 entry.GetPath(&path); 552 node.GetSize(&size); 553 ids += path.Path(); 554 bytes += size; 555 } 556 557 BMailChainRunner *runner = GetMailChainRunner(chain_id,status); 558 runner->GetMessages(&ids,bytes); 559 runner->Stop(); 560 } 561 } else { 562 const char *path; 563 msg->FindString("message_path",&path); 564 BStringList ids; 565 ids += path; 566 off_t size; 567 BNode(path).GetSize(&size); 568 BMailChainRunner *runner = GetMailChainRunner(chain_id,status); 569 runner->GetMessages(&ids,size); 570 runner->Stop(); 571 } 572 } 573 } 574 575 576 void 577 MailDaemonApp::Pulse() 578 { 579 bigtime_t idle = idle_time(); 580 if (led->IsRunning() && (idle < 100000)) 581 led->Stop(); 582 } 583 584 // #pragma mark - 585 586 587 void 588 makeIndices() 589 { 590 const char *stringIndices[] = { B_MAIL_ATTR_ACCOUNT,B_MAIL_ATTR_CC, 591 B_MAIL_ATTR_FROM,B_MAIL_ATTR_NAME, 592 B_MAIL_ATTR_PRIORITY,B_MAIL_ATTR_REPLY, 593 B_MAIL_ATTR_STATUS,B_MAIL_ATTR_SUBJECT, 594 B_MAIL_ATTR_TO,B_MAIL_ATTR_THREAD, NULL}; 595 596 // add mail indices for all devices capable of querying 597 598 int32 cookie = 0; 599 dev_t device; 600 while ((device = next_dev(&cookie)) >= B_OK) { 601 fs_info info; 602 if (fs_stat_dev(device,&info) < 0 || (info.flags & B_FS_HAS_QUERY) == 0) 603 continue; 604 605 // Work-around for misbehaviour of earlier versions - should be 606 // kept in for some time. 607 // It removes the B_MAIL_ATTR_FLAGS if it is of B_STRING_TYPE, 608 // because that's what the MDR created before... 609 index_info indexInfo; 610 if (fs_stat_index(device, B_MAIL_ATTR_FLAGS, &indexInfo) == 0 611 && indexInfo.type == B_STRING_TYPE) 612 fs_remove_index(device, B_MAIL_ATTR_FLAGS); 613 614 for (int32 i = 0;stringIndices[i];i++) 615 fs_create_index(device,stringIndices[i],B_STRING_TYPE,0); 616 617 fs_create_index(device,"MAIL:draft", B_INT32_TYPE, 0); 618 fs_create_index(device,B_MAIL_ATTR_WHEN,B_INT32_TYPE,0); 619 fs_create_index(device,B_MAIL_ATTR_FLAGS,B_INT32_TYPE,0); 620 fs_create_index(device,"MAIL:chain",B_INT32_TYPE,0); 621 fs_create_index(device,"MAIL:pending_chain",B_INT32_TYPE,0); 622 } 623 } 624 625 626 void 627 addAttribute(BMessage &msg,char *name,char *publicName,int32 type = B_STRING_TYPE,bool viewable = true,bool editable = false,int32 width = 200) 628 { 629 msg.AddString("attr:name",name); 630 msg.AddString("attr:public_name",publicName); 631 msg.AddInt32("attr:type",type); 632 msg.AddBool("attr:viewable",viewable); 633 msg.AddBool("attr:editable",editable); 634 msg.AddInt32("attr:width",width); 635 msg.AddInt32("attr:alignment",B_ALIGN_LEFT); 636 } 637 638 639 void 640 makeMimeType(bool remakeMIMETypes) 641 { 642 // Add MIME database entries for the e-mail file types we handle. Either 643 // do a full rebuild from nothing, or just add on the new attributes that 644 // we support which the regular BeOS mail daemon didn't have. 645 646 const char *types[2] = {"text/x-email","text/x-partial-email"}; 647 BMimeType mime; 648 BMessage info; 649 650 for (int i = 0; i < 2; i++) { 651 info.MakeEmpty(); 652 mime.SetTo(types[i]); 653 if (mime.InitCheck() != B_OK) { 654 fputs("could not init mime type.\n",stderr); 655 return; 656 } 657 658 if (!mime.IsInstalled() || remakeMIMETypes) { 659 // install the full mime type 660 mime.Delete (); 661 mime.Install(); 662 663 // Set up the list of e-mail related attributes that Tracker will 664 // let you display in columns for e-mail messages. 665 addAttribute(info,B_MAIL_ATTR_NAME,"Name"); 666 addAttribute(info,B_MAIL_ATTR_SUBJECT,"Subject"); 667 addAttribute(info,B_MAIL_ATTR_TO,"To"); 668 addAttribute(info,B_MAIL_ATTR_CC,"Cc"); 669 addAttribute(info,B_MAIL_ATTR_FROM,"From"); 670 addAttribute(info,B_MAIL_ATTR_REPLY,"Reply To"); 671 addAttribute(info,B_MAIL_ATTR_STATUS,"Status"); 672 addAttribute(info,B_MAIL_ATTR_PRIORITY,"Priority",B_STRING_TYPE,true,true,40); 673 addAttribute(info,B_MAIL_ATTR_WHEN,"When",B_TIME_TYPE,true,false,150); 674 addAttribute(info,B_MAIL_ATTR_THREAD,"Thread"); 675 addAttribute(info,B_MAIL_ATTR_ACCOUNT,"Account",B_STRING_TYPE,true,false,100); 676 mime.SetAttrInfo(&info); 677 678 if (i == 0) { 679 mime.SetShortDescription("E-mail"); 680 mime.SetLongDescription("Electronic Mail Message"); 681 mime.SetPreferredApp("application/x-vnd.Be-MAIL"); 682 } else { 683 mime.SetShortDescription("Partial E-mail"); 684 mime.SetLongDescription("A Partially Downloaded E-mail"); 685 mime.SetPreferredApp("application/x-vnd.Be-POST"); 686 } 687 } else { 688 // Just add the e-mail related attribute types we use to the MIME system. 689 mime.GetAttrInfo(&info); 690 bool hasAccount = false, hasThread = false, hasSize = false; 691 const char *result; 692 for (int32 index = 0;info.FindString("attr:name",index,&result) == B_OK;index++) { 693 if (!strcmp(result,B_MAIL_ATTR_ACCOUNT)) 694 hasAccount = true; 695 if (!strcmp(result,B_MAIL_ATTR_THREAD)) 696 hasThread = true; 697 if (!strcmp(result,"MAIL:fullsize")) 698 hasSize = true; 699 } 700 701 if (!hasAccount) 702 addAttribute(info,B_MAIL_ATTR_ACCOUNT,"Account",B_STRING_TYPE,true,false,100); 703 if (!hasThread) 704 addAttribute(info,B_MAIL_ATTR_THREAD,"Thread"); 705 /*if (!hasSize) 706 addAttribute(info,"MAIL:fullsize","Message Size",B_SIZE_T_TYPE,true,false,100);*/ 707 //--- Tracker can't display SIZT attributes. What a pain. 708 if (!hasAccount || !hasThread/* || !hasSize*/) 709 mime.SetAttrInfo(&info); 710 } 711 mime.Unset(); 712 } 713 } 714 715 716 int 717 main(int argc, const char **argv) 718 { 719 bool remakeMIMETypes = false; 720 721 for (int i = 1; i < argc; i++) { 722 if (strcmp(argv[i], "-E") == 0) { 723 if (!BMailSettings().DaemonAutoStarts()) 724 return 0; 725 } 726 if (strcmp(argv[i], "-M") == 0) { 727 remakeMIMETypes = true; 728 } 729 } 730 731 MailDaemonApp app; 732 733 // install MimeTypes, attributes, indices, and the 734 // system beep add startup 735 736 makeMimeType(remakeMIMETypes); 737 makeIndices(); 738 add_system_beep_event("New E-mail"); 739 740 be_app->Run(); 741 return 0; 742 } 743 744