////////////////////////////////////////////////////////////////////// // C++Mail wrapper // Written by Jonathan A. Foster // Started April 1st, 2021 // // The idea is simple: A C++ class that can be used to send an email. // This will work by using popen() to open "mail" and allow writing // the email to it in standard stream fashion. This is being developed // for use with C++CMS to allow emailing error reports when there is a // crash (500 page). // // *WARN*: Requires C++11 to work. Something in the type compatibility // testing is misisng in previous releases. // // NOTE: I'm making this an "include" library since I don't want to // get into the CMake learning curve at this time. Its problematic // enough just getting this wedged in. // // NOTE: I found the basic popen() C++ wrapper on-line somewhere. I // don't remember where or who wrote it. It has been changed slightly. // // WARN: errors are essentially ignored. In other words the return of // pclose() isn't tested. Nor is the original popen() verified. // // TODO: error handling :-D ////////////////////////////////////////////////////////////////////// #ifndef JFP_EMAIL_WRAPPER_H #define JFP_EMAIL_WRAPPER_H #include #include #include #include namespace JFP { ////////////////////////////////////////////////////////////////////// // A function to escape strings for building shell commands ////////////////////////////////////////////////////////////////////// // transform a copy so we don't cause an unexpected alteration, hence // the "const". std::string esc(const std::string s) { std::string r = s; for( auto it=r.find("'"); it!=std::string::npos; it=r.find("'", it+4) ) r.replace(it, 1, "'\\''"); return '\''+r+'\''; } ////////////////////////////////////////////////////////////////////// // A "streambuf" descendant to write to a popen(..., "w") FILE ////////////////////////////////////////////////////////////////////// struct FILEbuf: public std::streambuf { enum { bufsize = 4*1024 }; char buffer[bufsize]; FILE* fp; int (*close)(FILE*); int overflow(int c) { if(c!=std::char_traits::eof()) { *this->pptr() = std::char_traits::to_char_type(c); this->pbump(1); } return this->sync() ? std::char_traits::eof() : std::char_traits::not_eof(c); } int sync() { std::streamsize size(this->pptr()-this->pbase()); std::streamsize done(this->fp ? fwrite(this->pbase(), 1, size, this->fp) : 0); this->setp(this->pbase(), this->epptr()); return size == done ? 0 : -1; } FILEbuf(FILE* _fp, int(*_close)(FILE*) = fclose): fp(_fp), close(_close) { this->setp(this->buffer, this->buffer+(this->fp ? bufsize-1 : 0)); } ~FILEbuf() { this->sync(); this->fp && this->close(this->fp); } }; ////////////////////////////////////////////////////////////////////// // Now attach it to an ostream object, which will also perform the // popen() call. ////////////////////////////////////////////////////////////////////// struct popeno: public std::ostream, private virtual FILEbuf { popeno(std::string const& cmd): FILEbuf(popen(cmd.c_str(), "w"), pclose), std::ios(static_cast(this)), std::ostream(static_cast(this)) { } popeno(popeno &&old): FILEbuf(fp, close), std::ios(static_cast(this)), std::ostream(static_cast(this)) { old.sync(); // flush buffer so we don't need to copy it. } }; ////////////////////////////////////////////////////////////////////// // And now for a simplistic mailer // // This wrapper is basically needed to do the string /math/ for the // popeno constructor since the command string has to be passed to // inherited constructors prior to our constructor code being run. // // TODO: refactor to provide a cleaner and email only interface. ////////////////////////////////////////////////////////////////////// popeno eMail(const std::string &to, const std::string &subject) { popeno r("mail -s "+esc(subject)+" "+esc(to)); return r; } ////////////////////////////////////////////////////////////////////// // EXAMPLE ////////////////////////////////////////////////////////////////////// /* int main(int argc, char **argv) { eMail("jon@jfpi.net", "Test from C++") << "Greetings earthling\n" << "\n" << "- ChipMaster\n"; }*/ } // JFP #endif