#ifndef __IMAPSession_h__ # define __IMAPSession_h__ # include "MailFolder.h" # include # define CRLF "\r\n" namespace mail { class IMAPSession : public core::TCPSession { public: IMAPSession(core::EPoll &ePoll, core::TCPServer &server) : TCPSession(ePoll, &server, "IMAP Client Session") {} enum State { NOT_AUTHENTICATED, AUTHENTICATED, SELECTED }; std::string clientDomainName; std::string userName; std::string password; std::string tag; State state = NOT_AUTHENTICATED; // Valid only while state == SELECTED - the folder SELECT/EXAMINE // last loaded. readOnly is true for EXAMINE (STORE/EXPUNGE must // be refused), false for SELECT. MailFolder mailbox; std::string selectedFolderName; bool readOnly = false; // APPEND's message-body literal is the one place raw bytes (not // command lines) arrive - see TCPSocket::setBlockSize()/ // onBlockReceived(), unused anywhere else in this codebase before // now. __IMAP_APPEND sets this to a closure that does the actual // mailbox write (kept out of IMAPSession itself so this class // doesn't need to know about MailFileSystem/MailFolder specifics - // it only knows "a block finished, hand it off"), then calls // setBlockSize(n) and sends the "+ OK" continuation. Cleared // before invoking, so a handler that itself needs another literal // (not currently used by anything) can safely set a new one. std::function pendingBlockHandler; void onBlockReceived(coreutils::ZString &block) override { if (pendingBlockHandler) { auto handler = pendingBlockHandler; pendingBlockHandler = nullptr; handler(block); } } // Used to falsely claim to be Dovecot and advertise a capability // list (LITERAL+/SASL-IR/LOGIN-REFERRALS/ID/ENABLE/IDLE/STARTTLS/ // AUTH=PLAIN/AUTH=LOGIN) none of which had a real handler behind // it - see __IMAP_CAPABILITY.cpp's own doc comment for the same // reasoning applied to the CAPABILITY command's response. void onConnected() override { out << "* OK [CAPABILITY IMAP4rev1] BARANTMail ready." << CRLF; } protected: // Every IMAP command line is client-tag-prefixed ("a001 LOGIN ...", // RFC 3501 2.2.1) - the tag is an arbitrary string the client // chooses, not a fixed command name, so it can never appear in the // command table CommandList::processRequest() dispatches on // (ServerCore/CommandList.cpp looks up request[0], and for every // real client request[0] is this tag, not the command word). // Confirmed: with no tag-stripping anywhere, request[0] never // matched any registered command, the lookup threw, and the // catch-all in processRequest() silently swallowed it - no IMAP // command from any spec-compliant client ever reached a handler. // Extracting the tag here, before the generic space-delimited // dispatch runs, leaves the command word where the base // TCPSession::protocol()/CommandList expects to find it. void protocol(coreutils::ZString &data) override { if (data.getLength() == 0) { TCPSession::protocol(data); return; } tag = data.getTokenExclude(" ").str(); data.skipWhitespace(); coreutils::ZString command = data.unparsed(); TCPSession::protocol(command); } }; } #endif