diff options
Diffstat (limited to 'butl')
-rw-r--r-- | butl/buildfile | 1 | ||||
-rw-r--r-- | butl/sendmail | 92 | ||||
-rw-r--r-- | butl/sendmail.cxx | 40 | ||||
-rw-r--r-- | butl/sendmail.ixx | 65 |
4 files changed, 198 insertions, 0 deletions
diff --git a/butl/buildfile b/butl/buildfile index 6cef7f2..7d33535 100644 --- a/butl/buildfile +++ b/butl/buildfile @@ -23,6 +23,7 @@ lib{butl}: \ {hxx ixx cxx}{ process } \ {hxx }{ process-details } \ { txx cxx}{ process-run } \ + {hxx ixx cxx}{ sendmail } \ {hxx cxx}{ sha256 } \ {hxx }{ small-vector } \ {hxx txx }{ string-table } \ diff --git a/butl/sendmail b/butl/sendmail new file mode 100644 index 0000000..5a380e7 --- /dev/null +++ b/butl/sendmail @@ -0,0 +1,92 @@ +// file : butl/sendmail -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUTL_SENDMAIL +#define BUTL_SENDMAIL + +#include <string> + +#include <butl/export> + +#include <butl/process> +#include <butl/fdstream> +#include <butl/small-vector> + +namespace butl +{ + // Send email using the sendmail program. + // + // Write the body of the email to out. Note that you must explicitly close + // it before calling wait(). Throw process_error and io_error (both derive + // from system_error) in case of errors. + // + // Typical usage: + // + // try + // { + // sendmail sm (2, // Diagnostics to stderr. + // "", // Default From: address. + // "Test subject", + // {"test@example.com"}); + // + // sm.out << "Test body" << endl; + // + // sm.close (); + // + // if (!sm.wait ()) + // ... // sendmail returned non-zero status. + // } + // catch (const std::system_error& e) + // { + // cerr << "sendmail error: " << e << endl; + // } + // + class LIBBUTL_EXPORT sendmail: public process + { + public: + ofdstream out; + + // Notes: + // + // - If from is empty then the process user's address is used. + // + // - The to/cc/bcc addressed should already be quoted if required. + // + using recipients_type = small_vector<std::string, 1>; + + template <typename E, typename... O> + sendmail (E&& err, + const std::string& from, + const std::string& subject, + const recipients_type& to, + const recipients_type& cc = recipients_type (), + const recipients_type& bcc = recipients_type (), + O&&... options); + + // Version with the command line callback (see process_run() for + // details). + // + template <typename C, typename E, typename... O> + sendmail (const C&, + E&& err, + const std::string& from, + const std::string& subject, + const recipients_type& to, + const recipients_type& cc = recipients_type (), + const recipients_type& bcc = recipients_type (), + O&&... options); + + private: + void + headers (const std::string& from, + const std::string& subj, + const recipients_type& to, + const recipients_type& cc, + const recipients_type& bcc); + }; +} + +#include <butl/sendmail.ixx> + +#endif // BUTL_SENDMAIL diff --git a/butl/sendmail.cxx b/butl/sendmail.cxx new file mode 100644 index 0000000..103bab5 --- /dev/null +++ b/butl/sendmail.cxx @@ -0,0 +1,40 @@ +// file : butl/sendmail.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <butl/sendmail> + +using namespace std; + +namespace butl +{ + void sendmail:: + headers (const std::string& from, + const std::string& subj, + const recipients_type& to, + const recipients_type& cc, + const recipients_type& bcc) + { + if (!from.empty ()) + out << "From: " << from << endl; + + auto rcp =[this] (const char* h, const recipients_type& rs) + { + if (!rs.empty ()) + { + bool f (true); + out << h << ": "; + for (const string& r: rs) + out << (f ? (f = false, "") : ", ") << r; + out << endl; + } + }; + + rcp ("To", to); + rcp ("Cc", cc); + rcp ("Bcc", bcc); + + out << "Subject: " << subj << endl + << endl; // Header/body separator. + } +} diff --git a/butl/sendmail.ixx b/butl/sendmail.ixx new file mode 100644 index 0000000..3f6597d --- /dev/null +++ b/butl/sendmail.ixx @@ -0,0 +1,65 @@ +// file : butl/sendmail.ixx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <utility> // move(), forward() + +namespace butl +{ + template <typename E, typename... O> + inline sendmail:: + sendmail (E&& err, + const std::string& from, + const std::string& subj, + const recipients_type& to, + const recipients_type& cc, + const recipients_type& bcc, + O&&... options) + : sendmail ([] (const char* [], std::size_t) {}, + std::forward<E> (err), + from, + subj, + to, + cc, + bcc, + std::forward<O> (options)...) + { + } + + template <typename C, typename E, typename... O> + inline sendmail:: + sendmail (const C& cmdc, + E&& err, + const std::string& from, + const std::string& subj, + const recipients_type& to, + const recipients_type& cc, + const recipients_type& bcc, + O&&... options) + { + { + fdpipe pipe (fdopen_pipe ()); // Text mode seems appropriate. + + process& p (*this); + p = process_start (cmdc, + pipe.in, + 2, // No output expected so redirect to stderr. + std::forward<E> (err), + dir_path (), + "sendmail", + "-i", // Don't treat '.' as the end of input. + "-t", // Read recipients from headers. + std::forward<O> (options)...); + + out.open (std::move (pipe.out)); + } // Close pipe.in. + + // Write headers. + // + // Note that if this throws, then the ofdstream will be closed first + // (which should signal to the process we are done). Then the process's + // destructor will wait for its termination ignoring any errors. + // + headers (from, subj, to, cc, bcc); + } +} |