From 9d73fc5382c3e7f31abb2b4b22f3ae2be8720feb Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sat, 15 Mar 2014 23:33:20 +0100 Subject: [PATCH] IMAP commands. --- src/vmime/net/imap/IMAPCommand.cpp | 410 ++++++++++++++++++++++ src/vmime/net/imap/IMAPCommand.hpp | 124 +++++++ src/vmime/net/imap/IMAPConnection.cpp | 85 ++--- src/vmime/net/imap/IMAPConnection.hpp | 6 +- src/vmime/net/imap/IMAPFolder.cpp | 230 ++++-------- src/vmime/net/imap/IMAPMessage.cpp | 40 ++- src/vmime/net/imap/IMAPStore.cpp | 3 +- src/vmime/net/imap/IMAPUtils.cpp | 48 +-- src/vmime/net/imap/IMAPUtils.hpp | 5 +- src/vmime/utility/stringUtils.hpp | 20 ++ tests/net/imap/IMAPCommandTest.cpp | 484 ++++++++++++++++++++++++++ 11 files changed, 1189 insertions(+), 266 deletions(-) create mode 100644 src/vmime/net/imap/IMAPCommand.cpp create mode 100644 src/vmime/net/imap/IMAPCommand.hpp create mode 100644 tests/net/imap/IMAPCommandTest.cpp diff --git a/src/vmime/net/imap/IMAPCommand.cpp b/src/vmime/net/imap/IMAPCommand.cpp new file mode 100644 index 00000000..19c3b723 --- /dev/null +++ b/src/vmime/net/imap/IMAPCommand.cpp @@ -0,0 +1,410 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2014 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/net/imap/IMAPCommand.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" +#include "vmime/net/imap/IMAPUtils.hpp" + +#include + + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPCommand::IMAPCommand(const string& text, const string& traceText) + : m_text(text), m_traceText(traceText) +{ +} + + +// static +shared_ptr IMAPCommand::LOGIN(const string& username, const string& password) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "LOGIN " << IMAPUtils::quoteString(username) + << " " << IMAPUtils::quoteString(password); + + std::ostringstream trace; + trace.imbue(std::locale::classic()); + trace << "LOGIN "; + + return createCommand(cmd.str(), trace.str()); +} + + +// static +shared_ptr IMAPCommand::AUTHENTICATE(const string& mechName) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTHENTICATE " << mechName; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::AUTHENTICATE(const string& mechName, const string& initialResponse) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTHENTICATE " << mechName << " " << initialResponse; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::LIST(const string& refName, const string& mailboxName) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "LIST " << IMAPUtils::quoteString(refName) + << " " << IMAPUtils::quoteString(mailboxName); + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::SELECT + (const bool readOnly, const string& mailboxName, const std::vector & params) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + if (readOnly) + cmd << "EXAMINE "; + else + cmd << "SELECT "; + + cmd << IMAPUtils::quoteString(mailboxName); + + if (!params.empty()) + { + cmd << " ("; + + for (size_t i = 0, n = params.size() ; i < n ; ++i) + { + if (i != 0) cmd << " "; + cmd << params[i]; + } + + cmd << ")"; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::STATUS + (const string& mailboxName, const std::vector & attribs) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "STATUS " << IMAPUtils::quoteString(mailboxName); + + cmd << " ("; + + for (size_t i = 0, n = attribs.size() ; i < n ; ++i) + { + if (i != 0) cmd << " "; + cmd << attribs[i]; + } + + cmd << ")"; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::CREATE + (const string& mailboxName, const std::vector & params) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "CREATE " << IMAPUtils::quoteString(mailboxName); + + if (!params.empty()) + { + cmd << " ("; + + for (size_t i = 0, n = params.size() ; i < n ; ++i) + { + if (i != 0) cmd << " "; + cmd << params[i]; + } + + cmd << ")"; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::DELETE(const string& mailboxName) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "DELETE " << IMAPUtils::quoteString(mailboxName); + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::RENAME + (const string& mailboxName, const string& newMailboxName) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "RENAME " << IMAPUtils::quoteString(mailboxName) + << " " << IMAPUtils::quoteString(newMailboxName); + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::FETCH + (const messageSet& msgs, const std::vector & params) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + if (msgs.isUIDSet()) + cmd << "UID FETCH " << IMAPUtils::messageSetToSequenceSet(msgs); + else + cmd << "FETCH " << IMAPUtils::messageSetToSequenceSet(msgs); + + if (params.size() == 1) + { + cmd << " " << params[0]; + } + else + { + cmd << " ("; + + for (size_t i = 0, n = params.size() ; i < n ; ++i) + { + if (i != 0) cmd << " "; + cmd << params[i]; + } + + cmd << ")"; + } + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::STORE + (const messageSet& msgs, const int mode, const std::vector & flags) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + if (msgs.isUIDSet()) + cmd << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs); + else + cmd << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs); + + if (mode == message::FLAG_MODE_ADD) + cmd << " +FLAGS "; + else if (mode == message::FLAG_MODE_REMOVE) + cmd << " -FLAGS "; + else // if (mode == message::FLAG_MODE_SET) + cmd << " FLAGS "; + + cmd << "("; + + for (size_t i = 0, n = flags.size() ; i < n ; ++i) + { + if (i != 0) cmd << " "; + cmd << flags[i]; + } + + cmd << ")"; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::APPEND + (const string& mailboxName, const std::vector & flags, + vmime::datetime* date, const size_t size) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "APPEND " << IMAPUtils::quoteString(mailboxName); + + if (!flags.empty()) + { + cmd << " ("; + + for (size_t i = 0, n = flags.size() ; i < n ; ++i) + { + if (i != 0) cmd << " "; + cmd << flags[i]; + } + + cmd << ")"; + } + + if (date != NULL) + cmd << " " << IMAPUtils::dateTime(*date); + + cmd << " {" << size << "}"; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::COPY + (const messageSet& msgs, const string& mailboxName) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + if (msgs.isUIDSet()) + cmd << "UID COPY " << IMAPUtils::messageSetToSequenceSet(msgs); + else + cmd << "COPY " << IMAPUtils::messageSetToSequenceSet(msgs); + + cmd << " " << IMAPUtils::quoteString(mailboxName); + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::SEARCH + (const std::vector & keys, const vmime::charset* charset) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "SEARCH"; + + if (charset) + cmd << " CHARSET " << charset->getName(); + + for (size_t i = 0, n = keys.size() ; i < n ; ++i) + cmd << " " << keys[i]; + + return createCommand(cmd.str()); +} + + +// static +shared_ptr IMAPCommand::STARTTLS() +{ + return createCommand("STARTTLS"); +} + + +// static +shared_ptr IMAPCommand::CAPABILITY() +{ + return createCommand("CAPABILITY"); +} + + +// static +shared_ptr IMAPCommand::NOOP() +{ + return createCommand("NOOP"); +} + + +// static +shared_ptr IMAPCommand::EXPUNGE() +{ + return createCommand("EXPUNGE"); +} + + +// static +shared_ptr IMAPCommand::CLOSE() +{ + return createCommand("CLOSE"); +} + + +// static +shared_ptr IMAPCommand::LOGOUT() +{ + return createCommand("LOGOUT"); +} + + +// static +shared_ptr IMAPCommand::createCommand + (const string& text, const string& traceText) +{ + if (traceText.empty()) + return shared_ptr (new IMAPCommand(text, text)); + else + return shared_ptr (new IMAPCommand(text, traceText)); +} + + +const string IMAPCommand::getText() const +{ + return m_text; +} + + +const string IMAPCommand::getTraceText() const +{ + return m_traceText; +} + + +void IMAPCommand::send(shared_ptr conn) +{ + conn->sendCommand(dynamicCast (shared_from_this())); +} + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP diff --git a/src/vmime/net/imap/IMAPCommand.hpp b/src/vmime/net/imap/IMAPCommand.hpp new file mode 100644 index 00000000..c0a0d9b1 --- /dev/null +++ b/src/vmime/net/imap/IMAPCommand.hpp @@ -0,0 +1,124 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2014 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_IMAP_IMAPCOMMAND_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPCOMMAND_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + + +#include "vmime/object.hpp" +#include "vmime/base.hpp" +#include "vmime/datetime.hpp" + +#include "vmime/net/messageSet.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +class IMAPConnection; + + +/** An IMAP command that will be sent to the server. + */ +class VMIME_EXPORT IMAPCommand : public object +{ +public: + + static shared_ptr LOGIN(const string& username, const string& password); + static shared_ptr AUTHENTICATE(const string& mechName); + static shared_ptr AUTHENTICATE(const string& mechName, const string& initialResponse); + static shared_ptr LIST(const string& refName, const string& mailboxName); + static shared_ptr SELECT(const bool readOnly, const string& mailboxName, const std::vector & params); + static shared_ptr STATUS(const string& mailboxName, const std::vector & attribs); + static shared_ptr CREATE(const string& mailboxName, const std::vector & params); + static shared_ptr DELETE(const string& mailboxName); + static shared_ptr RENAME(const string& mailboxName, const string& newMailboxName); + static shared_ptr FETCH(const messageSet& msgs, const std::vector & params); + static shared_ptr STORE(const messageSet& msgs, const int mode, const std::vector & flags); + static shared_ptr APPEND(const string& mailboxName, const std::vector & flags, vmime::datetime* date, const size_t size); + static shared_ptr COPY(const messageSet& msgs, const string& mailboxName); + static shared_ptr SEARCH(const std::vector & keys, const vmime::charset* charset); + static shared_ptr STARTTLS(); + static shared_ptr CAPABILITY(); + static shared_ptr NOOP(); + static shared_ptr EXPUNGE(); + static shared_ptr CLOSE(); + static shared_ptr LOGOUT(); + + /** Creates a new IMAP command with the specified text. + * + * @param text command text + * @param traceText trace text (if empty, command text is used) + * @return a new IMAPCommand object + */ + static shared_ptr createCommand(const string& text, const string& traceText = ""); + + /** Sends this command over the specified connection. + * + * @param conn connection onto which the command will be sent + */ + virtual void send(shared_ptr conn); + + /** Returns the full text of the command, including command name + * and parameters (if any). This is the text that will be sent + * to the server. + * + * @return command text (eg. "LOGIN myusername mypassword") + */ + virtual const string getText() const; + + /** Returns the full text of the command, suitable for outputing + * to the tracer. + * + * @return trace text (eg. "LOGIN myusername ***") + */ + virtual const string getTraceText() const; + +protected: + + IMAPCommand(const string& text, const string& traceText); + IMAPCommand(const IMAPCommand&); + +private: + + string m_text; + string m_traceText; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_IMAP + +#endif // VMIME_NET_IMAP_IMAPCOMMAND_HPP_INCLUDED diff --git a/src/vmime/net/imap/IMAPConnection.cpp b/src/vmime/net/imap/IMAPConnection.cpp index 04705f6c..0bde327b 100644 --- a/src/vmime/net/imap/IMAPConnection.cpp +++ b/src/vmime/net/imap/IMAPConnection.cpp @@ -31,6 +31,7 @@ #include "vmime/net/imap/IMAPConnection.hpp" #include "vmime/net/imap/IMAPUtils.hpp" #include "vmime/net/imap/IMAPStore.hpp" +#include "vmime/net/imap/IMAPCommand.hpp" #include "vmime/exception.hpp" #include "vmime/platform.hpp" @@ -70,6 +71,7 @@ IMAPConnection::IMAPConnection(shared_ptr store, shared_ptr (); } @@ -133,7 +135,6 @@ void IMAPConnection::connect() m_socket->connect(address, port); - m_tag = make_shared (); m_parser = make_shared (m_tag, m_socket, m_timeoutHandler); @@ -259,8 +260,8 @@ void IMAPConnection::authenticate() const string username = getAuthenticator()->getUsername(); const string password = getAuthenticator()->getPassword(); - send(true, "LOGIN " + IMAPUtils::quoteString(username) - + " " + IMAPUtils::quoteString(password), true); + shared_ptr conn = dynamicCast (shared_from_this()); + IMAPCommand::LOGIN(username, password)->send(conn); std::auto_ptr resp(m_parser->readResponse()); @@ -355,8 +356,7 @@ void IMAPConnection::authenticateSASL() saslSession->init(); - std::ostringstream cmd; - cmd << "AUTHENTICATE " << mech->getName(); + shared_ptr authCmd; if (saslSession->getMechanism()->hasInitialResponse()) { @@ -369,12 +369,16 @@ void IMAPConnection::authenticateSASL() delete [] initialResp; if (encodedInitialResp.empty()) - cmd << " ="; + authCmd = IMAPCommand::AUTHENTICATE(mech->getName(), "="); else - cmd << " " << encodedInitialResp; + authCmd = IMAPCommand::AUTHENTICATE(mech->getName(), encodedInitialResp); + } + else + { + authCmd = IMAPCommand::AUTHENTICATE(mech->getName()); } - send(true, cmd.str(), true); + authCmd->send(dynamicCast (shared_from_this())); for (bool cont = true ; cont ; ) { @@ -428,7 +432,8 @@ void IMAPConnection::authenticateSASL() (challenge, challengeLen, &resp, &respLen); // Send response - send(false, saslContext->encodeB64(resp, respLen), true); + const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n"; + sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length()); // Server capabilities may change when logged in invalidateCapabilities(); @@ -448,7 +453,7 @@ void IMAPConnection::authenticateSASL() } // Cancel SASL exchange - send(false, "*", true); + sendRaw(utility::stringUtils::bytesFromString("*\r\n"), 3); } catch (...) { @@ -483,7 +488,7 @@ void IMAPConnection::startTLS() { try { - send(true, "STARTTLS", true); + IMAPCommand::STARTTLS()->send(dynamicCast (shared_from_this())); std::auto_ptr resp(m_parser->readResponse()); @@ -582,7 +587,7 @@ void IMAPConnection::invalidateCapabilities() void IMAPConnection::fetchCapabilities() { - send(true, "CAPABILITY", true); + IMAPCommand::CAPABILITY()->send(dynamicCast (shared_from_this())); std::auto_ptr resp(m_parser->readResponse()); @@ -675,7 +680,7 @@ void IMAPConnection::internalDisconnect() { if (isConnected()) { - send(true, "LOGOUT", true); + IMAPCommand::LOGOUT()->send(dynamicCast (shared_from_this())); m_socket->disconnect(); m_socket = null; @@ -692,7 +697,7 @@ void IMAPConnection::internalDisconnect() void IMAPConnection::initHierarchySeparator() { - send(true, "LIST \"\" \"\"", true); + IMAPCommand::LIST("", "")->send(dynamicCast (shared_from_this())); std::auto_ptr resp(m_parser->readResponse()); @@ -732,43 +737,17 @@ void IMAPConnection::initHierarchySeparator() } -void IMAPConnection::send(bool tag, const string& what, bool end) +void IMAPConnection::sendCommand(shared_ptr cmd) { - if (tag && !m_firstTag) + if (!m_firstTag) ++(*m_tag); -#if VMIME_DEBUG - std::ostringstream oss; + m_socket->send(*m_tag); + m_socket->send(" "); + m_socket->send(cmd->getText()); + m_socket->send("\r\n"); - if (tag) - { - oss << string(*m_tag); - oss << " "; - } - - oss << what; - - if (end) - oss << "\r\n"; - - m_socket->send(oss.str()); -#else - if (tag) - { - m_socket->send(*m_tag); - m_socket->send(" "); - } - - m_socket->send(what); - - if (end) - { - m_socket->send("\r\n"); - } -#endif - - if (tag) - m_firstTag = false; + m_firstTag = false; } @@ -826,6 +805,18 @@ shared_ptr IMAPConnection::getSocket() const } +void IMAPConnection::setSocket(shared_ptr sok) +{ + m_socket = sok; +} + + +shared_ptr IMAPConnection::getTag() +{ + return m_tag; +} + + bool IMAPConnection::isMODSEQDisabled() const { return m_noModSeq; diff --git a/src/vmime/net/imap/IMAPConnection.hpp b/src/vmime/net/imap/IMAPConnection.hpp index a3156f25..da1b9ba4 100644 --- a/src/vmime/net/imap/IMAPConnection.hpp +++ b/src/vmime/net/imap/IMAPConnection.hpp @@ -48,6 +48,7 @@ namespace imap { class IMAPTag; class IMAPStore; +class IMAPCommand; class VMIME_EXPORT IMAPConnection : public object @@ -79,7 +80,7 @@ public: char hierarchySeparator() const; - void send(bool tag, const string& what, bool end); + void sendCommand(shared_ptr cmd); void sendRaw(const byte_t* buffer, const size_t count); IMAPParser::response* readResponse(IMAPParser::literalHandler* lh = NULL); @@ -102,6 +103,9 @@ public: shared_ptr getConnectionInfos() const; shared_ptr getSocket() const; + void setSocket(shared_ptr sok); + + shared_ptr getTag(); bool isMODSEQDisabled() const; void disableMODSEQ(); diff --git a/src/vmime/net/imap/IMAPFolder.cpp b/src/vmime/net/imap/IMAPFolder.cpp index d02e5683..e602ea6e 100644 --- a/src/vmime/net/imap/IMAPFolder.cpp +++ b/src/vmime/net/imap/IMAPFolder.cpp @@ -35,6 +35,7 @@ #include "vmime/net/imap/IMAPUtils.hpp" #include "vmime/net/imap/IMAPConnection.hpp" #include "vmime/net/imap/IMAPFolderStatus.hpp" +#include "vmime/net/imap/IMAPCommand.hpp" #include "vmime/message.hpp" @@ -157,20 +158,16 @@ void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) // S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited // S: A142 OK [READ-WRITE] SELECT completed - std::ostringstream oss; - - if (mode == MODE_READ_ONLY) - oss << "EXAMINE "; - else - oss << "SELECT "; - - oss << IMAPUtils::quoteString(IMAPUtils::pathToString - (connection->hierarchySeparator(), getFullPath())); + std::vector selectParams; if (m_connection->hasCapability("CONDSTORE")) - oss << " (CONDSTORE)"; + selectParams.push_back("CONDSTORE"); - connection->send(true, oss.str(), true); + IMAPCommand::SELECT( + mode == MODE_READ_ONLY, + IMAPUtils::pathToString(connection->hierarchySeparator(), getFullPath()), + selectParams + )->send(connection); // Read the response std::auto_ptr resp(connection->readResponse()); @@ -289,7 +286,7 @@ void IMAPFolder::close(const bool expunge) if (m_mode == MODE_READ_ONLY) throw exceptions::operation_not_supported(); - oldConnection->send(true, "CLOSE", true); + IMAPCommand::CLOSE()->send(oldConnection); } // Close this folder connection @@ -345,8 +342,7 @@ void IMAPFolder::create(const folderAttributes& attribs) if (attribs.getType() & folderAttributes::TYPE_CONTAINS_FOLDERS) mailbox += m_connection->hierarchySeparator(); - std::ostringstream oss; - oss << "CREATE " << IMAPUtils::quoteString(mailbox); + std::vector createParams; if (attribs.getSpecialUse() != folderAttributes::SPECIALUSE_NONE) { @@ -354,7 +350,8 @@ void IMAPFolder::create(const folderAttributes& attribs) throw exceptions::operation_not_supported(); // C: t2 CREATE MySpecial (USE (\Drafts \Sent)) - oss << "(USE ("; + std::ostringstream oss; + oss << "USE ("; switch (attribs.getSpecialUse()) { @@ -369,10 +366,12 @@ void IMAPFolder::create(const folderAttributes& attribs) case folderAttributes::SPECIALUSE_IMPORTANT: oss << "\\Important"; break; } - oss << "))"; + oss << ")"; + + createParams.push_back(oss.str()); } - m_connection->send(true, oss.str(), true); + IMAPCommand::CREATE(mailbox, createParams)->send(m_connection); std::auto_ptr resp(m_connection->readResponse()); @@ -407,10 +406,7 @@ void IMAPFolder::destroy() const string mailbox = IMAPUtils::pathToString (m_connection->hierarchySeparator(), getFullPath()); - std::ostringstream oss; - oss << "DELETE " << IMAPUtils::quoteString(mailbox); - - m_connection->send(true, oss.str(), true); + IMAPCommand::DELETE(mailbox)->send(m_connection); std::auto_ptr resp(m_connection->readResponse()); @@ -464,12 +460,8 @@ int IMAPFolder::testExistAndGetType() // // ==> NO, does not exist - std::ostringstream oss; - oss << "LIST \"\" "; - oss << IMAPUtils::quoteString(IMAPUtils::pathToString - (m_connection->hierarchySeparator(), getFullPath())); - - m_connection->send(true, oss.str(), true); + IMAPCommand::LIST("", IMAPUtils::pathToString + (m_connection->hierarchySeparator(), getFullPath()))->send(m_connection); std::auto_ptr resp(m_connection->readResponse()); @@ -560,14 +552,10 @@ std::vector > IMAPFolder::getMessages(const messageSet& ms // S: * nnnn3 FETCH (UID uuuu3) // S: . OK UID FETCH completed - // Prepare command and arguments - std::ostringstream cmd; - cmd.imbue(std::locale::classic()); + std::vector params; + params.push_back("UID"); - cmd << "UID FETCH " << IMAPUtils::messageSetToSequenceSet(msgs) << " UID"; - - // Send the request - m_connection->send(true, cmd.str(), true); + IMAPCommand::FETCH(msgs, params)->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -679,28 +667,22 @@ std::vector > IMAPFolder::getFolders(const bool recursive) // S: * LIST (\NoInferiors) "/" foo/bar/zap // S: a005 OK LIST completed - std::ostringstream oss; - oss << "LIST "; + shared_ptr cmd; const string pathString = IMAPUtils::pathToString (m_connection->hierarchySeparator(), getFullPath()); if (recursive) { - oss << IMAPUtils::quoteString(pathString); - oss << " *"; + cmd = IMAPCommand::LIST(pathString, "*"); } else { - if (pathString.empty()) // don't add sep for root folder - oss << "\"\""; - else - oss << IMAPUtils::quoteString(pathString + m_connection->hierarchySeparator()); - - oss << " %"; + cmd = IMAPCommand::LIST + (pathString.empty() ? "" : (pathString + m_connection->hierarchySeparator()), "%"); } - m_connection->send(true, oss.str(), true); + cmd->send(m_connection); std::auto_ptr resp(m_connection->readResponse()); @@ -783,10 +765,8 @@ void IMAPFolder::fetchMessages(std::vector >& msg, const f } // Send the request - const string command = IMAPUtils::buildFetchRequest - (m_connection, messageSet::byNumber(list), options); - - m_connection->send(true, command, true); + IMAPUtils::buildFetchCommand + (m_connection, messageSet::byNumber(list), options)->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -881,10 +861,8 @@ std::vector > IMAPFolder::getAndFetchMessages attribsWithUID.add(fetchAttributes::UID); // Send the request - const string command = IMAPUtils::buildFetchRequest - (m_connection, msgs, attribsWithUID); - - m_connection->send(true, command, true); + IMAPUtils::buildFetchCommand + (m_connection, msgs, attribsWithUID)->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -1016,19 +994,11 @@ void IMAPFolder::deleteMessages(const messageSet& msgs) else if (m_mode == MODE_READ_ONLY) throw exceptions::illegal_state("Folder is read-only"); - // Build the request text - std::ostringstream command; - command.imbue(std::locale::classic()); - - if (msgs.isUIDSet()) - command << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs); - else - command << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs); - - command << " +FLAGS (\\Deleted)"; - // Send the request - m_connection->send(true, command.str(), true); + IMAPCommand::STORE( + msgs, message::FLAG_MODE_ADD, + IMAPUtils::messageFlagList(message::FLAG_DELETED) + )->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -1046,31 +1016,12 @@ void IMAPFolder::deleteMessages(const messageSet& msgs) void IMAPFolder::setMessageFlags(const messageSet& msgs, const int flags, const int mode) { - // Build the request text - std::ostringstream command; - command.imbue(std::locale::classic()); - - if (msgs.isUIDSet()) - command << "UID STORE " << IMAPUtils::messageSetToSequenceSet(msgs); - else - command << "STORE " << IMAPUtils::messageSetToSequenceSet(msgs); - - switch (mode) - { - case message::FLAG_MODE_ADD: command << " +FLAGS "; break; - case message::FLAG_MODE_REMOVE: command << " -FLAGS "; break; - default: - case message::FLAG_MODE_SET: command << " FLAGS "; break; - } - - const string flagList = IMAPUtils::messageFlagList(flags); + const std::vector flagList = IMAPUtils::messageFlagList(flags); if (!flagList.empty()) { - command << flagList; - // Send the request - m_connection->send(true, command.str(), true); + IMAPCommand::STORE(msgs, mode, flagList)->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -1116,31 +1067,11 @@ messageSet IMAPFolder::addMessage else if (m_mode == MODE_READ_ONLY) throw exceptions::illegal_state("Folder is read-only"); - // Build the request text - std::ostringstream command; - command.imbue(std::locale::classic()); - - command << "APPEND " << IMAPUtils::quoteString(IMAPUtils::pathToString - (m_connection->hierarchySeparator(), getFullPath())) << ' '; - - const string flagList = IMAPUtils::messageFlagList(flags); - - if (flags != -1 && !flagList.empty()) - { - command << flagList; - command << ' '; - } - - if (date != NULL) - { - command << IMAPUtils::dateTime(*date); - command << ' '; - } - - command << '{' << size << '}'; - // Send the request - m_connection->send(true, command.str(), true); + IMAPCommand::APPEND( + IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()), + IMAPUtils::messageFlagList(flags), date, size + )->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -1191,7 +1122,7 @@ messageSet IMAPFolder::addMessage progress->progress(current, total); } - m_connection->send(false, "", true); + m_connection->sendRaw(utility::stringUtils::bytesFromString("\r\n"), 2); if (progress) progress->stop(total); @@ -1230,7 +1161,7 @@ void IMAPFolder::expunge() throw exceptions::illegal_state("Folder is read-only"); // Send the request - m_connection->send(true, "EXPUNGE", true); + IMAPCommand::EXPUNGE()->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -1259,18 +1190,11 @@ void IMAPFolder::rename(const folder::path& newPath) else if (!store->isValidFolderName(newPath.getLastComponent())) throw exceptions::invalid_folder_name(); - // Build the request text - std::ostringstream command; - command.imbue(std::locale::classic()); - - command << "RENAME "; - command << IMAPUtils::quoteString(IMAPUtils::pathToString - (m_connection->hierarchySeparator(), getFullPath())) << " "; - command << IMAPUtils::quoteString(IMAPUtils::pathToString - (m_connection->hierarchySeparator(), newPath)); - // Send the request - m_connection->send(true, command.str(), true); + IMAPCommand::RENAME( + IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()), + IMAPUtils::pathToString(m_connection->hierarchySeparator(), newPath) + )->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -1327,16 +1251,11 @@ messageSet IMAPFolder::copyMessages(const folder::path& dest, const messageSet& else if (!isOpen()) throw exceptions::illegal_state("Folder not open"); - // Build the request text - std::ostringstream command; - command.imbue(std::locale::classic()); - - command << "COPY " << IMAPUtils::messageSetToSequenceSet(set) << " "; - command << IMAPUtils::quoteString(IMAPUtils::pathToString - (m_connection->hierarchySeparator(), dest)); - // Send the request - m_connection->send(true, command.str(), true); + IMAPCommand::COPY( + set, + IMAPUtils::pathToString(m_connection->hierarchySeparator(), dest) + )->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -1379,24 +1298,22 @@ shared_ptr IMAPFolder::getStatus() if (!store) throw exceptions::illegal_state("Store disconnected"); - // Build the request text - std::ostringstream command; - command.imbue(std::locale::classic()); + // Build the attributes list + std::vector attribs; - command << "STATUS "; - command << IMAPUtils::quoteString(IMAPUtils::pathToString - (m_connection->hierarchySeparator(), getFullPath())); - command << " ("; - - command << "MESSAGES" << ' ' << "UNSEEN" << ' ' << "UIDNEXT" << ' ' << "UIDVALIDITY"; + attribs.push_back("MESSAGES"); + attribs.push_back("UNSEEN"); + attribs.push_back("UIDNEXT"); + attribs.push_back("UIDVALIDITY"); if (m_connection->hasCapability("CONDSTORE")) - command << ' ' << "HIGHESTMODSEQ"; - - command << ")"; + attribs.push_back("HIGHESTMODSEQ"); // Send the request - m_connection->send(true, command.str(), true); + IMAPCommand::STATUS( + IMAPUtils::pathToString(m_connection->hierarchySeparator(), getFullPath()), + attribs + )->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -1443,7 +1360,7 @@ void IMAPFolder::noop() if (!store) throw exceptions::illegal_state("Store disconnected"); - m_connection->send(true, "NOOP", true); + IMAPCommand::NOOP()->send(m_connection); std::auto_ptr resp(m_connection->readResponse()); @@ -1459,15 +1376,15 @@ void IMAPFolder::noop() std::vector IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& uid) { - std::vector v; - - std::ostringstream command; - command.imbue(std::locale::classic()); - - command << "SEARCH UID " << uid << ":*"; - // Send the request - m_connection->send(true, command.str(), true); + std::ostringstream uidSearchKey; + uidSearchKey.imbue(std::locale::classic()); + uidSearchKey << "UID " << uid << ":*"; + + std::vector searchKeys; + searchKeys.push_back(uidSearchKey.str()); + + IMAPCommand::SEARCH(searchKeys, /* charset */ NULL)->send(m_connection); // Get the response std::auto_ptr resp(m_connection->readResponse()); @@ -1480,6 +1397,7 @@ std::vector IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& } const std::vector & respDataList = resp->continue_req_or_response_data(); + std::vector seqNumbers; for (std::vector ::const_iterator it = respDataList.begin() ; it != respDataList.end() ; ++it) @@ -1505,13 +1423,13 @@ std::vector IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& it != mailboxData->search_nz_number_list().end(); ++it) { - v.push_back((*it)->value()); + seqNumbers.push_back((*it)->value()); } } processStatusUpdate(resp.get()); - return v; + return seqNumbers; } diff --git a/src/vmime/net/imap/IMAPMessage.cpp b/src/vmime/net/imap/IMAPMessage.cpp index 1934d717..aa6b45ec 100644 --- a/src/vmime/net/imap/IMAPMessage.cpp +++ b/src/vmime/net/imap/IMAPMessage.cpp @@ -299,15 +299,7 @@ void IMAPMessage::extractImpl } } - // Build the request text - std::ostringstream command; - command.imbue(std::locale::classic()); - - if (m_uid.empty()) - command << "FETCH " << m_num << " BODY"; - else - command << "UID FETCH " << m_uid << " BODY"; - + // Build the body descriptor for FETCH /* BODY[] header + body BODY.PEEK[] header + body (peek) @@ -316,43 +308,53 @@ void IMAPMessage::extractImpl BODY[TEXT] body BODY.PEEK[TEXT] body (peek) */ + std::ostringstream bodyDesc; + bodyDesc.imbue(std::locale::classic()); + + bodyDesc << "BODY"; if (extractFlags & EXTRACT_PEEK) - command << ".PEEK"; + bodyDesc << ".PEEK"; - command << "["; + bodyDesc << "["; if (section.str().empty()) { // header + body if ((extractFlags & EXTRACT_HEADER) && (extractFlags & EXTRACT_BODY)) - command << ""; + bodyDesc << ""; // body only else if (extractFlags & EXTRACT_BODY) - command << "TEXT"; + bodyDesc << "TEXT"; // header only else if (extractFlags & EXTRACT_HEADER) - command << "HEADER"; + bodyDesc << "HEADER"; } else { - command << section.str(); + bodyDesc << section.str(); // header + body if ((extractFlags & EXTRACT_HEADER) && (extractFlags & EXTRACT_BODY)) throw exceptions::operation_not_supported(); // header only else if (extractFlags & EXTRACT_HEADER) - command << ".MIME"; // "MIME" not "HEADER" for parts + bodyDesc << ".MIME"; // "MIME" not "HEADER" for parts } - command << "]"; + bodyDesc << "]"; if (start != 0 || length != static_cast (-1)) - command << "<" << start << "." << length << ">"; + bodyDesc << "<" << start << "." << length << ">"; + + std::vector fetchParams; + fetchParams.push_back(bodyDesc.str()); // Send the request - constCast (folder)->m_connection->send(true, command.str(), true); + IMAPCommand::FETCH( + m_uid.empty() ? messageSet::byNumber(m_num) : messageSet::byUID(m_uid), + fetchParams + )->send(constCast (folder)->m_connection); // Get the response std::auto_ptr resp diff --git a/src/vmime/net/imap/IMAPStore.cpp b/src/vmime/net/imap/IMAPStore.cpp index 031ebf24..a2930c66 100644 --- a/src/vmime/net/imap/IMAPStore.cpp +++ b/src/vmime/net/imap/IMAPStore.cpp @@ -31,6 +31,7 @@ #include "vmime/net/imap/IMAPFolder.hpp" #include "vmime/net/imap/IMAPConnection.hpp" #include "vmime/net/imap/IMAPFolderStatus.hpp" +#include "vmime/net/imap/IMAPCommand.hpp" #include "vmime/exception.hpp" #include "vmime/platform.hpp" @@ -192,7 +193,7 @@ void IMAPStore::noop() if (!isConnected()) throw exceptions::not_connected(); - m_connection->send(true, "NOOP", true); + IMAPCommand::NOOP()->send(m_connection); std::auto_ptr resp(m_connection->readResponse()); diff --git a/src/vmime/net/imap/IMAPUtils.cpp b/src/vmime/net/imap/IMAPUtils.cpp index 519e413b..0bb086ff 100644 --- a/src/vmime/net/imap/IMAPUtils.cpp +++ b/src/vmime/net/imap/IMAPUtils.cpp @@ -488,35 +488,21 @@ int IMAPUtils::messageFlagsFromFlags(const IMAPParser::flag_list* list) } -const string IMAPUtils::messageFlagList(const int flags) +// static +const std::vector IMAPUtils::messageFlagList(const int flags) { std::vector flagList; + if (flags == -1) + return flagList; // default flags + if (flags & message::FLAG_REPLIED) flagList.push_back("\\Answered"); if (flags & message::FLAG_MARKED) flagList.push_back("\\Flagged"); if (flags & message::FLAG_DELETED) flagList.push_back("\\Deleted"); if (flags & message::FLAG_SEEN) flagList.push_back("\\Seen"); if (flags & message::FLAG_DRAFT) flagList.push_back("\\Draft"); - if (!flagList.empty()) - { - std::ostringstream res; - res.imbue(std::locale::classic()); - - res << "("; - - if (flagList.size() >= 2) - { - std::copy(flagList.begin(), flagList.end() - 1, - std::ostream_iterator (res, " ")); - } - - res << *(flagList.end() - 1) << ")"; - - return (res.str()); - } - - return ""; + return flagList; } @@ -590,7 +576,7 @@ const string IMAPUtils::dateTime(const vmime::datetime& date) // static -const string IMAPUtils::buildFetchRequest +shared_ptr IMAPUtils::buildFetchCommand (shared_ptr cnt, const messageSet& msgs, const fetchAttributes& options) { // Example: @@ -659,25 +645,7 @@ const string IMAPUtils::buildFetchRequest } } - // Build the request text - std::ostringstream command; - command.imbue(std::locale::classic()); - - if (msgs.isUIDSet()) - command << "UID FETCH " << messageSetToSequenceSet(msgs) << " ("; - else - command << "FETCH " << messageSetToSequenceSet(msgs) << " ("; - - for (std::vector ::const_iterator it = items.begin() ; - it != items.end() ; ++it) - { - if (it != items.begin()) command << " "; - command << *it; - } - - command << ")"; - - return command.str(); + return IMAPCommand::FETCH(msgs, items); } diff --git a/src/vmime/net/imap/IMAPUtils.hpp b/src/vmime/net/imap/IMAPUtils.hpp index 2a7efbb0..61b45e48 100644 --- a/src/vmime/net/imap/IMAPUtils.hpp +++ b/src/vmime/net/imap/IMAPUtils.hpp @@ -38,6 +38,7 @@ #include "vmime/net/message.hpp" #include "vmime/net/imap/IMAPParser.hpp" #include "vmime/net/imap/IMAPConnection.hpp" +#include "vmime/net/imap/IMAPCommand.hpp" #include "vmime/mailboxList.hpp" @@ -79,7 +80,7 @@ public: static int messageFlagsFromFlags(const IMAPParser::flag_list* list); - static const string messageFlagList(const int flags); + static const std::vector messageFlagList(const int flags); /** Format a date/time to IMAP date/time format. * @@ -96,7 +97,7 @@ public: * @param options fetch options * @return fetch request */ - static const string buildFetchRequest + static shared_ptr buildFetchCommand (shared_ptr cnt, const messageSet& msgs, const fetchAttributes& options); /** Convert a parser-style address list to a mailbox list. diff --git a/src/vmime/utility/stringUtils.hpp b/src/vmime/utility/stringUtils.hpp index 7d9925e2..a3062af8 100644 --- a/src/vmime/utility/stringUtils.hpp +++ b/src/vmime/utility/stringUtils.hpp @@ -53,6 +53,26 @@ public: return string(reinterpret_cast (data), count); } + /** Casts a string to bytes. + * + * @param str string + * @return pointer to the first byte of the string + */ + static const byte_t* bytesFromString(const string& str) + { + return reinterpret_cast (str.data()); + } + + /** Casts a NULL-terminated string to bytes. + * + * @param str string + * @return pointer to the first byte of the string + */ + static const byte_t* bytesFromString(const char* str) + { + return reinterpret_cast (str); + } + /** Appends bytes to a string. * * @param str string to which append data diff --git a/tests/net/imap/IMAPCommandTest.cpp b/tests/net/imap/IMAPCommandTest.cpp new file mode 100644 index 00000000..8183dfa7 --- /dev/null +++ b/tests/net/imap/IMAPCommandTest.cpp @@ -0,0 +1,484 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2014 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include "vmime/net/imap/IMAPCommand.hpp" +#include "vmime/net/imap/IMAPStore.hpp" +#include "vmime/net/imap/IMAPConnection.hpp" + + +using namespace vmime::net::imap; + + +VMIME_TEST_SUITE_BEGIN(IMAPCommandTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testCreateCommand) + VMIME_TEST(testCreateCommandParams) + VMIME_TEST(testLOGIN) + VMIME_TEST(testAUTHENTICATE) + VMIME_TEST(testAUTHENTICATE_InitialResponse) + VMIME_TEST(testLIST) + VMIME_TEST(testSELECT) + VMIME_TEST(testSTATUS) + VMIME_TEST(testCREATE) + VMIME_TEST(testDELETE) + VMIME_TEST(testRENAME) + VMIME_TEST(testFETCH) + VMIME_TEST(testSTORE) + VMIME_TEST(testAPPEND) + VMIME_TEST(testCOPY) + VMIME_TEST(testSEARCH) + VMIME_TEST(testSTARTTLS) + VMIME_TEST(testCAPABILITY) + VMIME_TEST(testNOOP) + VMIME_TEST(testEXPUNGE) + VMIME_TEST(testCLOSE) + VMIME_TEST(testLOGOUT) + VMIME_TEST(testSend) + VMIME_TEST_LIST_END + + + void testCreateCommand() + { + vmime::shared_ptr cmd = IMAPCommand::createCommand("MY_COMMAND"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "MY_COMMAND", cmd->getText()); + } + + void testCreateCommandParams() + { + vmime::shared_ptr cmd = IMAPCommand::createCommand("MY_COMMAND param1 param2"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "MY_COMMAND param1 param2", cmd->getText()); + } + + void testLOGIN() + { + vmime::shared_ptr cmd = IMAPCommand::LOGIN("username", "password"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "LOGIN username password", cmd->getText()); + VASSERT_EQ("Trace Text", "LOGIN ", cmd->getTraceText()); + } + + void testAUTHENTICATE() + { + vmime::shared_ptr cmd = IMAPCommand::AUTHENTICATE("saslmechanism"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "AUTHENTICATE saslmechanism", cmd->getText()); + } + + void testAUTHENTICATE_InitialResponse() + { + vmime::shared_ptr cmd = IMAPCommand::AUTHENTICATE("saslmechanism", "initial-response"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "AUTHENTICATE saslmechanism initial-response", cmd->getText()); + } + + void testLIST() + { + vmime::shared_ptr cmd = IMAPCommand::LIST("ref-name", "mailbox-name"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "LIST ref-name mailbox-name", cmd->getText()); + + vmime::shared_ptr cmdQuote = IMAPCommand::LIST("ref name", "mailbox-name"); + + VASSERT_NOT_NULL("Not null", cmdQuote); + VASSERT_EQ("Text", "LIST \"ref name\" mailbox-name", cmdQuote->getText()); + } + + void testSELECT() + { + std::vector params; + params.push_back("param-1"); + params.push_back("param-2"); + + + vmime::shared_ptr cmdRO = IMAPCommand::SELECT + (/* readOnly */ true, "mailbox-name", std::vector ()); + + VASSERT_NOT_NULL("Not null", cmdRO); + VASSERT_EQ("Text", "EXAMINE mailbox-name", cmdRO->getText()); + + vmime::shared_ptr cmdROQuote = IMAPCommand::SELECT + (/* readOnly */ true, "mailbox name", std::vector ()); + + VASSERT_NOT_NULL("Not null", cmdROQuote); + VASSERT_EQ("Text", "EXAMINE \"mailbox name\"", cmdROQuote->getText()); + + + vmime::shared_ptr cmdRW = IMAPCommand::SELECT + (/* readOnly */ false, "mailbox-name", std::vector ()); + + VASSERT_NOT_NULL("Not null", cmdRW); + VASSERT_EQ("Text", "SELECT mailbox-name", cmdRW->getText()); + + vmime::shared_ptr cmdRWParams = IMAPCommand::SELECT + (/* readOnly */ false, "mailbox-name", params); + + VASSERT_NOT_NULL("Not null", cmdRWParams); + VASSERT_EQ("Text", "SELECT mailbox-name (param-1 param-2)", cmdRWParams->getText()); + + vmime::shared_ptr cmdRWQuote = IMAPCommand::SELECT + (/* readOnly */ false, "mailbox name", std::vector ()); + + VASSERT_NOT_NULL("Not null", cmdRWQuote); + VASSERT_EQ("Text", "SELECT \"mailbox name\"", cmdRWQuote->getText()); + } + + void testSTATUS() + { + std::vector attribs; + attribs.push_back("attrib-1"); + attribs.push_back("attrib-2"); + + + vmime::shared_ptr cmd = + IMAPCommand::STATUS("mailbox-name", attribs); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "STATUS mailbox-name (attrib-1 attrib-2)", cmd->getText()); + + + vmime::shared_ptr cmdQuote = + IMAPCommand::STATUS("mailbox name", attribs); + + VASSERT_NOT_NULL("Not null", cmdQuote); + VASSERT_EQ("Text", "STATUS \"mailbox name\" (attrib-1 attrib-2)", cmdQuote->getText()); + } + + void testCREATE() + { + std::vector params; + params.push_back("param-1"); + params.push_back("param-2"); + + + vmime::shared_ptr cmd = + IMAPCommand::CREATE("mailbox-name", params); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "CREATE mailbox-name (param-1 param-2)", cmd->getText()); + + + vmime::shared_ptr cmdQuote = + IMAPCommand::CREATE("mailbox name", params); + + VASSERT_NOT_NULL("Not null", cmdQuote); + VASSERT_EQ("Text", "CREATE \"mailbox name\" (param-1 param-2)", cmdQuote->getText()); + + + vmime::shared_ptr cmdNoParam = + IMAPCommand::CREATE("mailbox-name", std::vector ()); + + VASSERT_NOT_NULL("Not null", cmdNoParam); + VASSERT_EQ("Text", "CREATE mailbox-name", cmdNoParam->getText()); + } + + void testDELETE() + { + vmime::shared_ptr cmd = + IMAPCommand::DELETE("mailbox-name"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "DELETE mailbox-name", cmd->getText()); + + + vmime::shared_ptr cmdQuote = + IMAPCommand::DELETE("mailbox name"); + + VASSERT_NOT_NULL("Not null", cmdQuote); + VASSERT_EQ("Text", "DELETE \"mailbox name\"", cmdQuote->getText()); + } + + void testRENAME() + { + vmime::shared_ptr cmd = + IMAPCommand::RENAME("mailbox-name", "new-mailbox-name"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "RENAME mailbox-name new-mailbox-name", cmd->getText()); + + + vmime::shared_ptr cmdQuote = + IMAPCommand::RENAME("mailbox name", "new mailbox name"); + + VASSERT_NOT_NULL("Not null", cmdQuote); + VASSERT_EQ("Text", "RENAME \"mailbox name\" \"new mailbox name\"", cmdQuote->getText()); + } + + void testFETCH() + { + std::vector params; + params.push_back("param-1"); + params.push_back("param-2"); + + + vmime::shared_ptr cmdNum = + IMAPCommand::FETCH(vmime::net::messageSet::byNumber(42), params); + + VASSERT_NOT_NULL("Not null", cmdNum); + VASSERT_EQ("Text", "FETCH 42 (param-1 param-2)", cmdNum->getText()); + + + vmime::shared_ptr cmdNums = + IMAPCommand::FETCH(vmime::net::messageSet::byNumber(42, 47), params); + + VASSERT_NOT_NULL("Not null", cmdNums); + VASSERT_EQ("Text", "FETCH 42:47 (param-1 param-2)", cmdNums->getText()); + + + vmime::shared_ptr cmdUID = + IMAPCommand::FETCH(vmime::net::messageSet::byUID(42), params); + + VASSERT_NOT_NULL("Not null", cmdUID); + VASSERT_EQ("Text", "UID FETCH 42 (param-1 param-2)", cmdUID->getText()); + + + vmime::shared_ptr cmdUIDs = + IMAPCommand::FETCH(vmime::net::messageSet::byUID(42, 47), params); + + VASSERT_NOT_NULL("Not null", cmdUIDs); + VASSERT_EQ("Text", "UID FETCH 42:47 (param-1 param-2)", cmdUIDs->getText()); + } + + void testSTORE() + { + std::vector flags; + flags.push_back("flag-1"); + flags.push_back("flag-2"); + + + vmime::shared_ptr cmdNum = IMAPCommand::STORE + (vmime::net::messageSet::byNumber(42), vmime::net::message::FLAG_MODE_SET, flags); + + VASSERT_NOT_NULL("Not null", cmdNum); + VASSERT_EQ("Text", "STORE 42 FLAGS (flag-1 flag-2)", cmdNum->getText()); + + + vmime::shared_ptr cmdNums = IMAPCommand::STORE + (vmime::net::messageSet::byNumber(42, 47), vmime::net::message::FLAG_MODE_SET, flags); + + VASSERT_NOT_NULL("Not null", cmdNums); + VASSERT_EQ("Text", "STORE 42:47 FLAGS (flag-1 flag-2)", cmdNums->getText()); + + + vmime::shared_ptr cmdUID = IMAPCommand::STORE + (vmime::net::messageSet::byUID(42), vmime::net::message::FLAG_MODE_SET, flags); + + VASSERT_NOT_NULL("Not null", cmdUID); + VASSERT_EQ("Text", "UID STORE 42 FLAGS (flag-1 flag-2)", cmdUID->getText()); + + + vmime::shared_ptr cmdUIDs = IMAPCommand::STORE + (vmime::net::messageSet::byUID(42, 47), vmime::net::message::FLAG_MODE_SET, flags); + + VASSERT_NOT_NULL("Not null", cmdUIDs); + VASSERT_EQ("Text", "UID STORE 42:47 FLAGS (flag-1 flag-2)", cmdUIDs->getText()); + + + vmime::shared_ptr cmdAdd = IMAPCommand::STORE + (vmime::net::messageSet::byUID(42, 47), vmime::net::message::FLAG_MODE_ADD, flags); + + VASSERT_NOT_NULL("Not null", cmdAdd); + VASSERT_EQ("Text", "UID STORE 42:47 +FLAGS (flag-1 flag-2)", cmdAdd->getText()); + + + vmime::shared_ptr cmdRem = IMAPCommand::STORE + (vmime::net::messageSet::byUID(42, 47), vmime::net::message::FLAG_MODE_REMOVE, flags); + + VASSERT_NOT_NULL("Not null", cmdRem); + VASSERT_EQ("Text", "UID STORE 42:47 -FLAGS (flag-1 flag-2)", cmdRem->getText()); + } + + void testAPPEND() + { + std::vector flags; + flags.push_back("flag-1"); + flags.push_back("flag-2"); + + + vmime::shared_ptr cmd = + IMAPCommand::APPEND("mailbox-name", flags, /* date */ NULL, 1234); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "APPEND mailbox-name (flag-1 flag-2) {1234}", cmd->getText()); + + + vmime::shared_ptr cmdQuote = + IMAPCommand::APPEND("mailbox name", flags, /* date */ NULL, 1234); + + VASSERT_NOT_NULL("Not null", cmdQuote); + VASSERT_EQ("Text", "APPEND \"mailbox name\" (flag-1 flag-2) {1234}", cmdQuote->getText()); + + + vmime::datetime date(2014, 3, 15, 23, 11, 47, vmime::datetime::GMT2); + vmime::shared_ptr cmdDate = + IMAPCommand::APPEND("mailbox name", flags, &date, 1234); + + VASSERT_NOT_NULL("Not null", cmdDate); + VASSERT_EQ("Text", "APPEND \"mailbox name\" (flag-1 flag-2) \"15-Mar-2014 23:11:47 +0200\" {1234}", cmdDate->getText()); + } + + void testCOPY() + { + vmime::shared_ptr cmdNum = + IMAPCommand::COPY(vmime::net::messageSet::byNumber(42), "mailbox-name"); + + VASSERT_NOT_NULL("Not null", cmdNum); + VASSERT_EQ("Text", "COPY 42 mailbox-name", cmdNum->getText()); + + + vmime::shared_ptr cmdNums = + IMAPCommand::COPY(vmime::net::messageSet::byNumber(42, 47), "mailbox-name"); + + VASSERT_NOT_NULL("Not null", cmdNums); + VASSERT_EQ("Text", "COPY 42:47 mailbox-name", cmdNums->getText()); + + + vmime::shared_ptr cmdUID = + IMAPCommand::COPY(vmime::net::messageSet::byUID(42), "mailbox-name"); + + VASSERT_NOT_NULL("Not null", cmdUID); + VASSERT_EQ("Text", "UID COPY 42 mailbox-name", cmdUID->getText()); + + + vmime::shared_ptr cmdUIDs = + IMAPCommand::COPY(vmime::net::messageSet::byUID(42, 47), "mailbox-name"); + + VASSERT_NOT_NULL("Not null", cmdUIDs); + VASSERT_EQ("Text", "UID COPY 42:47 mailbox-name", cmdUIDs->getText()); + + + vmime::shared_ptr cmdQuote = + IMAPCommand::COPY(vmime::net::messageSet::byNumber(42, 47), "mailbox name"); + + VASSERT_NOT_NULL("Not null", cmdQuote); + VASSERT_EQ("Text", "COPY 42:47 \"mailbox name\"", cmdQuote->getText()); + } + + void testSEARCH() + { + std::vector searchKeys; + searchKeys.push_back("search-key-1"); + searchKeys.push_back("search-key-2"); + + vmime::shared_ptr cmd = + IMAPCommand::SEARCH(searchKeys, /* charset */ NULL); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "SEARCH search-key-1 search-key-2", cmd->getText()); + + + vmime::charset cset("test-charset"); + + vmime::shared_ptr cmdCset = + IMAPCommand::SEARCH(searchKeys, &cset); + + VASSERT_NOT_NULL("Not null", cmdCset); + VASSERT_EQ("Text", "SEARCH CHARSET test-charset search-key-1 search-key-2", cmdCset->getText()); + } + + void testSTARTTLS() + { + vmime::shared_ptr cmd = IMAPCommand::STARTTLS(); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "STARTTLS", cmd->getText()); + } + + void testCAPABILITY() + { + vmime::shared_ptr cmd = IMAPCommand::CAPABILITY(); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "CAPABILITY", cmd->getText()); + } + + void testNOOP() + { + vmime::shared_ptr cmd = IMAPCommand::NOOP(); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "NOOP", cmd->getText()); + } + + void testEXPUNGE() + { + vmime::shared_ptr cmd = IMAPCommand::EXPUNGE(); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "EXPUNGE", cmd->getText()); + } + + void testCLOSE() + { + vmime::shared_ptr cmd = IMAPCommand::CLOSE(); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "CLOSE", cmd->getText()); + } + + void testLOGOUT() + { + vmime::shared_ptr cmd = IMAPCommand::LOGOUT(); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "LOGOUT", cmd->getText()); + } + + void testSend() + { + vmime::shared_ptr cmd = IMAPCommand::createCommand("MY_COMMAND param1 param2"); + + vmime::shared_ptr sess = + vmime::make_shared (); + + vmime::shared_ptr auth = + vmime::make_shared (); + + vmime::shared_ptr store = + vmime::make_shared (sess, auth, /* secured */ false); + + vmime::shared_ptr conn = + vmime::make_shared (store, auth); + + vmime::shared_ptr sok = vmime::make_shared (); + conn->setSocket(sok); + + cmd->send(conn); + + vmime::string response; + sok->localReceive(response); + + VASSERT_EQ("Sent buffer", vmime::string(*conn->getTag()) + " MY_COMMAND param1 param2\r\n", response); + } + +VMIME_TEST_SUITE_END