diff --git a/src/kits/mail/MailProtocol.cpp b/src/kits/mail/MailProtocol.cpp index f392f2b373..ff20de187f 100644 --- a/src/kits/mail/MailProtocol.cpp +++ b/src/kits/mail/MailProtocol.cpp @@ -4,22 +4,24 @@ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include class BMailProtocol; @@ -40,7 +42,7 @@ class ManifestAdder : public BMailChainCallback { (**uids_on_disk) += uid; } } - + private: BStringList *manifest,**uids_on_disk; const char *uid; @@ -50,7 +52,7 @@ class MessageDeletion : public BMailChainCallback { public: MessageDeletion(BMailProtocol *home, const char *uid, BEntry *io_entry, bool delete_anyway); virtual void Callback(status_t result); - + private: BMailProtocol *us; bool always; @@ -76,6 +78,7 @@ BMailProtocol::error_alert(const char *process, status_t error) namespace { + class DeleteHandler : public BHandler { public: DeleteHandler(BMailProtocol *a) @@ -96,64 +99,152 @@ class DeleteHandler : public BHandler { BMailProtocol *us; }; + class TrashMonitor : public BHandler { public: TrashMonitor(BMailProtocol *a, int32 chain_id) - : us(a), trash("/boot/home/Desktop/Trash"), messages_for_us(0), id(chain_id) + : us(a), messages_for_us(0), id(chain_id) { } + ~TrashMonitor() + { + stop_watching(this); + } + void MessageReceived(BMessage *msg) { if (msg->what == 'INIT') { - node_ref to_watch; - trash.GetNodeRef(&to_watch); - watch_node(&to_watch,B_WATCH_DIRECTORY,this); + BVolumeRoster volumes; + BVolume volume; + while (volumes.GetNextVolume(&volume) == B_OK) { + BPath trashPath; + if (find_directory(B_TRASH_DIRECTORY, &trashPath, + false, &volume) == B_OK) { + // watch this trash directory + BNode node(trashPath.Path()); + node_ref to_watch; + node.GetNodeRef(&to_watch); + watch_node(&to_watch, B_WATCH_DIRECTORY, this); + } + } + // Also watch for new volume + watch_node(NULL, B_WATCH_MOUNT, this); return; } - if ((msg->what == B_NODE_MONITOR) && (us->InitCheck() == B_OK)) { - int32 opcode; - if (msg->FindInt32("opcode",&opcode) < B_OK) + + if (msg->what != B_NODE_MONITOR || (us->InitCheck() != B_OK)) + return; + + int32 opcode; + if (msg->FindInt32("opcode",&opcode) < B_OK) + return; + + if (opcode == B_DEVICE_MOUNTED) { + dev_t device; + if (msg->FindInt32("new device", &device) != B_OK) return; - - if (opcode == B_ENTRY_MOVED) { - int64 node(msg->FindInt64("to directory")); - dev_t device(msg->FindInt32("device")); - node_ref item_ref; - item_ref.node = node; - item_ref.device = device; - - BDirectory moved_to(&item_ref); - - BNode trash_item(&moved_to,msg->FindString("name")); - int32 chain; - if (trash_item.ReadAttr("MAIL:chain",B_INT32_TYPE,0,&chain,sizeof(chain)) < B_OK) - return; - - if (chain == id) - messages_for_us += (moved_to == trash) ? 1 : -1; + BVolume volume(device); + BPath trashPath; + if (find_directory(B_TRASH_DIRECTORY, &trashPath, + false, &volume) == B_OK) { + // watch also this new volume's trash directory + BNode node(trashPath.Path()); + node_ref to_watch; + node.GetNodeRef(&to_watch); + watch_node(&to_watch, B_WATCH_DIRECTORY, this); } - - if (messages_for_us < 0) - messages_for_us = 0; // Guard against weirdness - - if (trash.CountEntries() == 0) { - if (messages_for_us > 0) - us->CheckForDeletedMessages(); - + return; + } + + if (opcode == B_ENTRY_MOVED) { + entry_ref entry; + const char* name; + msg->FindInt64("to directory", &entry.directory); + msg->FindInt32("device", &entry.device); + msg->FindString("name", &name); + entry.set_name(name); + + BNode node(&entry); + int32 chain; + + // check it's a mail + if (node.ReadAttr("MAIL:chain",B_INT32_TYPE,0,&chain,sizeof(chain)) < B_OK) + return; + + // check it's a mail for us + if (chain != id) + return; + + // check if it was moved to trash + bool moved_to_trash = false; + BPath trashPath; + BPath entryPath(&entry); + BVolumeRoster volumes; + BVolume volume; + while (volumes.GetNextVolume(&volume) == B_OK) { + BPath trashPath; + if (find_directory(B_TRASH_DIRECTORY, &trashPath, + false, &volume) != B_OK) { + continue; + } + + if (strncmp(entryPath.Path(), trashPath.Path(), + strlen(trashPath.Path())) == 0) { + moved_to_trash = true; + break; + } + } + + messages_for_us += (moved_to_trash) ? 1 : -1; + if (messages_for_us < 0) { + // Guard against weirdness messages_for_us = 0; + return; } } + + if (opcode != B_ENTRY_REMOVED) + return; + + // Check if this (trash) entry removal made one trash now empty + + bool someTrashIsEmpty = false; + BVolumeRoster volumes; + BVolume volume; + while (volumes.GetNextVolume(&volume) == B_OK) { + BPath trashPath; + if (find_directory(B_TRASH_DIRECTORY, &trashPath, + false, &volume) != B_OK) { + continue; + } + BDirectory trash(trashPath.Path()); + if (trash.CountEntries() == 0) { + someTrashIsEmpty = true; + break; + } + } + + if (someTrashIsEmpty) { + // One trash is empty, check for deleted messages + if (messages_for_us > 0) + us->CheckForDeletedMessages(); + + messages_for_us = 0; + } } private: BMailProtocol *us; - BDirectory trash; int32 messages_for_us; int32 id; }; -} // unnamed namspace +} // unnamed namespace + + +// #pragma mark BMailProtocol + BMailProtocol::BMailProtocol(BMessage *settings, BMailChainRunner *run) : BMailFilter(settings), @@ -163,17 +254,17 @@ BMailProtocol::BMailProtocol(BMessage *settings, BMailChainRunner *run) BMailProtocol::settings = settings; manifest = new BStringList; - + { BString attr_name = "MAIL:"; attr_name << runner->Chain()->ID() << ":manifest"; //--- In case someone puts multiple accounts in the same directory - + if (runner->Chain()->MetaData()->HasString("path")) { BNode node(runner->Chain()->MetaData()->FindString("path")); if (node.InitCheck() >= B_OK) { // We already have a directory so we can try to read metadata // from it. Note that it is normal for this directory not to - // be founf on the first run as it will be later created by + // be found on the first run as it will be later created by // the INBOX system filter. attr_info info; if (node.GetAttrInfo(attr_name.String(),&info) < B_OK) { @@ -190,14 +281,14 @@ BMailProtocol::BMailProtocol(BMessage *settings, BMailChainRunner *run) } } else runner->ShowError("Error while reading account manifest: no destination directory exists."); } - + uids_on_disk = new BStringList; BVolumeRoster volumes; BVolume volume; while (volumes.GetNextVolume(&volume) == B_OK) { BQuery fido; entry_ref entry; - + fido.SetVolume(&volume); fido.PushAttr("MAIL:chain"); fido.PushInt32(settings->FindInt32("chain")); @@ -213,21 +304,21 @@ BMailProtocol::BMailProtocol(BMessage *settings, BMailChainRunner *run) fido.PushOp(B_AND); } fido.Fetch(); - + BString uid; while (fido.GetNextRef(&entry) == B_OK) { BNode(&entry).ReadAttrString("MAIL:unique_id",&uid); uids_on_disk->AddItem(uid.String()); } } - + (*manifest) |= (*uids_on_disk); - + if (!settings->FindBool("login_and_do_nothing_else_of_any_importance")) { DeleteHandler *h = new DeleteHandler(this); runner->AddHandler(h); runner->PostMessage('DELE',h); - + trash_monitor = new TrashMonitor(this,runner->Chain()->ID()); runner->AddHandler(trash_monitor); runner->PostMessage('INIT',trash_monitor); @@ -269,7 +360,7 @@ BMailProtocol::~BMailProtocol() for (int32 i = 0; i < (a)->CountItems(); i++)\ puts((a)->ItemAt(i)); \ puts("Done\n"); - + status_t BMailProtocol::ProcessMailMessage(BPositionIO **io_message, BEntry *io_entry, BMessage *io_headers, BPath *io_folder, const char *io_uid) @@ -278,7 +369,7 @@ BMailProtocol::ProcessMailMessage(BPositionIO **io_message, BEntry *io_entry, if (io_uid == NULL) return B_ERROR; - + error = GetMessage(io_uid, io_message, io_headers, io_folder); if (error < B_OK) { if (error != B_MAIL_END_FETCH) { @@ -306,16 +397,16 @@ void BMailProtocol::CheckForDeletedMessages() { if (((settings->FindBool("delete_remote_when_local")) || !(settings->FindBool("leave_mail_on_server"))) && (manifest->CountItems() > 0)) { BStringList to_delete; - + if (uids_on_disk == NULL) { BStringList query_contents; BVolumeRoster volumes; BVolume volume; - + while (volumes.GetNextVolume(&volume) == B_OK) { BQuery fido; entry_ref entry; - + fido.SetVolume(&volume); fido.PushAttr("MAIL:chain"); fido.PushInt32(settings->FindInt32("chain")); @@ -325,24 +416,24 @@ void BMailProtocol::CheckForDeletedMessages() { fido.PushOp(B_EQ); fido.PushOp(B_OR); fido.Fetch(); - + BString uid; while (fido.GetNextRef(&entry) == B_OK) { BNode(&entry).ReadAttrString("MAIL:unique_id",&uid); query_contents.AddItem(uid.String()); } } - + query_contents.NotHere(*manifest,&to_delete); } else { uids_on_disk->NotHere(*manifest,&to_delete); delete uids_on_disk; uids_on_disk = NULL; - } + } for (int32 i = 0; i < to_delete.CountItems(); i++) DeleteMessage(to_delete[i]); - + //*(unique_ids) -= to_delete; --- This line causes bad things to // happen (POP3 client uses the wrong indices to retrieve // messages). Without it, bad things don't happen.