Add basic support for delivery status notifications (DSN)

Added possibility to send e-mails with explicit request for delivery status notification and its content.
This commit is contained in:
Jan Osusky 2017-10-20 14:02:33 +02:00
parent 0f99387648
commit e30766cf2a
10 changed files with 118 additions and 28 deletions

View File

@ -58,6 +58,7 @@ namespace mediaTypes {
const char* const MESSAGE_PARTIAL = "partial"; const char* const MESSAGE_PARTIAL = "partial";
const char* const MESSAGE_EXTERNAL_BODY = "external-body"; const char* const MESSAGE_EXTERNAL_BODY = "external-body";
const char* const MESSAGE_DISPOSITION_NOTIFICATION = "disposition-notification"; const char* const MESSAGE_DISPOSITION_NOTIFICATION = "disposition-notification";
const char* const MESSAGE_DELIVERY_STATUS = "delivery-status";
const char* const APPLICATION_OCTET_STREAM = "octet-stream"; const char* const APPLICATION_OCTET_STREAM = "octet-stream";
@ -232,5 +233,19 @@ namespace dispositionModifiers {
const char* const ERROR = "error"; const char* const ERROR = "error";
} }
// Constants for DSN (delivery status notification)
namespace dsn
{
const char* const NOTIFY = "NOTIFY";
const char* const NEVER = "NEVER";
const char* const SUCCESS = "SUCCESS";
const char* const FAILURE = "FAILURE";
const char* const DELAY = "DELAY";
const char* const ORCPT = "ORCPT";
const char* const RET = "RET";
const char* const FULL = "FULL";
const char* const HDRS = "HDRS";
const char* const ENVID = "ENVID";
}
} // vmime } // vmime

View File

@ -70,6 +70,7 @@ namespace vmime {
extern VMIME_EXPORT const char* const MESSAGE_PARTIAL; extern VMIME_EXPORT const char* const MESSAGE_PARTIAL;
extern VMIME_EXPORT const char* const MESSAGE_EXTERNAL_BODY; extern VMIME_EXPORT const char* const MESSAGE_EXTERNAL_BODY;
extern VMIME_EXPORT const char* const MESSAGE_DISPOSITION_NOTIFICATION; extern VMIME_EXPORT const char* const MESSAGE_DISPOSITION_NOTIFICATION;
extern VMIME_EXPORT const char* const MESSAGE_DELIVERY_STATUS;
extern VMIME_EXPORT const char* const APPLICATION_OCTET_STREAM; extern VMIME_EXPORT const char* const APPLICATION_OCTET_STREAM;
@ -250,6 +251,21 @@ namespace vmime {
extern VMIME_EXPORT const char* const ERROR; extern VMIME_EXPORT const char* const ERROR;
} }
/** Constants for DSN (delivery status notification) */
namespace dsn
{
extern VMIME_EXPORT const char* const NOTIFY;
extern VMIME_EXPORT const char* const NEVER;
extern VMIME_EXPORT const char* const SUCCESS;
extern VMIME_EXPORT const char* const FAILURE;
extern VMIME_EXPORT const char* const DELAY;
extern VMIME_EXPORT const char* const ORCPT;
extern VMIME_EXPORT const char* const RET;
extern VMIME_EXPORT const char* const FULL;
extern VMIME_EXPORT const char* const HDRS;
extern VMIME_EXPORT const char* const ENVID;
}
} }

View File

@ -148,7 +148,9 @@ void sendmailTransport::send(
utility::inputStream& is, utility::inputStream& is,
const size_t size, const size_t size,
utility::progressListener* progress, utility::progressListener* progress,
const mailbox& sender const mailbox& sender,
const std::string& /*dsnNotify*/, const std::string& /*dsnRet*/,
const std::string& /*dsnEnvelopId*/
) { ) {
// If no recipient/expeditor was found, throw an exception // If no recipient/expeditor was found, throw an exception

View File

@ -73,7 +73,10 @@ public:
utility::inputStream& is, utility::inputStream& is,
const size_t size, const size_t size,
utility::progressListener* progress = NULL, utility::progressListener* progress = NULL,
const mailbox& sender = mailbox() const mailbox& sender = mailbox(),
const std::string& dsnNotify = std::string(),
const std::string& dsnRet = std::string(),
const std::string& dsnEnvelopId = std::string()
); );
bool isSecuredConnection() const; bool isSecuredConnection() const;

View File

@ -100,14 +100,16 @@ shared_ptr <SMTPCommand> SMTPCommand::STARTTLS() {
// static // static
shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8) { shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8,
const std::string& dsnRet, const std::string& dsnEnvelopId) {
return MAIL(mbox, utf8, 0); return MAIL(mbox, utf8, 0, dsnRet, dsnEnvelopId);
} }
// static // static
shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8, const size_t size) { shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8, const size_t size,
const std::string& dsnRet, const std::string& dsnEnvelopId) {
std::ostringstream cmd; std::ostringstream cmd;
cmd.imbue(std::locale::classic()); cmd.imbue(std::locale::classic());
@ -125,6 +127,13 @@ shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8,
cmd << ">"; cmd << ">";
if (!dsnRet.empty()) {
cmd << " " << dsn::RET << "=" << dsnRet;
}
if (!dsnEnvelopId.empty()) {
cmd << " " << dsn::ENVID << "=<" << dsnEnvelopId << ">";
}
if (utf8) { if (utf8) {
cmd << " SMTPUTF8"; cmd << " SMTPUTF8";
} }
@ -138,7 +147,8 @@ shared_ptr <SMTPCommand> SMTPCommand::MAIL(const mailbox& mbox, const bool utf8,
// static // static
shared_ptr <SMTPCommand> SMTPCommand::RCPT(const mailbox& mbox, const bool utf8) { shared_ptr <SMTPCommand> SMTPCommand::RCPT(const mailbox& mbox, const bool utf8,
const string& dsnNotify) {
std::ostringstream cmd; std::ostringstream cmd;
cmd.imbue(std::locale::classic()); cmd.imbue(std::locale::classic());
@ -156,6 +166,10 @@ shared_ptr <SMTPCommand> SMTPCommand::RCPT(const mailbox& mbox, const bool utf8)
cmd << ">"; cmd << ">";
if (!dsnNotify.empty()) {
cmd << " " << dsn::NOTIFY << "=" << dsnNotify;
}
return createCommand(cmd.str()); return createCommand(cmd.str());
} }

View File

@ -63,9 +63,12 @@ public:
static shared_ptr <SMTPCommand> AUTH(const string& mechName); static shared_ptr <SMTPCommand> AUTH(const string& mechName);
static shared_ptr <SMTPCommand> AUTH(const string& mechName, const std::string& initialResponse); static shared_ptr <SMTPCommand> AUTH(const string& mechName, const std::string& initialResponse);
static shared_ptr <SMTPCommand> STARTTLS(); static shared_ptr <SMTPCommand> STARTTLS();
static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8); static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8,
static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, const size_t size); const std::string& dsnRet, const std::string& dsnEnvelopId);
static shared_ptr <SMTPCommand> RCPT(const mailbox& mbox, const bool utf8); static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, const size_t size,
const std::string& dsnRet, const std::string& dsnEnvelopId);
static shared_ptr <SMTPCommand> RCPT(const mailbox& mbox, const bool utf8,
const std::string& dsnNotify);
static shared_ptr <SMTPCommand> RSET(); static shared_ptr <SMTPCommand> RSET();
static shared_ptr <SMTPCommand> DATA(); static shared_ptr <SMTPCommand> DATA();
static shared_ptr <SMTPCommand> BDAT(const size_t chunkSize, const bool last); static shared_ptr <SMTPCommand> BDAT(const size_t chunkSize, const bool last);

View File

@ -183,7 +183,8 @@ void SMTPTransport::sendEnvelope(
const mailboxList& recipients, const mailboxList& recipients,
const mailbox& sender, const mailbox& sender,
bool sendDATACommand, bool sendDATACommand,
const size_t size const size_t size,
const std::string& dsnNotify, const std::string& dsnRet, const std::string& dsnEnvelopId
) { ) {
// If no recipient/expeditor was found, throw an exception // If no recipient/expeditor was found, throw an exception
if (recipients.isEmpty()) { if (recipients.isEmpty()) {
@ -229,7 +230,7 @@ void SMTPTransport::sendEnvelope(
commands->addCommand( commands->addCommand(
SMTPCommand::MAIL( SMTPCommand::MAIL(
sender, hasSMTPUTF8 && needSMTPUTF8, hasSize ? size : 0 sender, hasSMTPUTF8 && needSMTPUTF8, hasSize ? size : 0, dsnRet, dsnEnvelopId
) )
); );
@ -237,7 +238,7 @@ void SMTPTransport::sendEnvelope(
commands->addCommand( commands->addCommand(
SMTPCommand::MAIL( SMTPCommand::MAIL(
expeditor, hasSMTPUTF8 && needSMTPUTF8, hasSize ? size : 0 expeditor, hasSMTPUTF8 && needSMTPUTF8, hasSize ? size : 0, dsnRet, dsnEnvelopId
) )
); );
} }
@ -249,7 +250,7 @@ void SMTPTransport::sendEnvelope(
for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) { for (size_t i = 0 ; i < recipients.getMailboxCount() ; ++i) {
const mailbox& mbox = *recipients.getMailboxAt(i); const mailbox& mbox = *recipients.getMailboxAt(i);
commands->addCommand(SMTPCommand::RCPT(mbox, hasSMTPUTF8 && needSMTPUTF8)); commands->addCommand(SMTPCommand::RCPT(mbox, hasSMTPUTF8 && needSMTPUTF8, dsnNotify));
} }
// Prepare sending of message data // Prepare sending of message data
@ -375,7 +376,8 @@ void SMTPTransport::send(
utility::inputStream& is, utility::inputStream& is,
const size_t size, const size_t size,
utility::progressListener* progress, utility::progressListener* progress,
const mailbox& sender const mailbox& sender,
const std::string& dsnNotify, const std::string& dsnRet, const std::string& dsnEnvelopId
) { ) {
if (!isConnected()) { if (!isConnected()) {
@ -383,7 +385,8 @@ void SMTPTransport::send(
} }
// Send message envelope // Send message envelope
sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ true, size); sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ true, size,
dsnNotify, dsnRet, dsnEnvelopId);
// Send the message data // Send the message data
// Stream copy with "\n." to "\n.." transformation // Stream copy with "\n." to "\n.." transformation
@ -415,7 +418,8 @@ void SMTPTransport::send(
const mailbox& expeditor, const mailbox& expeditor,
const mailboxList& recipients, const mailboxList& recipients,
utility::progressListener* progress, utility::progressListener* progress,
const mailbox& sender const mailbox& sender,
const std::string& dsnNotify, const std::string& dsnRet, const std::string& dsnEnvelopId
) { ) {
if (!isConnected()) { if (!isConnected()) {
@ -442,14 +446,14 @@ void SMTPTransport::send(
utility::inputStreamStringAdapter isAdapter(str); utility::inputStreamStringAdapter isAdapter(str);
send(expeditor, recipients, isAdapter, str.length(), progress, sender); send(expeditor, recipients, isAdapter, str.length(), progress, sender, dsnNotify, dsnRet, dsnEnvelopId);
return; return;
} }
// Send message envelope // Send message envelope
const size_t msgSize = msg->getGeneratedSize(ctx); const size_t msgSize = msg->getGeneratedSize(ctx);
sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ false, msgSize); sendEnvelope(expeditor, recipients, sender, /* sendDATACommand */ false, msgSize, dsnNotify, dsnRet, dsnEnvelopId);
// Send the message by chunks // Send the message by chunks
SMTPChunkingOutputStreamAdapter chunkStream(m_connection, msgSize, progress); SMTPChunkingOutputStreamAdapter chunkStream(m_connection, msgSize, progress);

View File

@ -78,7 +78,10 @@ public:
utility::inputStream& is, utility::inputStream& is,
const size_t size, const size_t size,
utility::progressListener* progress = NULL, utility::progressListener* progress = NULL,
const mailbox& sender = mailbox() const mailbox& sender = mailbox(),
const std::string& dsnNotify = std::string(),
const std::string& dsnRet = std::string(),
const std::string& dsnEnvelopId = std::string()
); );
void send( void send(
@ -86,7 +89,10 @@ public:
const mailbox& expeditor, const mailbox& expeditor,
const mailboxList& recipients, const mailboxList& recipients,
utility::progressListener* progress = NULL, utility::progressListener* progress = NULL,
const mailbox& sender = mailbox() const mailbox& sender = mailbox(),
const std::string& dsnNotify = std::string(),
const std::string& dsnRet = std::string(),
const std::string& dsnEnvelopId = std::string()
); );
bool isSecuredConnection() const; bool isSecuredConnection() const;
@ -108,13 +114,19 @@ private:
* @param sender envelope sender (if empty, expeditor will be used) * @param sender envelope sender (if empty, expeditor will be used)
* @param sendDATACommand if true, the DATA command will be sent * @param sendDATACommand if true, the DATA command will be sent
* @param size message size, in bytes (or 0, if not known) * @param size message size, in bytes (or 0, if not known)
* @param dsnNotify comma separated list of notification conditions as specified in RFC 1891
* @param dsnRet content of DSN - full message or headers only ("FULL" or "HDRS")
* @param dsnEnvelopId envelop ID to be able to pair the DSN with the original message
*/ */
void sendEnvelope( void sendEnvelope(
const mailbox& expeditor, const mailbox& expeditor,
const mailboxList& recipients, const mailboxList& recipients,
const mailbox& sender, const mailbox& sender,
bool sendDATACommand, bool sendDATACommand,
const size_t size const size_t size,
const std::string& dsnNotify,
const std::string& dsnRet,
const std::string& dsnEnvelopId
); );

View File

@ -136,7 +136,9 @@ static void extractMailboxes(
void transport::send( void transport::send(
const shared_ptr <vmime::message>& msg, const shared_ptr <vmime::message>& msg,
utility::progressListener* progress utility::progressListener* progress,
const std::string& dsnNotify, const std::string& dsnRet,
const std::string& dsnEnvelopId
) { ) {
// Extract expeditor // Extract expeditor
@ -221,7 +223,8 @@ void transport::send(
} headerExchanger(msg, hdr); } headerExchanger(msg, hdr);
send(msg, expeditor, recipients, progress, sender); send(msg, expeditor, recipients, progress, sender,
dsnNotify, dsnRet, dsnEnvelopId);
} }
@ -230,7 +233,8 @@ void transport::send(
const mailbox& expeditor, const mailbox& expeditor,
const mailboxList& recipients, const mailboxList& recipients,
utility::progressListener* progress, utility::progressListener* progress,
const mailbox& sender const mailbox& sender,
const std::string& dsnNotify, const std::string& dsnRet, const std::string& dsnEnvelopId
) { ) {
// Generate the message, "stream" it and delegate the sending // Generate the message, "stream" it and delegate the sending
@ -244,7 +248,8 @@ void transport::send(
utility::inputStreamStringAdapter isAdapter(str); utility::inputStreamStringAdapter isAdapter(str);
send(expeditor, recipients, isAdapter, str.length(), progress, sender); send(expeditor, recipients, isAdapter, str.length(), progress, sender,
dsnNotify, dsnRet, dsnEnvelopId);
} }

View File

@ -69,10 +69,16 @@ public:
* *
* @param msg message to send * @param msg message to send
* @param progress progress listener, or NULL if not used * @param progress progress listener, or NULL if not used
* @param dsnNotify comma separated list of notification conditions as specified in RFC 1891
* @param dsnRet content of DSN - full message or headers only ("FULL" or "HDRS")
* @param dsnEnvelopId envelop ID to be able to pair the DSN with original message
*/ */
virtual void send( virtual void send(
const shared_ptr <vmime::message>& msg, const shared_ptr <vmime::message>& msg,
utility::progressListener* progress = NULL utility::progressListener* progress = NULL,
const std::string& dsnNotify = std::string(),
const std::string& dsnRet = std::string(),
const std::string& dsnEnvelopId = std::string()
); );
/** Send a message over this transport service. /** Send a message over this transport service.
@ -83,6 +89,10 @@ public:
* @param size size of the message data * @param size size of the message data
* @param progress progress listener, or NULL if not used * @param progress progress listener, or NULL if not used
* @param sender envelope sender (if empty, expeditor will be used) * @param sender envelope sender (if empty, expeditor will be used)
* @param dsnNotify comma separated list of notification conditions as specified in RFC 1891
* @param dsnRet content of DSN - full message or headers only ("FULL" or "HDRS")
* @param dsnEnvelopId envelope identifier to be transmitted along with the message
* to be able to pair the DSN with original message (plain text not in "<" ">")
*/ */
virtual void send( virtual void send(
const mailbox& expeditor, const mailbox& expeditor,
@ -90,7 +100,10 @@ public:
utility::inputStream& is, utility::inputStream& is,
const size_t size, const size_t size,
utility::progressListener* progress = NULL, utility::progressListener* progress = NULL,
const mailbox& sender = mailbox() const mailbox& sender = mailbox(),
const std::string& dsnNotify = std::string(),
const std::string& dsnRet = std::string(),
const std::string& dsnEnvelopId = std::string()
) = 0; ) = 0;
/** Send a message over this transport service. /** Send a message over this transport service.
@ -108,7 +121,10 @@ public:
const mailbox& expeditor, const mailbox& expeditor,
const mailboxList& recipients, const mailboxList& recipients,
utility::progressListener* progress = NULL, utility::progressListener* progress = NULL,
const mailbox& sender = mailbox() const mailbox& sender = mailbox(),
const std::string& dsnNotify = std::string(),
const std::string& dsnRet = std::string(),
const std::string& dsnEnvelopId = std::string()
); );