diff --git a/CHANGELOG b/CHANGELOG index abaf4568e..1963807d9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ Release 1.5.0 (2012-07-30) - IPAddress bitwise operators (&,|,^,~) - IPAddress BinaryReader/Writer << and >> operators - IPAddress force IPv6 always lowercase (RFC 5952) +- fixed SF#3538785: SMTPClientSession::sendMessage() should take recipient list Release 1.4.4 (2012-07-??) ========================== diff --git a/Net/include/Poco/Net/MailMessage.h b/Net/include/Poco/Net/MailMessage.h index 072dcade2..459996684 100644 --- a/Net/include/Poco/Net/MailMessage.h +++ b/Net/include/Poco/Net/MailMessage.h @@ -69,7 +69,7 @@ class Net_API MailMessage: public MessageHeader /// encodings are supported: 7bit, 8bit, quoted-printable /// and base64. { -public: +public: typedef std::vector Recipients; enum ContentDisposition @@ -94,6 +94,9 @@ public: void addRecipient(const MailRecipient& recipient); /// Adds a recipient for the message. + + void setRecipients(const Recipients& recipient); + /// Clears existing and sets new recipient list for the message. const Recipients& recipients() const; /// Returns the recipients of the message. diff --git a/Net/include/Poco/Net/SMTPClientSession.h b/Net/include/Poco/Net/SMTPClientSession.h index 016e980bb..d734d1ee7 100644 --- a/Net/include/Poco/Net/SMTPClientSession.h +++ b/Net/include/Poco/Net/SMTPClientSession.h @@ -59,6 +59,8 @@ class Net_API SMTPClientSession /// client for sending e-mail messages. { public: + typedef std::vector Recipients; + enum { SMTP_PORT = 25 @@ -130,7 +132,18 @@ public: void sendMessage(const MailMessage& message); /// Sends the given mail message by sending a MAIL FROM command, /// a RCPT TO command for every recipient, and a DATA command with - /// the message headers and content. + /// the message headers and content. Using this function results in + /// RCPT TO commands list generated from the recipient list supplied + /// with the message itself. + /// + /// Throws a SMTPException in case of a SMTP-specific error, or a + /// NetException in case of a general network communication failure. + + void sendMessage(const MailMessage& message, const Recipients& recipients); + /// Sends the given mail message by sending a MAIL FROM command, + /// a RCPT TO command for every recipient, and a DATA command with + /// the message headers and content. Using this function results in + /// message header being generated from the supplied recipients list. /// /// Throws a SMTPException in case of a SMTP-specific error, or a /// NetException in case of a general network communication failure. @@ -176,6 +189,9 @@ protected: DialogSocket& socket(); private: + void sendCommands(const MailMessage& message, const Recipients* pRecipients = 0); + void transportMessage(const MailMessage& message); + DialogSocket _socket; bool _isOpen; }; diff --git a/Net/src/MailMessage.cpp b/Net/src/MailMessage.cpp index 6a561e698..e60bc00d8 100644 --- a/Net/src/MailMessage.cpp +++ b/Net/src/MailMessage.cpp @@ -133,6 +133,12 @@ void MailMessage::addRecipient(const MailRecipient& recipient) } +void MailMessage::setRecipients(const Recipients& recipients) +{ + _recipients.assign(recipients.begin(), recipients.end()); +} + + void MailMessage::setSender(const std::string& sender) { set(HEADER_FROM, sender); diff --git a/Net/src/SMTPClientSession.cpp b/Net/src/SMTPClientSession.cpp index a36c397f8..a6df7253c 100644 --- a/Net/src/SMTPClientSession.cpp +++ b/Net/src/SMTPClientSession.cpp @@ -318,7 +318,7 @@ void SMTPClientSession::close() } -void SMTPClientSession::sendMessage(const MailMessage& message) +void SMTPClientSession::sendCommands(const MailMessage& message, const Recipients* pRecipients) { std::string response; int status = 0; @@ -335,17 +335,55 @@ void SMTPClientSession::sendMessage(const MailMessage& message) { status = sendCommand("MAIL FROM:", fromField.substr(emailPos, fromField.size() - emailPos), response); } + if (!isPositiveCompletion(status)) throw SMTPException("Cannot send message", response, status); - for (MailMessage::Recipients::const_iterator it = message.recipients().begin(); it != message.recipients().end(); ++it) + + std::ostringstream recipient; + if (pRecipients) { - std::string recipient("<"); - recipient.append(it->getAddress()); - recipient.append(">"); - int status = sendCommand("RCPT TO:", recipient, response); - if (!isPositiveCompletion(status)) throw SMTPException(std::string("Recipient rejected: ") + recipient, response, status); + for (Recipients::const_iterator it = pRecipients->begin(); it != pRecipients->end(); ++it) + { + recipient << '<' << *it << '>'; + int status = sendCommand("RCPT TO:", recipient.str(), response); + if (!isPositiveCompletion(status)) throw SMTPException(std::string("Recipient rejected: ") + recipient.str(), response, status); + recipient.str(""); + } } + else + { + for (MailMessage::Recipients::const_iterator it = message.recipients().begin(); it != message.recipients().end(); ++it) + { + recipient << '<' << it->getAddress() << '>'; + int status = sendCommand("RCPT TO:", recipient.str(), response); + if (!isPositiveCompletion(status)) throw SMTPException(std::string("Recipient rejected: ") + recipient.str(), response, status); + recipient.str(""); + } + } + status = sendCommand("DATA", response); if (!isPositiveIntermediate(status)) throw SMTPException("Cannot send message data", response, status); +} + + +void SMTPClientSession::sendMessage(const MailMessage& message) +{ + sendCommands(message); + transportMessage(message); +} + + +void SMTPClientSession::sendMessage(const MailMessage& message, const Recipients& recipients) +{ + sendCommands(message, &recipients); + transportMessage(message); +} + + +void SMTPClientSession::transportMessage(const MailMessage& message) +{ + std::string response; + int status = 0; + SocketOutputStream socketStream(_socket); MailOutputStream mailStream(socketStream); message.write(mailStream); diff --git a/Net/testsuite/src/SMTPClientSessionTest.cpp b/Net/testsuite/src/SMTPClientSessionTest.cpp index 0971d694d..660a9d42f 100644 --- a/Net/testsuite/src/SMTPClientSessionTest.cpp +++ b/Net/testsuite/src/SMTPClientSessionTest.cpp @@ -160,6 +160,141 @@ void SMTPClientSessionTest::testSend() } +void SMTPClientSessionTest::testSendMultiRecipient() +{ + DialogServer server; + server.addResponse("220 localhost SMTP ready"); + server.addResponse("250 Hello localhost"); + server.addResponse("250 OK"); + server.addResponse("250 OK"); + server.addResponse("250 OK"); + server.addResponse("250 OK"); + server.addResponse("354 Send data"); + server.addResponse("250 OK"); + server.addResponse("250 OK"); + server.addResponse("221 Bye"); + SMTPClientSession session("localhost", server.port()); + session.login("localhost"); + + MailMessage message; + message.setSender("john.doe@no.where"); + MailMessage::Recipients msgRecipients; + msgRecipients.push_back(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, "jane.doe@no.where", "Jane Doe")); + msgRecipients.push_back(MailRecipient(MailRecipient::CC_RECIPIENT, "jack.doe@no.where", "Jack Doe")); + msgRecipients.push_back(MailRecipient(MailRecipient::BCC_RECIPIENT, "joe.doe@no.where", "Joe Doe")); + message.setRecipients(msgRecipients); + message.setSubject("Test Message"); + message.setContent("Hello\r\nblah blah\r\n\r\nJohn\r\n"); + + server.clearCommands(); + session.sendMessage(message); + std::string cmd = server.popCommandWait(); + assert (cmd == "MAIL FROM: "); + cmd = server.popCommandWait(); + assert (cmd == "RCPT TO: "); + cmd = server.popCommandWait(); + assert (cmd == "RCPT TO: "); + cmd = server.popCommandWait(); + assert (cmd == "RCPT TO: "); + cmd = server.popCommandWait(); + assert (cmd == "DATA"); + cmd = server.popCommandWait(); + assert (cmd == "CC: Jack Doe "); + cmd = server.popCommandWait(); + assert (cmd == "Content-Transfer-Encoding: quoted-printable"); + cmd = server.popCommandWait(); + assert (cmd == "Content-Type: text/plain"); + cmd = server.popCommandWait(); + assert (cmd.substr(0, 4) == "Date"); + cmd = server.popCommandWait(); + assert (cmd == "From: john.doe@no.where"); + cmd = server.popCommandWait(); + assert (cmd == "Subject: Test Message"); + cmd = server.popCommandWait(); + assert (cmd == "To: Jane Doe "); + cmd = server.popCommandWait(); + assert (cmd == "Hello"); + cmd = server.popCommandWait(); + assert (cmd == "blah blah"); + cmd = server.popCommandWait(); + assert (cmd == "John"); + cmd = server.popCommandWait(); + assert (cmd == "."); + + session.close(); +} + + +void SMTPClientSessionTest::testMultiSeparateRecipient() +{ + DialogServer server; + server.addResponse("220 localhost SMTP ready"); + server.addResponse("250 Hello localhost"); + server.addResponse("250 OK"); + server.addResponse("250 OK"); + server.addResponse("250 OK"); + server.addResponse("250 OK"); + server.addResponse("354 Send data"); + server.addResponse("250 OK"); + server.addResponse("250 OK"); + server.addResponse("221 Bye"); + SMTPClientSession session("localhost", server.port()); + session.login("localhost"); + + MailMessage message; + message.setSender("john.doe@no.where"); + MailMessage::Recipients msgRecipients; + msgRecipients.push_back(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, "jane.doe@no.where", "Jane Doe")); + msgRecipients.push_back(MailRecipient(MailRecipient::CC_RECIPIENT, "jack.doe@no.where", "Jack Doe")); + msgRecipients.push_back(MailRecipient(MailRecipient::CC_RECIPIENT, "joe.doe@no.where", "Joe Doe")); + message.setRecipients(msgRecipients); + message.setSubject("Test Message"); + message.setContent("Hello\r\nblah blah\r\n\r\nJohn\r\n"); + + SMTPClientSession::Recipients recipients; + recipients.push_back("jill.doe@no.where"); + recipients.push_back("josh.doe@no.where"); + recipients.push_back("jake.doe@no.where"); + + server.clearCommands(); + session.sendMessage(message, recipients); + std::string cmd = server.popCommandWait(); + assert (cmd == "MAIL FROM: "); + cmd = server.popCommandWait(); + assert (cmd == "RCPT TO: "); + cmd = server.popCommandWait(); + assert (cmd == "RCPT TO: "); + cmd = server.popCommandWait(); + assert (cmd == "RCPT TO: "); + cmd = server.popCommandWait(); + assert (cmd == "DATA"); + cmd = server.popCommandWait(); + assert (cmd == "CC: Jack Doe , Joe Doe "); + cmd = server.popCommandWait(); + assert (cmd == "Content-Transfer-Encoding: quoted-printable"); + cmd = server.popCommandWait(); + assert (cmd == "Content-Type: text/plain"); + cmd = server.popCommandWait(); + assert (cmd.substr(0, 4) == "Date"); + cmd = server.popCommandWait(); + assert (cmd == "From: john.doe@no.where"); + cmd = server.popCommandWait(); + assert (cmd == "Subject: Test Message"); + cmd = server.popCommandWait(); + assert (cmd == "To: Jane Doe "); + cmd = server.popCommandWait(); + assert (cmd == "Hello"); + cmd = server.popCommandWait(); + assert (cmd == "blah blah"); + cmd = server.popCommandWait(); + assert (cmd == "John"); + cmd = server.popCommandWait(); + assert (cmd == "."); + + session.close(); +} + + void SMTPClientSessionTest::testSendFailed() { DialogServer server; @@ -210,6 +345,8 @@ CppUnit::Test* SMTPClientSessionTest::suite() CppUnit_addTest(pSuite, SMTPClientSessionTest, testLoginHELO); CppUnit_addTest(pSuite, SMTPClientSessionTest, testLoginFailed); CppUnit_addTest(pSuite, SMTPClientSessionTest, testSend); + CppUnit_addTest(pSuite, SMTPClientSessionTest, testSendMultiRecipient); + CppUnit_addTest(pSuite, SMTPClientSessionTest, testMultiSeparateRecipient); CppUnit_addTest(pSuite, SMTPClientSessionTest, testSendFailed); return pSuite; diff --git a/Net/testsuite/src/SMTPClientSessionTest.h b/Net/testsuite/src/SMTPClientSessionTest.h index e407d8c9a..0d13f30bd 100644 --- a/Net/testsuite/src/SMTPClientSessionTest.h +++ b/Net/testsuite/src/SMTPClientSessionTest.h @@ -50,6 +50,8 @@ public: void testLoginHELO(); void testLoginFailed(); void testSend(); + void testSendMultiRecipient(); + void testMultiSeparateRecipient(); void testSendFailed(); void setUp();