#ifndef __IMAPArgs_h__ #define __IMAPArgs_h__ #include "ZString.h" #include #include #include #include namespace mail { class MailFolder; /// /// Shared argument parsing for every IMAP command handler. /// /// CommandList::processRequest() (ServerCore) pre-splits a command /// line on literal spaces before any handler sees it - fine for SMTP, /// wrong for IMAP, whose arguments routinely contain quoted strings /// and parenthesized lists with embedded spaces /// (LOGIN "user name" pw, STORE 1 +FLAGS (\Seen \Flagged)). Handlers /// must never read request[N]; instead they call nextArgument() /// repeatedly against the same ZString&, which parses off the RAW, /// unsplit cursor (request.reset(), already done by /// CommandList::processRequest() before dispatch, put the cursor back /// at the real start). /// namespace IMAPArgs { /// /// Skips leading whitespace, then returns the next IMAP argument: /// an atom (run up to the next space), a quoted "..." string, or a /// (...)/[...]/{...} container - all via ZString::getContainer(), /// which already understands these four delimiter shapes, /// INCLUDING their delimiters. Callers that want a quoted string's /// interior, or a container's interior, strip the surrounding /// characters themselves (different callers want different /// things: FETCH wants to know it got a "[...]" section, STORE just /// wants a plain flag list). Returns an empty ZString at end of /// input. /// coreutils::ZString nextArgument(coreutils::ZString &request); /// /// Case-insensitive compare of an already-extracted argument /// against a bare keyword (FLAGS, SEEN, BEFORE, ...). /// bool isKeyword(coreutils::ZString &argument, const char *keyword); /// /// Strips a single layer of "..." or (...) or [...] delimiters /// from an argument previously returned by nextArgument(), if /// present. Leaves an atom (no delimiters) unchanged. /// std::string unwrap(coreutils::ZString &argument); /// /// Parses an IMAP sequence-set ("1:5,7,9:*") against folder's /// CURRENT message ordering. uidMode=true: the numbers in the set /// are UIDs and "*" is the highest UID present; uidMode=false: /// numbers are 1-based sequence numbers and "*" is the last /// message. Numbers outside the folder's current bounds are /// silently skipped (matches real server behavior - a client can /// race a concurrent EXPUNGE). Returns the resolved UIDs, ascending /// and de-duplicated. /// std::vector parseSequenceSet(coreutils::ZString &request, MailFolder &folder, bool uidMode); /// /// "(\Seen \Flagged)" -> {"\Seen", "\Flagged"}. Also accepts a /// bare unparenthesized single flag, for STORE clients that omit /// the parens around a single-flag list. /// std::vector parseFlagList(coreutils::ZString &request); /// /// RFC 3501 date-time ("DD-Mon-YYYY HH:MM:SS +ZZZZ", always /// quoted - APPEND's optional date-time argument) or bare date /// ("DD-Mon-YYYY", optionally quoted - SEARCH's BEFORE/ON/SINCE) -> /// Unix timestamp. Returns -1 on unparseable input. /// time_t parseDate(coreutils::ZString &argument); /// /// RFC 3501 5.1: the mailbox name INBOX is reserved and MUST be /// interpreted case-insensitively, referring to the user's /// primary mailbox - every other mailbox name is server-defined /// and compared literally. On-disk folders are named "Inbox", /// "Spam", "Trash" (capitalized) - this maps any-case "inbox" to /// that literal directory name and passes every other name /// through unchanged. /// std::string normalizeFolderName(const std::string &name); /// /// Every folder-taking command turns a client-supplied name /// straight into "/" (MailFileSystem:: /// getFolderPath()). A name containing '/' escapes into a /// subdirectory; a bare ".." - no slash, so a check for '/' alone /// does NOT catch it - resolves to the mailbox's own PARENT /// directory, which really exists and really is a directory, so /// ifFolderExists() would return true for it: SELECT/DELETE/ /// APPEND/RENAME would then operate on the directory holding /// every OTHER user's mailbox on the same domain. Rejects empty, /// anything containing '/', and anything starting with '.' /// (which also reserves the leading-dot namespace for this /// server's own index files - .password, .uidvalidity, .uidnext, /// .imapindex, .subscriptions). /// bool isValidFolderName(const std::string &name); } } #endif