/** * @file operator-lsub.cc * @brief Implementation of the LSUB command. * @author Andreas Aardal Hanssen * @date 2002-2005 */ #include "convert.h" #include "depot.h" #include "iodevice.h" #include "iofactory.h" #include "mailbox.h" #include "operators.h" #include "recursivedescent.h" #include "regmatch.h" #include "session.h" #include #include #include #include namespace { const int DIR_SELECT = 0x01; const int DIR_MARKED = 0x02; const int DIR_NOINFERIORS = 0x04; const int DIR_LEAF = 0x08; } using namespace Binc; using std::multimap; using std::string; LsubOperator::LsubOperator() {} LsubOperator::~LsubOperator() {} const string LsubOperator::getName() const { return "LSUB"; } Session::State LsubOperator::getState() const { return Session::State(Session::AUTHENTICATED | Session::SELECTED); } Operator::ProcessResult LsubOperator::process(Depot &depot, Request &command) { const char delim = depot.getDelimiter(); // remove leading or trailing delimiter in wildcard string wildcard = command.getListMailbox(); trim(wildcard, string(&delim, 1)); // convert wildcard to regular expression string regex = toRegex(wildcard, depot.getDelimiter()); string wildcardLower = regex; lowercase(wildcardLower); if (wildcardLower.substr(0, 6) == "^inbox") regex = "^[iI][nN][bB][oO][xX]" + regex.substr(6); // remove leading or trailing delimiter in reference string ref = command.getMailbox(); trim(ref, string(&delim, 1)); wildcardLower = ref; lowercase(wildcardLower); if (wildcardLower.substr(0, 5) == "inbox" && (wildcardLower.length() == 5 || wildcardLower[5] == delim)) { ref = "INBOX" + ref.substr(5); } // a multimap from mailbox name to flags multimap mailboxes; // read through all entries in depository. for (auto i = depot.begin("."); i != depot.end(); ++i) { const string path = *i; const string mpath = depot.filenameToMailbox(path); Mailbox *m = nullptr; // skip entries that are not identified as mailboxes if ((m = depot.get(mpath)) == nullptr) continue; // convert file name to mailbox name. skip it if there is no // corresponding mailbox name. string tmp = toCanonMailbox(depot.filenameToMailbox(path)); trim(tmp, string(&delim, 1)); if (tmp == "") { continue; } else { int flags = DIR_SELECT; multimap::iterator mi = mailboxes.find(tmp); if (mi != mailboxes.end()) { flags |= mi->second; mailboxes.erase(mi); } mailboxes.insert(make_pair(tmp, flags)); } // now add all superior mailboxes with no flags set if not // added already. string::size_type pos = tmp.rfind(delim); while (pos != string::npos) { tmp = tmp.substr(0, pos); trim(tmp, string(&delim, 1)); multimap::iterator mi = mailboxes.find(tmp); if (mi == mailboxes.end()) mailboxes.insert(make_pair(tmp, 0)); pos = tmp.rfind(delim); } } // find leaf nodes O(N^2) multimap::iterator i; for (i = mailboxes.begin(); i != mailboxes.end(); ++i) { string mailbox = i->first; mailbox += delim; bool leaf = true; multimap::const_iterator j; for (j = mailboxes.begin(); j != mailboxes.end(); ++j) { string::size_type pos = j->first.rfind(delim); if (pos == string::npos) continue; string base = j->first.substr(0, pos + 1); if (mailbox == base) { leaf = false; break; } } if (leaf) { unsigned int flags = i->second; flags |= DIR_LEAF; i->second = flags; } } depot.loadSubscribes(); std::vector subscribed = depot.getSubscriptions(); sort(subscribed.begin(), subscribed.end()); // finally, print all mailbox entries with flags. for (const auto &j : subscribed) { if (ref == "" || (ref.length() <= j.length() && ref == j.substr(0, ref.length()))) { if (regexMatch(j.substr(ref.length()), regex) == 0) { int flags = 0; for (i = mailboxes.begin(); i != mailboxes.end(); ++i) { if (i->first == j) { flags = i->second; break; } } bincClient << "* LSUB ("; string sep = ""; bool noselect = false; if (!(flags & DIR_SELECT)) { bincClient << sep << "\\Noselect"; sep = " "; noselect = true; } if (!noselect) { if (flags & DIR_MARKED) bincClient << sep << "\\Marked"; else bincClient << sep << "\\Unmarked"; sep = " "; } if (flags & DIR_LEAF) bincClient << sep << "\\HasNoChildren"; else bincClient << sep << "\\HasChildren"; sep = " "; if (flags & DIR_NOINFERIORS) bincClient << sep << "\\Noinferiors"; bincClient << ") \"" << depot.getDelimiter() << "\" " << toImapString(j) << std::endl; } } } return Operator::ProcessResult::OK; } Parser::ParseResult LsubOperator::parse(Request &c_in, Parser &p) { constexpr auto ACCEPT = Parser::ParseResult::ACCEPT; constexpr auto ERROR = Parser::ParseResult::ERROR; constexpr auto REJECT = Parser::ParseResult::REJECT; Session &session = Session::getInstance(); if (c_in.getUidMode()) return REJECT; Parser::ParseResult res; if ((res = p.expectSPACE()) != ACCEPT) { session.setLastError("Expected SPACE after LSUB"); return ERROR; } string mailbox; if ((res = p.expectMailbox(mailbox)) != ACCEPT) { session.setLastError("Expected mailbox after LSUB SPACE"); return ERROR; } c_in.setMailbox(mailbox); if ((res = p.expectSPACE()) != ACCEPT) { session.setLastError("Expected SPACE after LSUB SPACE mailbox"); return ERROR; } string listmailbox; if ((res = p.expectListMailbox(listmailbox)) != ACCEPT) { session.setLastError("Expected list_mailbox after LSUB SPACE" " mailbox SPACE"); return ERROR; } if ((res = p.expectCRLF()) != ACCEPT) { session.setLastError("Expected CRLF after LSUB SPACE" " mailbox SPACE list_mailbox"); return ERROR; } c_in.setListMailbox(listmailbox); c_in.setName("LSUB"); return ACCEPT; }