From 38a86328282779dd8e94ad55e3c10687ebfbe979 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 28 Apr 2005 10:57:41 +0000 Subject: [PATCH] Sendmail implementation + filtered streams. --- ChangeLog | 13 + SConstruct | 101 ++++- src/exception.cpp | 12 + src/messaging/builtinServices.inl | 5 + src/messaging/imap/IMAPFolder.cpp | 6 +- src/messaging/imap/IMAPMessage.cpp | 13 +- src/messaging/maildir/maildirFolder.cpp | 9 +- src/messaging/maildir/maildirMessage.cpp | 6 +- src/messaging/pop3/POP3Folder.cpp | 6 +- src/messaging/pop3/POP3Message.cpp | 7 +- src/messaging/pop3/POP3Store.cpp | 6 +- src/messaging/sendmail/sendmailTransport.cpp | 387 ++++++++++++++++++ src/messaging/smtp/SMTPTransport.cpp | 64 +-- src/messaging/transport.cpp | 2 +- src/utility/filteredStream.cpp | 115 ++++++ src/utility/stream.cpp | 43 ++ vmime/exception.hpp | 15 + vmime/messaging/folder.hpp | 10 +- vmime/messaging/imap/IMAPFolder.hpp | 6 +- vmime/messaging/imap/IMAPMessage.hpp | 6 +- vmime/messaging/imap/IMAPParser.hpp | 16 +- vmime/messaging/maildir/maildirFolder.hpp | 8 +- vmime/messaging/maildir/maildirMessage.hpp | 6 +- vmime/messaging/message.hpp | 7 +- vmime/messaging/pop3/POP3Folder.hpp | 6 +- vmime/messaging/pop3/POP3Message.hpp | 4 +- vmime/messaging/pop3/POP3Store.hpp | 4 +- .../messaging/sendmail/sendmailTransport.hpp | 98 +++++ vmime/messaging/service.hpp | 4 +- vmime/messaging/serviceFactory.hpp | 3 +- vmime/messaging/session.hpp | 2 +- vmime/messaging/smtp/SMTPTransport.hpp | 2 +- vmime/messaging/transport.hpp | 4 +- vmime/utility/filteredStream.hpp | 116 ++++++ .../progressionListener.hpp | 16 +- vmime/utility/stream.hpp | 47 +++ vmime/vmime.hpp | 1 + 37 files changed, 1025 insertions(+), 151 deletions(-) create mode 100644 src/messaging/sendmail/sendmailTransport.cpp create mode 100644 src/utility/filteredStream.cpp create mode 100644 vmime/messaging/sendmail/sendmailTransport.hpp create mode 100644 vmime/utility/filteredStream.hpp rename vmime/{messaging => utility}/progressionListener.hpp (81%) diff --git a/ChangeLog b/ChangeLog index 19593b2c..53e4effb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,19 @@ VERSION 0.7.1cvs ================ +2005-04-27 Vincent Richard + + * progressionListener.{hpp|cpp}: moved to 'vmime::utility' package since + this can be of general use. + + * stream.{hpp|cpp}: added a bufferedStreamCopy() function which can + take a 'progressionListener' parameter. + + * filteredStream.{hpp|cpp}: new feature added: filtered input and + output streams. + + * Added 'sendmail' transport service for local delivery. + 2005-04-19 Vincent Richard * defaultParameter.cpp: fixed a bug in implementation of RFC-2231 (values diff --git a/SConstruct b/SConstruct index 811bd828..b47344b3 100644 --- a/SConstruct +++ b/SConstruct @@ -148,8 +148,10 @@ libvmime_sources = [ 'vmime.hpp', 'utility/file.hpp', 'utility/datetimeUtils.cpp', 'utility/datetimeUtils.hpp', + 'utility/filteredStream.cpp', 'utility/filteredStream.hpp', 'utility/md5.cpp', 'utility/md5.hpp', 'utility/path.cpp', 'utility/path.hpp', + 'utility/progressionListener.hpp', 'utility/random.cpp', 'utility/random.hpp', 'utility/smartPtr.hpp', 'utility/stream.cpp', 'utility/stream.hpp', @@ -185,7 +187,6 @@ libvmime_messaging_sources = [ 'messaging/events.cpp', 'messaging/events.hpp', 'messaging/folder.cpp', 'messaging/folder.hpp', 'messaging/message.cpp', 'messaging/message.hpp', - 'messaging/progressionListener.hpp', 'messaging/service.cpp', 'messaging/service.hpp', 'messaging/serviceFactory.cpp', 'messaging/serviceFactory.hpp', 'messaging/serviceInfos.hpp', @@ -232,6 +233,12 @@ libvmime_messaging_proto_sources = [ 'messaging/maildir/maildirMessage.cpp', 'messaging/maildir/maildirMessage.hpp', 'messaging/maildir/maildirUtils.cpp', 'messaging/maildir/maildirUtils.hpp' ] + ], + [ + 'sendmail', + [ + 'messaging/sendmail/sendmailTransport.cpp', 'messaging/sendmail/sendmailTransport.hpp' + ] ] ] @@ -413,7 +420,11 @@ TargetSignatures('build') ############# # Try to guess some default values -# TODO +defaultSendmailPath = WhereIs("sendmail") + +if defaultSendmailPath == None: + defaultSendmailPath = '' + # Command line options opts = Options('options.cache') @@ -453,8 +464,8 @@ opts.AddOptions( 'Specifies which protocols to build into the library.\n' + 'This option has no effect if "with_messaging" is not activated.\n' + 'Separate protocols with spaces; string must be quoted with ".\n' - + 'Currently available protocols: pop3, smtp, imap, maildir.', - '"pop3 smtp imap maildir"' + + 'Currently available protocols: pop3, smtp, imap, maildir, sendmail.', + '"pop3 smtp imap maildir sendmail"' ), ( 'with_platforms', @@ -466,6 +477,11 @@ opts.AddOptions( + 'Currently available platform handlers: posix.', '"posix"' ), + ( + 'sendmail_path', + 'Specifies the path to sendmail.', + defaultSendmailPath + ), EnumOption( 'with_wide_char_support', 'Support for wide characters (rarely used, should be set to "no")', @@ -517,6 +533,7 @@ opts.AddOptions( ) + ############################### # Configuration Environment # ############################### @@ -583,20 +600,6 @@ for platform in re.split('\W+', string.replace(env['with_platforms'], '"', '')): if len(platform) >= 1: platforms.append(platform) -# Show configuration summary -print "" -print "+=================+" -print "| CONFIGURATION |" -print "+=================+" -print "" -print "Installation prefix : " + env['prefix'] -print "Debugging mode : " + env['debug'] -print "Messaging support : " + env['with_messaging'] -if env['with_messaging'] == 'yes': - print " * protocols : " + env['with_messaging_protocols'] -print "File-system support : " + env['with_filesystem'] -print "Platform handlers : " + env['with_platforms'] -print "" ######################## @@ -609,6 +612,7 @@ def IsProtocolSupported(protoList, proto): return 1 return 0 + # File-system support must be activated when 'maildir' protocol is selected if env['with_messaging'] == 'yes': if IsProtocolSupported(messaging_protocols, 'maildir'): @@ -616,6 +620,48 @@ if env['with_messaging'] == 'yes': print "ERROR: 'maildir' protocol requires file-system support!\n" Exit(1) +# Sendmail transport is only available on POSIX platforms +if os.name != 'posix': + if IsProtocolSupported(messaging_protocols, 'sendmail'): + print "WARNING: ignoring 'sendmail' support (only available on POSIX platforms)\n" + + newProtoList = [ ] + + for p in messaging_protocols: + if string.upper(p) != "SENDMAIL": + newProtoList.append(p) + + messaging_protocols = newProtoList + +# Check sendmail path +if IsProtocolSupported(messaging_protocols, 'sendmail'): + if env['sendmail_path'] == '': + print "ERROR: no path specified for 'sendmail'" + Exit(1) + + +########################### +# Configuration summary # +########################### + +print "" +print "+=================+" +print "| CONFIGURATION |" +print "+=================+" +print "" +print "Installation prefix : " + env['prefix'] +print "Debugging mode : " + env['debug'] +print "Messaging support : " + env['with_messaging'] +if env['with_messaging'] == 'yes': + print " * protocols : " + env['with_messaging_protocols'] +print "File-system support : " + env['with_filesystem'] +print "Platform handlers : " + env['with_platforms'] + +if IsProtocolSupported(messaging_protocols, 'sendmail'): + print "Sendmail path : " + env['sendmail_path'] + +print "" + ######################### # Generate config.hpp # @@ -724,6 +770,12 @@ for platform in libvmime_platforms_sources: if not platform in platforms: config_hpp.write('#define VMIME_BUILTIN_PLATFORM_' + string.upper(platform) + ' 0\n') +config_hpp.write('\n') +config_hpp.write('// Miscellaneous flags\n') + +if IsProtocolSupported(messaging_protocols, 'sendmail'): + config_hpp.write('#define VMIME_SENDMAIL_PATH "' + env['sendmail_path'] + '"\n') + config_hpp.write(""" #endif // VMIME_CONFIG_HPP_INCLUDED @@ -1432,6 +1484,13 @@ x*) esac +# +# System mail +# + +AC_PATH_PROG(SENDMAIL, sendmail, /usr/sbin/sendmail, /usr/sbin:/usr/lib) + + # # Detect some platform-specific stuff # @@ -1629,6 +1688,10 @@ typedef unsigned ${VMIME_TYPE_INT32} vmime_uint32; configure_in.write(""" " > vmime/config.hpp +# Miscellaneous flags +echo "// Miscellaneous flags" >> vmime/config.hpp +echo "#define VMIME_SENDMAIL_PATH \\"$SENDMAIL\\"" >> vmime/config.hpp +echo "" >> vmime/config.hpp # Additional defines echo "// Additional defines" >> vmime/config.hpp @@ -1926,7 +1989,7 @@ typedef unsigned int vmime_uint32; // -- Messaging support #define VMIME_HAVE_MESSAGING_FEATURES 1 // -- Built-in messaging protocols -#define VMIME_BUILTIN_MESSAGING_PROTOS " pop3 smtp imap maildir" +#define VMIME_BUILTIN_MESSAGING_PROTOS "pop3 smtp imap maildir" #define VMIME_BUILTIN_MESSAGING_PROTO_POP3 1 #define VMIME_BUILTIN_MESSAGING_PROTO_SMTP 1 #define VMIME_BUILTIN_MESSAGING_PROTO_IMAP 1 diff --git a/src/exception.cpp b/src/exception.cpp index b7be006c..bac5c728 100644 --- a/src/exception.cpp +++ b/src/exception.cpp @@ -294,6 +294,18 @@ exception* invalid_argument::clone() const { return new invalid_argument(*this); const string invalid_argument::name() const { return "invalid_argument"; } +// +// system_error +// + +system_error::~system_error() throw() { } +system_error::system_error(const string& what, const exception& other) + : exception(what, other) {} + +exception* system_error::clone() const { return new system_error(*this); } +const string system_error::name() const { return "system_error"; } + + #if VMIME_HAVE_MESSAGING_FEATURES diff --git a/src/messaging/builtinServices.inl b/src/messaging/builtinServices.inl index cea2f7a8..d1b63a30 100644 --- a/src/messaging/builtinServices.inl +++ b/src/messaging/builtinServices.inl @@ -47,5 +47,10 @@ REGISTER_SERVICE(maildir::maildirStore, maildir); #endif +#if VMIME_BUILTIN_MESSAGING_PROTO_SENDMAIL + #include "vmime/messaging/sendmail/sendmailTransport.hpp" + REGISTER_SERVICE(sendmail::sendmailTransport, sendmail); +#endif + #endif // VMIME_BUILDING_DOC diff --git a/src/messaging/imap/IMAPFolder.cpp b/src/messaging/imap/IMAPFolder.cpp index 5bbfa148..39404c82 100644 --- a/src/messaging/imap/IMAPFolder.cpp +++ b/src/messaging/imap/IMAPFolder.cpp @@ -596,7 +596,7 @@ std::vector IMAPFolder::getFolders(const bool recursive) void IMAPFolder::fetchMessages(std::vector & msg, const int options, - progressionListener* progress) + utility::progressionListener* progress) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); @@ -1045,7 +1045,7 @@ void IMAPFolder::setMessageFlags(const string& set, const int flags, const int m void IMAPFolder::addMessage(vmime::message* msg, const int flags, - vmime::datetime* date, progressionListener* progress) + vmime::datetime* date, utility::progressionListener* progress) { std::ostringstream oss; utility::outputStreamAdapter ossAdapter(oss); @@ -1060,7 +1060,7 @@ void IMAPFolder::addMessage(vmime::message* msg, const int flags, void IMAPFolder::addMessage(utility::inputStream& is, const int size, const int flags, - vmime::datetime* date, progressionListener* progress) + vmime::datetime* date, utility::progressionListener* progress) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); diff --git a/src/messaging/imap/IMAPMessage.cpp b/src/messaging/imap/IMAPMessage.cpp index 629668c5..a2220c68 100644 --- a/src/messaging/imap/IMAPMessage.cpp +++ b/src/messaging/imap/IMAPMessage.cpp @@ -243,7 +243,7 @@ class IMAPMessage_literalHandler : public IMAPParser::literalHandler { public: - IMAPMessage_literalHandler(utility::outputStream& os, progressionListener* progress) + IMAPMessage_literalHandler(utility::outputStream& os, utility::progressionListener* progress) : m_os(os), m_progress(progress) { } @@ -268,7 +268,7 @@ public: private: utility::outputStream& m_os; - progressionListener* m_progress; + utility::progressionListener* m_progress; }; #endif // VMIME_BUILDING_DOC @@ -366,7 +366,7 @@ const header& IMAPMessage::getHeader() const } -void IMAPMessage::extract(utility::outputStream& os, progressionListener* progress, +void IMAPMessage::extract(utility::outputStream& os, utility::progressionListener* progress, const int start, const int length) const { if (!m_folder) @@ -377,7 +377,7 @@ void IMAPMessage::extract(utility::outputStream& os, progressionListener* progre void IMAPMessage::extractPart - (const part& p, utility::outputStream& os, progressionListener* progress, + (const part& p, utility::outputStream& os, utility::progressionListener* progress, const int start, const int length) const { if (!m_folder) @@ -401,8 +401,9 @@ void IMAPMessage::fetchPartHeader(part& p) } -void IMAPMessage::extract(const part* p, utility::outputStream& os, progressionListener* progress, - const int start, const int length, const bool headerOnly) const +void IMAPMessage::extract(const part* p, utility::outputStream& os, + utility::progressionListener* progress, const int start, + const int length, const bool headerOnly) const { IMAPMessage_literalHandler literalHandler(os, progress); diff --git a/src/messaging/maildir/maildirFolder.cpp b/src/messaging/maildir/maildirFolder.cpp index c3b7e71c..fa337d09 100644 --- a/src/messaging/maildir/maildirFolder.cpp +++ b/src/messaging/maildir/maildirFolder.cpp @@ -839,7 +839,7 @@ void maildirFolder::setMessageFlagsImpl void maildirFolder::addMessage(vmime::message* msg, const int flags, - vmime::datetime* date, progressionListener* progress) + vmime::datetime* date, utility::progressionListener* progress) { std::ostringstream oss; utility::outputStreamAdapter ossAdapter(oss); @@ -854,7 +854,7 @@ void maildirFolder::addMessage(vmime::message* msg, const int flags, void maildirFolder::addMessage(utility::inputStream& is, const int size, - const int flags, vmime::datetime* /* date */, progressionListener* progress) + const int flags, vmime::datetime* /* date */, utility::progressionListener* progress) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); @@ -938,7 +938,8 @@ void maildirFolder::addMessage(utility::inputStream& is, const int size, void maildirFolder::copyMessageImpl(const utility::file::path& tmpDirPath, const utility::file::path& curDirPath, const utility::file::path::component& filename, - utility::inputStream& is, const utility::stream::size_type size, progressionListener* progress) + utility::inputStream& is, const utility::stream::size_type size, + utility::progressionListener* progress) { utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); @@ -1295,7 +1296,7 @@ store* maildirFolder::getStore() void maildirFolder::fetchMessages(std::vector & msg, - const int options, progressionListener* progress) + const int options, utility::progressionListener* progress) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); diff --git a/src/messaging/maildir/maildirMessage.cpp b/src/messaging/maildir/maildirMessage.cpp index 9b728359..2815e980 100644 --- a/src/messaging/maildir/maildirMessage.cpp +++ b/src/messaging/maildir/maildirMessage.cpp @@ -309,14 +309,14 @@ void maildirMessage::setFlags(const int flags, const int mode) void maildirMessage::extract(utility::outputStream& os, - progressionListener* progress, const int start, const int length) const + utility::progressionListener* progress, const int start, const int length) const { extractImpl(os, progress, 0, m_size, start, length); } void maildirMessage::extractPart(const part& p, utility::outputStream& os, - progressionListener* progress, const int start, const int length) const + utility::progressionListener* progress, const int start, const int length) const { const maildirPart& mp = dynamic_cast (p); @@ -324,7 +324,7 @@ void maildirMessage::extractPart(const part& p, utility::outputStream& os, } -void maildirMessage::extractImpl(utility::outputStream& os, progressionListener* progress, +void maildirMessage::extractImpl(utility::outputStream& os, utility::progressionListener* progress, const int start, const int length, const int partialStart, const int partialLength) const { utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); diff --git a/src/messaging/pop3/POP3Folder.cpp b/src/messaging/pop3/POP3Folder.cpp index c70e2b5b..6f4cb237 100644 --- a/src/messaging/pop3/POP3Folder.cpp +++ b/src/messaging/pop3/POP3Folder.cpp @@ -293,7 +293,7 @@ std::vector POP3Folder::getFolders(const bool /* recursive */) void POP3Folder::fetchMessages(std::vector & msg, const int options, - progressionListener* progress) + utility::progressionListener* progress) { if (!m_store) throw exceptions::illegal_state("Store disconnected"); @@ -681,14 +681,14 @@ void POP3Folder::rename(const folder::path& /* newPath */) void POP3Folder::addMessage(vmime::message* /* msg */, const int /* flags */, - vmime::datetime* /* date */, progressionListener* /* progress */) + vmime::datetime* /* date */, utility::progressionListener* /* progress */) { throw exceptions::operation_not_supported(); } void POP3Folder::addMessage(utility::inputStream& /* is */, const int /* size */, const int /* flags */, - vmime::datetime* /* date */, progressionListener* /* progress */) + vmime::datetime* /* date */, utility::progressionListener* /* progress */) { throw exceptions::operation_not_supported(); } diff --git a/src/messaging/pop3/POP3Message.cpp b/src/messaging/pop3/POP3Message.cpp index 01680d34..46d57ba9 100644 --- a/src/messaging/pop3/POP3Message.cpp +++ b/src/messaging/pop3/POP3Message.cpp @@ -110,8 +110,8 @@ const header& POP3Message::getHeader() const } -void POP3Message::extract(utility::outputStream& os, progressionListener* progress, - const int start, const int length) const +void POP3Message::extract(utility::outputStream& os, + utility::progressionListener* progress, const int start, const int length) const { if (!m_folder) throw exceptions::illegal_state("Folder closed"); @@ -146,7 +146,8 @@ void POP3Message::extract(utility::outputStream& os, progressionListener* progre void POP3Message::extractPart - (const part& /* p */, utility::outputStream& /* os */, progressionListener* /* progress */, + (const part& /* p */, utility::outputStream& /* os */, + utility::progressionListener* /* progress */, const int /* start */, const int /* length */) const { throw exceptions::operation_not_supported(); diff --git a/src/messaging/pop3/POP3Store.cpp b/src/messaging/pop3/POP3Store.cpp index d6ece2a0..69e09bc6 100644 --- a/src/messaging/pop3/POP3Store.cpp +++ b/src/messaging/pop3/POP3Store.cpp @@ -311,7 +311,7 @@ void POP3Store::sendRequest(const string& buffer, const bool end) void POP3Store::readResponse(string& buffer, const bool multiLine, - progressionListener* progress) + utility::progressionListener* progress) { bool foundTerminator = false; int current = 0, total = 0; @@ -406,8 +406,8 @@ void POP3Store::readResponse(string& buffer, const bool multiLine, } -void POP3Store::readResponse(utility::outputStream& os, progressionListener* progress, - const int predictedSize) +void POP3Store::readResponse(utility::outputStream& os, + utility::progressionListener* progress, const int predictedSize) { bool foundTerminator = false; int current = 0, total = predictedSize; diff --git a/src/messaging/sendmail/sendmailTransport.cpp b/src/messaging/sendmail/sendmailTransport.cpp new file mode 100644 index 00000000..87d1637f --- /dev/null +++ b/src/messaging/sendmail/sendmailTransport.cpp @@ -0,0 +1,387 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "vmime/messaging/sendmail/sendmailTransport.hpp" + +#include "vmime/exception.hpp" +#include "vmime/platformDependant.hpp" +#include "vmime/message.hpp" +#include "vmime/mailboxList.hpp" + +#include "vmime/messaging/authHelper.hpp" + +#include "vmime/utility/filteredStream.hpp" + + +#if VMIME_BUILTIN_PLATFORM_POSIX + + +#include +#include +#include +#include +#include +#include + + +namespace vmime { +namespace messaging { +namespace sendmail { + + +sendmailTransport::sendmailTransport(session* sess, authenticator* auth) + : transport(sess, getInfosInstance(), auth), m_connected(false) +{ +} + + +sendmailTransport::~sendmailTransport() +{ + if (isConnected()) + disconnect(); +} + + +const string sendmailTransport::getProtocolName() const +{ + return "sendmail"; +} + + +void sendmailTransport::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + // Use the specified path for 'sendmail' or a default one if no path is specified + m_sendmailPath = getSession()->getProperties().getProperty + (sm_infos.getPropertyPrefix() + "binpath", string(VMIME_SENDMAIL_PATH)); + + m_connected = true; +} + + +const bool sendmailTransport::isConnected() const +{ + return (m_connected); +} + + +void sendmailTransport::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + internalDisconnect(); +} + + +void sendmailTransport::internalDisconnect() +{ + m_connected = false; +} + + +void sendmailTransport::noop() +{ + // Do nothing +} + + +void sendmailTransport::send + (const mailbox& expeditor, const mailboxList& recipients, + utility::inputStream& is, const utility::stream::size_type size, + utility::progressionListener* progress) +{ + // If no recipient/expeditor was found, throw an exception + if (recipients.isEmpty()) + throw exceptions::no_recipient(); + else if (expeditor.isEmpty()) + throw exceptions::no_expeditor(); + + // Construct the argument list + std::vector args; + + args.push_back("-i"); + args.push_back("-f"); + args.push_back(expeditor.getEmail()); + args.push_back("--"); + + for (int i = 0 ; i < recipients.getMailboxCount() ; ++i) + args.push_back(recipients.getMailboxAt(i)->getEmail()); + + // Call sendmail + try + { + internalSend(args, is, size, progress); + } + catch (vmime::exception& e) + { + throw exceptions::command_error("SEND", "", "sendmail failed", e); + } +} + + +static const string getSignalMessage(const int num) +{ + switch (num) + { + case SIGHUP: return "SIGHUP"; + case SIGINT: return "SIGINT"; + case SIGQUIT: return "SIGQUIT"; + case SIGILL: return "SIGILL"; + case SIGABRT: return "SIGABRT"; + case SIGFPE: return "SIGFPE"; + case SIGKILL: return "SIGKILL"; + case SIGSEGV: return "SIGSEGV"; + case SIGPIPE: return "SIGPIPE"; + case SIGALRM: return "SIGALRM"; + case SIGTERM: return "SIGTERM"; + case SIGUSR1: return "SIGUSR1"; + case SIGUSR2: return "SIGUSR2"; + case SIGCHLD: return "SIGCHLD"; + case SIGCONT: return "SIGCONT"; + case SIGSTOP: return "SIGSTOP"; + case SIGTSTP: return "SIGTSTP"; + case SIGTTIN: return "SIGTTIN"; + case SIGTTOU: return "SIGTTOU"; + } + + return "(unknown)"; +} + + +static const string getErrorMessage(const int num) +{ +#ifdef strerror_r + char res[256]; + res[0] = '\0'; + + strerror_r(num, res, sizeof(res)); + + return string(res); +#else + return string(strerror(num)); +#endif +} + + + +#ifndef VMIME_BUILDING_DOC + +// Output stream adapter for UNIX pipe + +class outputStreamPipeAdapter : public utility::outputStream +{ +public: + + outputStreamPipeAdapter(const int desc) + : m_desc(desc) + { + } + + void write(const value_type* const data, const size_type count) + { + if (::write(m_desc, data, count) == -1) + { + const string errorMsg = getErrorMessage(errno); + throw exceptions::system_error(errorMsg); + } + } + +private: + + int m_desc; +}; + +#endif // VMIME_BUILDING_DOC + + + +// The following code is highly inspired and adapted from the 'sendmail' +// provider module in Evolution data server code. +// +// Original authors: Dan Winship +// Copyright 2000 Ximian, Inc. (www.ximian.com) + +void sendmailTransport::internalSend + (const std::vector args, utility::inputStream& is, + const utility::stream::size_type size, utility::progressionListener* progress) +{ + // Construct C-style argument array + const char** argv = new const char*[args.size() + 2]; + + argv[0] = "sendmail"; + argv[args.size()] = NULL; + + for (unsigned int i = 0 ; i < args.size() ; ++i) + argv[i + 1] = args[i].c_str(); + + // Create a pipe to communicate with sendmail + int fd[2]; + + if (pipe(fd) == -1) + { + throw exceptions::system_error(getErrorMessage(errno)); + } + + // Block SIGCHLD so the calling application doesn't notice + // sendmail exiting before we do + sigset_t mask, oldMask; + + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &mask, &oldMask); + + // Spawn 'sendmail' process + pid_t pid = fork(); + + if (pid == -1) // error + { + const string errorMsg = getErrorMessage(errno); + + sigprocmask(SIG_SETMASK, &oldMask, NULL); + + close(fd[0]); + close(fd[1]); + + throw exceptions::system_error(errorMsg); + } + else if (pid == 0) // child process + { + dup2(fd[0], STDIN_FILENO); + close(fd[1]); + + execv(m_sendmailPath.c_str(), const_cast (argv)); + _exit(255); + } + + close(fd[0]); + + // Copy message data from input stream to output pipe + try + { + outputStreamPipeAdapter pos(fd[1]); + + // Workaround for lame sendmail implementations that + // can't handle CRLF eoln sequences: we transform CRLF + // sequences into LF characters. + utility::CRLFToLFFilteredOutputStream fos(pos); + + // TODO: remove 'Bcc:' field from message header + + utility::bufferedStreamCopy(is, fos, size, progress); + } + catch (exception& e) + { + close(fd[1]); + + int wstat; + + while (waitpid(pid, &wstat, 0) == -1 && errno == EINTR) + ; + + sigprocmask(SIG_SETMASK, &oldMask, NULL); + + throw; + } + + close(fd[1]); + + // Wait for sendmail to exit + int wstat; + + while (waitpid(pid, &wstat, 0) == -1 && errno == EINTR) + ; + + sigprocmask(SIG_SETMASK, &oldMask, NULL); + + if (!WIFEXITED(wstat)) + { + throw exceptions::system_error("sendmail exited with signal " + + getSignalMessage(WTERMSIG(wstat)) + ", mail not sent"); + } + else if (WEXITSTATUS(wstat) != 0) + { + if (WEXITSTATUS(wstat) == 255) + { + std::ostringstream oss; + oss << "Could not execute '" << m_sendmailPath; + oss << "', mail not sent"; + + throw exceptions::system_error(oss.str()); + } + else + { + std::ostringstream oss; + oss << "sendmail exited with status " << WEXITSTATUS(wstat); + oss << ", mail not sent"; + + throw exceptions::system_error(oss.str()); + } + } +} + + +// Service infos + +sendmailTransport::_infos sendmailTransport::sm_infos; + + +const serviceInfos& sendmailTransport::getInfosInstance() +{ + return (sm_infos); +} + + +const serviceInfos& sendmailTransport::getInfos() const +{ + return (sm_infos); +} + + +const port_t sendmailTransport::_infos::getDefaultPort() const +{ + return (0); +} + + +const string sendmailTransport::_infos::getPropertyPrefix() const +{ + return "transport.sendmail."; +} + + +const std::vector sendmailTransport::_infos::getAvailableProperties() const +{ + std::vector list; + + // Path to sendmail (override default) + list.push_back("binpath"); + + return (list); +} + + +} // sendmail +} // messaging +} // vmime + + +#endif // VMIME_BUILTIN_PLATFORM_POSIX diff --git a/src/messaging/smtp/SMTPTransport.cpp b/src/messaging/smtp/SMTPTransport.cpp index 16fc1454..4f7df174 100644 --- a/src/messaging/smtp/SMTPTransport.cpp +++ b/src/messaging/smtp/SMTPTransport.cpp @@ -26,6 +26,8 @@ #include "vmime/messaging/authHelper.hpp" +#include "vmime/utility/filteredStream.hpp" + namespace vmime { namespace messaging { @@ -259,7 +261,7 @@ void SMTPTransport::noop() void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, - progressionListener* progress) + utility::progressionListener* progress) { // If no recipient/expeditor was found, throw an exception if (recipients.isEmpty()) @@ -304,63 +306,13 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients throw exceptions::command_error("DATA", response); } - int current = 0, total = size; + // Stream copy with "\n." to "\n.." transformation + utility::outputStreamSocketAdapter sos(*m_socket); + utility::dotFilteredOutputStream fos(sos); - if (progress) - progress->start(total); - - char buffer[65536]; - char previousChar = '\0'; - - while (!is.eof()) - { - const int read = is.read(buffer, sizeof(buffer)); - - // Transform '.' into '..' at the beginning of a line - char* start = buffer; - char* end = buffer + read; - char* pos = buffer; - - while ((pos = std::find(pos, end, '.')) != end) - { - // '\n' in the previous buffer, '.' in the current one - if (pos == buffer && previousChar == '\n') - { - m_socket->sendRaw(".", 1); - } - // Both '\n' and '.' are in the current buffer - else if (pos > buffer && *(pos - 1) == '\n') - { - m_socket->sendRaw(start, pos - start); - m_socket->sendRaw(".", 1); - - start = pos; - } - - ++pos; - } - - if (read > 0) - previousChar = buffer[read - 1]; - else - previousChar = '\0'; - - // Send the remaining data - m_socket->sendRaw(start, end - start); - - current += read; - - // Notify progression - if (progress) - { - total = std::max(total, current); - progress->progress(current, total); - } - } - - if (progress) - progress->stop(total); + utility::bufferedStreamCopy(is, fos, size, progress); + // Send end-of-data delimiter m_socket->sendRaw("\r\n.\r\n", 5); readResponse(response); diff --git a/src/messaging/transport.cpp b/src/messaging/transport.cpp index 1caa7fae..0b52aa39 100644 --- a/src/messaging/transport.cpp +++ b/src/messaging/transport.cpp @@ -47,7 +47,7 @@ static void extractMailboxes } -void transport::send(vmime::message* msg, progressionListener* progress) +void transport::send(vmime::message* msg, utility::progressionListener* progress) { // Extract expeditor mailbox expeditor; diff --git a/src/utility/filteredStream.cpp b/src/utility/filteredStream.cpp new file mode 100644 index 00000000..9586f165 --- /dev/null +++ b/src/utility/filteredStream.cpp @@ -0,0 +1,115 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "vmime/utility/filteredStream.hpp" + + +namespace vmime { +namespace utility { + + +// dotFilteredOutputStream + +dotFilteredOutputStream::dotFilteredOutputStream(outputStream& os) + : m_stream(os) +{ +} + + +outputStream& dotFilteredOutputStream::getNextOutputStream() +{ + return (m_stream); +} + + +void dotFilteredOutputStream::write + (const value_type* const data, const size_type count) +{ + const value_type* pos = data; + const value_type* end = data + count; + const value_type* start = data; + + // Replace "\n." with "\n.." + while ((pos = std::find(pos, end, '.')) != end) + { + const value_type previousChar = + (pos == data ? m_previousChar : *(pos - 1)); + + if (previousChar == '\n') + { + m_stream.write(start, pos - data); + m_stream.write("..", 2); + + start = pos + 1; + } + + ++pos; + } + + m_stream.write(start, end - start); + m_previousChar = data[count - 1]; +} + + +// CRLFToLFFilteredOutputStream + +CRLFToLFFilteredOutputStream::CRLFToLFFilteredOutputStream(outputStream& os) + : m_stream(os) +{ +} + + +outputStream& CRLFToLFFilteredOutputStream::getNextOutputStream() +{ + return (m_stream); +} + + +void CRLFToLFFilteredOutputStream::write + (const value_type* const data, const size_type count) +{ + const value_type* pos = data; + const value_type* end = data + count; + const value_type* start = data; + + // Replace "\r\n" (CRLF) with "\n" (LF) + while ((pos = std::find(pos, end, '\n')) != end) + { + const value_type previousChar = + (pos == data ? m_previousChar : *(pos - 1)); + + if (previousChar == '\r') + { + m_stream.write(start, pos - 1 - data); // do not write \r + m_stream.write("\n", 1); + + start = pos + 1; + } + + ++pos; + } + + m_stream.write(start, end - start); + m_previousChar = data[count - 1]; +} + + +} // utility +} // vmime + diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp index 397bf20b..3899eb52 100644 --- a/src/utility/stream.cpp +++ b/src/utility/stream.cpp @@ -23,6 +23,10 @@ #include // for std::copy #include // for std::back_inserter +#if VMIME_HAVE_MESSAGING_FEATURES + #include "vmime/messaging/socket.hpp" +#endif + namespace vmime { namespace utility { @@ -45,10 +49,20 @@ outputStream& operator<<(outputStream& os, const string& str) const stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os) +{ + return bufferedStreamCopy(is, os, 0, NULL); +} + + +const stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os, + const stream::size_type length, progressionListener* progress) { stream::value_type buffer[65536]; stream::size_type total = 0; + if (progress != NULL) + progress->start(length); + while (!is.eof()) { const stream::size_type read = is.read(buffer, sizeof(buffer)); @@ -57,9 +71,15 @@ const stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os) { os.write(buffer, read); total += read; + + if (progress != NULL) + progress->progress(total, std::max(total, length)); } } + if (progress != NULL) + progress->stop(total); + return (total); } @@ -299,5 +319,28 @@ const stream::size_type inputStreamPointerAdapter::skip(const size_type count) } +// outputStreamSocketAdapter + +#ifdef VMIME_HAVE_MESSAGING_FEATURES + + +outputStreamSocketAdapter::outputStreamSocketAdapter(messaging::socket& sok) + : m_socket(sok) +{ +} + + +void outputStreamSocketAdapter::write + (const value_type* const data, const size_type count) +{ + m_socket.sendRaw(data, count); +} + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + + + } // utility } // vmime diff --git a/vmime/exception.hpp b/vmime/exception.hpp index 85f0a3ff..396ae5b3 100644 --- a/vmime/exception.hpp +++ b/vmime/exception.hpp @@ -311,6 +311,21 @@ public: }; +/** Underlying operating system error. + */ + +class system_error : public vmime::exception +{ +public: + + system_error(const string& what, const exception& other = NO_EXCEPTION); + ~system_error() throw(); + + exception* clone() const; + const string name() const; +}; + + #if VMIME_HAVE_MESSAGING_FEATURES diff --git a/vmime/messaging/folder.hpp b/vmime/messaging/folder.hpp index 9a8a1155..610e55af 100644 --- a/vmime/messaging/folder.hpp +++ b/vmime/messaging/folder.hpp @@ -25,11 +25,13 @@ #include "vmime/types.hpp" #include "vmime/dateTime.hpp" -#include "vmime/messaging/progressionListener.hpp" + #include "vmime/messaging/message.hpp" #include "vmime/messaging/events.hpp" + #include "vmime/utility/path.hpp" #include "vmime/utility/stream.hpp" +#include "vmime/utility/progressionListener.hpp" namespace vmime { @@ -244,7 +246,7 @@ public: * @param date date/time for the new message (if NULL, the current time is used) * @param progress progression listener, or NULL if not used */ - virtual void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL) = 0; + virtual void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL) = 0; /** Add a message to this folder. * @@ -254,7 +256,7 @@ public: * @param date date/time for the new message (if NULL, the current time is used) * @param progress progression listener, or NULL if not used */ - virtual void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL) = 0; + virtual void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL) = 0; /** Copy a message from this folder to another folder. * @@ -328,7 +330,7 @@ public: * @param options objects to fetch (combination of folder::FetchOptions flags) * @param progress progression listener, or NULL if not used */ - virtual void fetchMessages(std::vector & msg, const int options, progressionListener* progress = NULL) = 0; + virtual void fetchMessages(std::vector & msg, const int options, utility::progressionListener* progress = NULL) = 0; /** Fetch objects for the specified message. * diff --git a/vmime/messaging/imap/IMAPFolder.hpp b/vmime/messaging/imap/IMAPFolder.hpp index 4df20e44..128fea3c 100644 --- a/vmime/messaging/imap/IMAPFolder.hpp +++ b/vmime/messaging/imap/IMAPFolder.hpp @@ -91,8 +91,8 @@ public: void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET); void setMessageFlags(const std::vector & nums, const int flags, const int mode = message::FLAG_MODE_SET); - void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); - void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL); + void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL); void copyMessage(const folder::path& dest, const int num); void copyMessages(const folder::path& dest, const int from = 1, const int to = -1); @@ -108,7 +108,7 @@ public: store* getStore(); - void fetchMessages(std::vector & msg, const int options, progressionListener* progress = NULL); + void fetchMessages(std::vector & msg, const int options, utility::progressionListener* progress = NULL); void fetchMessage(message* msg, const int options); const int getFetchCapabilities() const; diff --git a/vmime/messaging/imap/IMAPMessage.hpp b/vmime/messaging/imap/IMAPMessage.hpp index b3c10562..f9c22a75 100644 --- a/vmime/messaging/imap/IMAPMessage.hpp +++ b/vmime/messaging/imap/IMAPMessage.hpp @@ -64,8 +64,8 @@ public: const int getFlags() const; void setFlags(const int flags, const int mode = FLAG_MODE_SET); - void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; - void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extract(utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extractPart(const part& p, utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1) const; void fetchPartHeader(part& p); @@ -75,7 +75,7 @@ private: void processFetchResponse(const int options, const IMAPParser::msg_att* msgAtt); - void extract(const part* p, utility::outputStream& os, progressionListener* progress, const int start, const int length, const bool headerOnly) const; + void extract(const part* p, utility::outputStream& os, utility::progressionListener* progress, const int start, const int length, const bool headerOnly) const; void convertAddressList(const IMAPParser::address_list& src, mailboxList& dest); diff --git a/vmime/messaging/imap/IMAPParser.hpp b/vmime/messaging/imap/IMAPParser.hpp index 4a4c8f59..368215b0 100644 --- a/vmime/messaging/imap/IMAPParser.hpp +++ b/vmime/messaging/imap/IMAPParser.hpp @@ -28,13 +28,13 @@ #include "vmime/utility/smartPtr.hpp" #include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/progressionListener.hpp" #include "vmime/encoderB64.hpp" #include "vmime/encoderQP.hpp" #include "vmime/platformDependant.hpp" -#include "vmime/messaging/progressionListener.hpp" #include "vmime/messaging/timeoutHandler.hpp" #include "vmime/messaging/socket.hpp" @@ -135,7 +135,7 @@ public: { protected: - target(class progressionListener* progress) : m_progress(progress) {} + target(utility::progressionListener* progress) : m_progress(progress) {} target(const target&) {} public: @@ -143,13 +143,13 @@ public: virtual ~target() { } - class progressionListener* progressionListener() { return (m_progress); } + utility::progressionListener* progressionListener() { return (m_progress); } virtual void putData(const string& chunk) = 0; private: - class progressionListener* m_progress; + utility::progressionListener* m_progress; }; @@ -158,7 +158,7 @@ public: { public: - targetString(class progressionListener* progress, vmime::string& str) + targetString(utility::progressionListener* progress, vmime::string& str) : target(progress), m_string(str) { } const vmime::string& string() const { return (m_string); } @@ -181,7 +181,7 @@ public: { public: - targetStream(class progressionListener* progress, utility::outputStream& stream) + targetStream(utility::progressionListener* progress, utility::outputStream& stream) : target(progress), m_stream(stream) { } const utility::outputStream& stream() const { return (m_stream); } @@ -809,7 +809,7 @@ public: m_value = "[literal-handler]"; const string::size_type length = text->value().length(); - progressionListener* progress = target->progressionListener(); + utility::progressionListener* progress = target->progressionListener(); if (progress) { @@ -4914,7 +4914,7 @@ private: IMAPTag* m_tag; socket* m_socket; - progressionListener* m_progress; + utility::progressionListener* m_progress; literalHandler* m_literalHandler; diff --git a/vmime/messaging/maildir/maildirFolder.hpp b/vmime/messaging/maildir/maildirFolder.hpp index 0b880f15..8eaaf333 100644 --- a/vmime/messaging/maildir/maildirFolder.hpp +++ b/vmime/messaging/maildir/maildirFolder.hpp @@ -91,8 +91,8 @@ public: void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET); void setMessageFlags(const std::vector & nums, const int flags, const int mode = message::FLAG_MODE_SET); - void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); - void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL); + void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL); void copyMessage(const folder::path& dest, const int num); void copyMessages(const folder::path& dest, const int from = 1, const int to = -1); @@ -108,7 +108,7 @@ public: store* getStore(); - void fetchMessages(std::vector & msg, const int options, progressionListener* progress = NULL); + void fetchMessages(std::vector & msg, const int options, utility::progressionListener* progress = NULL); void fetchMessage(message* msg, const int options); const int getFetchCapabilities() const; @@ -132,7 +132,7 @@ private: void setMessageFlagsImpl(const std::vector & nums, const int flags, const int mode); void copyMessagesImpl(const folder::path& dest, const std::vector & nums); - void copyMessageImpl(const utility::file::path& tmpDirPath, const utility::file::path& curDirPath, const utility::file::path::component& filename, utility::inputStream& is, const utility::stream::size_type size, progressionListener* progress); + void copyMessageImpl(const utility::file::path& tmpDirPath, const utility::file::path& curDirPath, const utility::file::path::component& filename, utility::inputStream& is, const utility::stream::size_type size, utility::progressionListener* progress); void notifyMessagesCopied(const folder::path& dest); diff --git a/vmime/messaging/maildir/maildirMessage.hpp b/vmime/messaging/maildir/maildirMessage.hpp index ffbc1ca2..cd345784 100644 --- a/vmime/messaging/maildir/maildirMessage.hpp +++ b/vmime/messaging/maildir/maildirMessage.hpp @@ -65,8 +65,8 @@ public: const int getFlags() const; void setFlags(const int flags, const int mode = FLAG_MODE_SET); - void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; - void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extract(utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extractPart(const part& p, utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1) const; void fetchPartHeader(part& p); @@ -78,7 +78,7 @@ private: header& getOrCreateHeader(); - void extractImpl(utility::outputStream& os, progressionListener* progress, const int start, const int length, const int partialStart, const int partialLength) const; + void extractImpl(utility::outputStream& os, utility::progressionListener* progress, const int start, const int length, const int partialStart, const int partialLength) const; maildirFolder* m_folder; diff --git a/vmime/messaging/message.hpp b/vmime/messaging/message.hpp index ee32c753..47cc6b76 100644 --- a/vmime/messaging/message.hpp +++ b/vmime/messaging/message.hpp @@ -22,7 +22,8 @@ #include "vmime/header.hpp" -#include "vmime/messaging/progressionListener.hpp" + +#include "vmime/utility/progressionListener.hpp" #include "vmime/utility/stream.hpp" @@ -257,7 +258,7 @@ public: * @param length number of bytes to retrieve (used for partial fetch) */ - virtual void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const = 0; + virtual void extract(utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1) const = 0; /** Extract the specified (MIME) part of the message (header + contents). * @@ -269,7 +270,7 @@ public: * @param start index of the first byte to retrieve (used for partial fetch) * @param length number of bytes to retrieve (used for partial fetch) */ - virtual void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const = 0; + virtual void extractPart(const part& p, utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1) const = 0; /** Fetch the MIME header for the specified part. * diff --git a/vmime/messaging/pop3/POP3Folder.hpp b/vmime/messaging/pop3/POP3Folder.hpp index 3cf9855d..8cfe12db 100644 --- a/vmime/messaging/pop3/POP3Folder.hpp +++ b/vmime/messaging/pop3/POP3Folder.hpp @@ -90,8 +90,8 @@ public: void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET); void setMessageFlags(const std::vector & nums, const int flags, const int mode = message::FLAG_MODE_SET); - void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); - void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL); + void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, utility::progressionListener* progress = NULL); void copyMessage(const folder::path& dest, const int num); void copyMessages(const folder::path& dest, const int from = 1, const int to = -1); @@ -107,7 +107,7 @@ public: store* getStore(); - void fetchMessages(std::vector & msg, const int options, progressionListener* progress = NULL); + void fetchMessages(std::vector & msg, const int options, utility::progressionListener* progress = NULL); void fetchMessage(message* msg, const int options); const int getFetchCapabilities() const; diff --git a/vmime/messaging/pop3/POP3Message.hpp b/vmime/messaging/pop3/POP3Message.hpp index 82b5b132..c19d7579 100644 --- a/vmime/messaging/pop3/POP3Message.hpp +++ b/vmime/messaging/pop3/POP3Message.hpp @@ -64,8 +64,8 @@ public: const int getFlags() const; void setFlags(const int flags, const int mode = FLAG_MODE_SET); - void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; - void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extract(utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extractPart(const part& p, utility::outputStream& os, utility::progressionListener* progress = NULL, const int start = 0, const int length = -1) const; void fetchPartHeader(part& p); diff --git a/vmime/messaging/pop3/POP3Store.hpp b/vmime/messaging/pop3/POP3Store.hpp index 598025ca..c7528f9c 100644 --- a/vmime/messaging/pop3/POP3Store.hpp +++ b/vmime/messaging/pop3/POP3Store.hpp @@ -74,8 +74,8 @@ private: static void stripResponseCode(const string& buffer, string& result); void sendRequest(const string& buffer, const bool end = true); - void readResponse(string& buffer, const bool multiLine, progressionListener* progress = NULL); - void readResponse(utility::outputStream& os, progressionListener* progress = NULL, const int predictedSize = 0); + void readResponse(string& buffer, const bool multiLine, utility::progressionListener* progress = NULL); + void readResponse(utility::outputStream& os, utility::progressionListener* progress = NULL, const int predictedSize = 0); static const bool checkTerminator(string& buffer, const bool multiLine); static const bool checkOneTerminator(string& buffer, const string& term); diff --git a/vmime/messaging/sendmail/sendmailTransport.hpp b/vmime/messaging/sendmail/sendmailTransport.hpp new file mode 100644 index 00000000..e3ee78c5 --- /dev/null +++ b/vmime/messaging/sendmail/sendmailTransport.hpp @@ -0,0 +1,98 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SENDMAIL_SENDMAILTRANSPORT_HPP_INCLUDED +#define VMIME_MESSAGING_SENDMAIL_SENDMAILTRANSPORT_HPP_INCLUDED + + +#include "vmime/config.hpp" + +#include "vmime/messaging/transport.hpp" +#include "vmime/messaging/socket.hpp" +#include "vmime/messaging/timeoutHandler.hpp" + + +#if VMIME_BUILTIN_PLATFORM_POSIX + + +namespace vmime { +namespace messaging { +namespace sendmail { + + +/** Sendmail local transport service. + */ + +class sendmailTransport : public transport +{ +public: + + sendmailTransport(session* sess, authenticator* auth); + ~sendmailTransport(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + + void connect(); + const bool isConnected() const; + void disconnect(); + + void noop(); + + void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, utility::progressionListener* progress = NULL); + +private: + + void internalDisconnect(); + + void internalSend(const std::vector args, utility::inputStream& is, + const utility::stream::size_type size, utility::progressionListener* progress); + + + string m_sendmailPath; + + bool m_connected; + + + // Service infos + class _infos : public serviceInfos + { + public: + + const port_t getDefaultPort() const; + + const string getPropertyPrefix() const; + const std::vector getAvailableProperties() const; + }; + + static _infos sm_infos; +}; + + +} // sendmail +} // messaging +} // vmime + + +#endif // VMIME_BUILTIN_PLATFORM_POSIX + + +#endif // VMIME_MESSAGING_SENDMAIL_SENDMAILTRANSPORT_HPP_INCLUDED diff --git a/vmime/messaging/service.hpp b/vmime/messaging/service.hpp index 212e2c55..cf12e213 100644 --- a/vmime/messaging/service.hpp +++ b/vmime/messaging/service.hpp @@ -24,13 +24,13 @@ #include "vmime/types.hpp" #include "vmime/messaging/session.hpp" - #include "vmime/messaging/authenticator.hpp" -#include "vmime/messaging/progressionListener.hpp" #include "vmime/messaging/serviceFactory.hpp" #include "vmime/messaging/serviceInfos.hpp" +#include "vmime/utility/progressionListener.hpp" + namespace vmime { namespace messaging { diff --git a/vmime/messaging/serviceFactory.hpp b/vmime/messaging/serviceFactory.hpp index 5c17afcc..782e4c75 100644 --- a/vmime/messaging/serviceFactory.hpp +++ b/vmime/messaging/serviceFactory.hpp @@ -31,9 +31,10 @@ #include "vmime/messaging/serviceInfos.hpp" #include "vmime/messaging/authenticator.hpp" -#include "vmime/messaging/progressionListener.hpp" #include "vmime/messaging/timeoutHandler.hpp" +#include "vmime/utility/progressionListener.hpp" + namespace vmime { namespace messaging { diff --git a/vmime/messaging/session.hpp b/vmime/messaging/session.hpp index 958ee448..bce4a2c0 100644 --- a/vmime/messaging/session.hpp +++ b/vmime/messaging/session.hpp @@ -22,7 +22,7 @@ #include "vmime/messaging/authenticator.hpp" -#include "vmime/messaging/progressionListener.hpp" + #include "vmime/utility/url.hpp" #include "vmime/propertySet.hpp" diff --git a/vmime/messaging/smtp/SMTPTransport.hpp b/vmime/messaging/smtp/SMTPTransport.hpp index 9b2eaa9b..3ccfdd49 100644 --- a/vmime/messaging/smtp/SMTPTransport.hpp +++ b/vmime/messaging/smtp/SMTPTransport.hpp @@ -54,7 +54,7 @@ public: void noop(); - void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, progressionListener* progress = NULL); + void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, utility::progressionListener* progress = NULL); private: diff --git a/vmime/messaging/transport.hpp b/vmime/messaging/transport.hpp index 8ffe8ad2..272fdb32 100644 --- a/vmime/messaging/transport.hpp +++ b/vmime/messaging/transport.hpp @@ -51,7 +51,7 @@ public: * @param msg message to send * @param progress progression listener, or NULL if not used */ - virtual void send(vmime::message* msg, progressionListener* progress = NULL); + virtual void send(vmime::message* msg, utility::progressionListener* progress = NULL); /** Send a message over this transport service. * @@ -61,7 +61,7 @@ public: * @param size size of the message data * @param progress progression listener, or NULL if not used */ - virtual void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, progressionListener* progress = NULL) = 0; + virtual void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, utility::progressionListener* progress = NULL) = 0; const Type getType() const; diff --git a/vmime/utility/filteredStream.hpp b/vmime/utility/filteredStream.hpp new file mode 100644 index 00000000..fc014e4e --- /dev/null +++ b/vmime/utility/filteredStream.hpp @@ -0,0 +1,116 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_FILTEREDSTREAM_HPP_INCLUDED +#define VMIME_UTILITY_FILTEREDSTREAM_HPP_INCLUDED + + +#include "vmime/utility/stream.hpp" + + +namespace vmime { +namespace utility { + + +/** A stream whose input is filtered. + */ + +class filteredInputStream : public inputStream +{ +public: + + /** Return a reference to the stream being filtered. + * + * @return stream being filtered + */ + virtual inputStream& getPreviousInputStream() = 0; +}; + + +/** A stream whose output is filtered. + */ + +class filteredOutputStream : public outputStream +{ +public: + + /** Return a reference to the stream being filtered. + * + * @return destination stream for filtered data + */ + virtual outputStream& getNextOutputStream() = 0; +}; + + +/** A filtered output stream which replaces "\n." + * sequences with "\n.." sequences. + */ + +class dotFilteredOutputStream : public filteredOutputStream +{ +public: + + /** Construct a new filter for the specified output stream. + * + * @param os stream into which write filtered data + */ + dotFilteredOutputStream(outputStream& os); + + outputStream& getNextOutputStream(); + + void write(const value_type* const data, const size_type count); + +private: + + outputStream& m_stream; + value_type m_previousChar; +}; + + +/** A filtered output stream which replaces CRLF sequences + * with single LF characters. + */ + +class CRLFToLFFilteredOutputStream : public filteredOutputStream +{ +public: + + /** Construct a new filter for the specified output stream. + * + * @param os stream into which write filtered data + */ + CRLFToLFFilteredOutputStream(outputStream& os); + + outputStream& getNextOutputStream(); + + void write(const value_type* const data, const size_type count); + +private: + + outputStream& m_stream; + value_type m_previousChar; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_FILTEREDSTREAM_HPP_INCLUDED + diff --git a/vmime/messaging/progressionListener.hpp b/vmime/utility/progressionListener.hpp similarity index 81% rename from vmime/messaging/progressionListener.hpp rename to vmime/utility/progressionListener.hpp index a1008d16..24c2f35c 100644 --- a/vmime/messaging/progressionListener.hpp +++ b/vmime/utility/progressionListener.hpp @@ -17,12 +17,12 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // -#ifndef VMIME_MESSAGING_PROGRESSIONLISTENER_HPP_INCLUDED -#define VMIME_MESSAGING_PROGRESSIONLISTENER_HPP_INCLUDED +#ifndef VMIME_UTILITY_PROGRESSIONLISTENER_HPP_INCLUDED +#define VMIME_UTILITY_PROGRESSIONLISTENER_HPP_INCLUDED namespace vmime { -namespace messaging { +namespace utility { /** An interface to implement if you want to be notified @@ -39,8 +39,8 @@ public: /** Allow the caller object to cancel the current operation. * - * @warning WARNING: this is implementation-dependant: the underlying - * messaging protocol may not support this). + * @warning WARNING: this is implementation-dependant: cancelling + * may not be supported by the notifier object. * * @return true to cancel the operation, false otherwise */ @@ -49,7 +49,7 @@ public: /** Called at the beginning of the operation. * * @param predictedTotal predicted amount of units (this has - * no concrete meaning: they are not bytes, nor percentage...) + * no concrete meaning: these are not bytes, nor percentage...) */ virtual void start(const int predictedTotal) = 0; @@ -68,8 +68,8 @@ public: }; -} // messaging +} // utility } // vmime -#endif // VMIME_MESSAGING_PROGRESSIONLISTENER_HPP_INCLUDED +#endif // VMIME_UTILITY_PROGRESSIONLISTENER_HPP_INCLUDED diff --git a/vmime/utility/stream.hpp b/vmime/utility/stream.hpp index 91f3f573..c9967496 100644 --- a/vmime/utility/stream.hpp +++ b/vmime/utility/stream.hpp @@ -25,8 +25,20 @@ #include #include +#include "vmime/config.hpp" #include "vmime/types.hpp" +#include "vmime/utility/progressionListener.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + namespace vmime { + namespace messaging { + class socket; // forward reference + } // messaging + } // vmime +#endif + #if defined(_MSC_VER) && (_MSC_VER <= 1200) // VC++6 # include #endif @@ -160,6 +172,18 @@ outputStream& operator<<(outputStream& os, const T& t) const stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os); +/** Copy data from one stream into another stream using a buffered method + * and notify progression of the operation. + * + * @param is input stream (source data) + * @param os output stream (destination for data) + * @param length predicted number of bytes to copy + * @param progress listener to notify + * @return number of bytes copied + */ + +const stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os, + const stream::size_type length, progressionListener* progress); // Adapters @@ -296,6 +320,29 @@ private: }; +#if VMIME_HAVE_MESSAGING_FEATURES + + +/** An output stream that is connected to a socket. + */ + +class outputStreamSocketAdapter : public outputStream +{ +public: + + outputStreamSocketAdapter(messaging::socket& sok); + + void write(const value_type* const data, const size_type count); + +private: + + messaging::socket& m_socket; +}; + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + } // utility } // vmime diff --git a/vmime/vmime.hpp b/vmime/vmime.hpp index c0e52d3b..b5cac466 100644 --- a/vmime/vmime.hpp +++ b/vmime/vmime.hpp @@ -84,6 +84,7 @@ // Utilities #include "vmime/utility/datetimeUtils.hpp" +#include "vmime/utility/filteredStream.hpp" // Messaging features #if VMIME_HAVE_MESSAGING_FEATURES