ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

146 lines
4.4 KiB

  1. //////////////////////////////////////////////////////////////////////
  2. // C++Mail wrapper
  3. // Written by Jonathan A. Foster <jon@jfpossibiliteis.com>
  4. // Started April 1st, 2021
  5. //
  6. // The idea is simple: A C++ class that can be used to send an email.
  7. // This will work by using popen() to open "mail" and allow writing
  8. // the email to it in standard stream fashion. This is being developed
  9. // for use with C++CMS to allow emailing error reports when there is a
  10. // crash (500 page).
  11. //
  12. // *WARN*: Requires C++11 to work. Something in the type compatibility
  13. // testing is misisng in previous releases.
  14. //
  15. // NOTE: I'm making this an "include" library since I don't want to
  16. // get into the CMake learning curve at this time. Its problematic
  17. // enough just getting this wedged in.
  18. //
  19. // NOTE: I found the basic popen() C++ wrapper on-line somewhere. I
  20. // don't remember where or who wrote it. It has been changed slightly.
  21. //
  22. // WARN: errors are essentially ignored. In other words the return of
  23. // pclose() isn't tested. Nor is the original popen() verified.
  24. //
  25. // TODO: error handling :-D
  26. //////////////////////////////////////////////////////////////////////
  27. #ifndef JFP_EMAIL_WRAPPER_H
  28. #define JFP_EMAIL_WRAPPER_H
  29. #include <stdio.h>
  30. #include <streambuf>
  31. #include <ostream>
  32. #include <memory>
  33. namespace JFP {
  34. //////////////////////////////////////////////////////////////////////
  35. // A function to escape strings for building shell commands
  36. //////////////////////////////////////////////////////////////////////
  37. // transform a copy so we don't cause an unexpected alteration, hence
  38. // the "const".
  39. std::string esc(const std::string s) {
  40. std::string r = s;
  41. for(
  42. auto it=r.find("'");
  43. it!=std::string::npos;
  44. it=r.find("'", it+4)
  45. ) r.replace(it, 1, "'\\''");
  46. return '\''+r+'\'';
  47. }
  48. //////////////////////////////////////////////////////////////////////
  49. // A "streambuf" descendant to write to a popen(..., "w") FILE
  50. //////////////////////////////////////////////////////////////////////
  51. struct FILEbuf: public std::streambuf {
  52. enum { bufsize = 4*1024 };
  53. char buffer[bufsize];
  54. FILE* fp;
  55. int (*close)(FILE*);
  56. int overflow(int c) {
  57. if(c!=std::char_traits<char>::eof()) {
  58. *this->pptr() = std::char_traits<char>::to_char_type(c);
  59. this->pbump(1);
  60. }
  61. return this->sync()
  62. ? std::char_traits<char>::eof()
  63. : std::char_traits<char>::not_eof(c);
  64. }
  65. int sync() {
  66. std::streamsize size(this->pptr()-this->pbase());
  67. std::streamsize done(this->fp ? fwrite(this->pbase(), 1, size, this->fp) : 0);
  68. this->setp(this->pbase(), this->epptr());
  69. return size == done ? 0 : -1;
  70. }
  71. FILEbuf(FILE* _fp, int(*_close)(FILE*) = fclose): fp(_fp), close(_close) {
  72. this->setp(this->buffer, this->buffer+(this->fp ? bufsize-1 : 0));
  73. }
  74. ~FILEbuf() { this->sync(); this->fp && this->close(this->fp); }
  75. };
  76. //////////////////////////////////////////////////////////////////////
  77. // Now attach it to an ostream object, which will also perform the
  78. // popen() call.
  79. //////////////////////////////////////////////////////////////////////
  80. struct popeno: public std::ostream, private virtual FILEbuf {
  81. popeno(std::string const& cmd):
  82. FILEbuf(popen(cmd.c_str(), "w"), pclose),
  83. std::ios(static_cast<std::streambuf*>(this)),
  84. std::ostream(static_cast<std::streambuf*>(this))
  85. { }
  86. popeno(popeno &&old):
  87. FILEbuf(fp, close),
  88. std::ios(static_cast<std::streambuf*>(this)),
  89. std::ostream(static_cast<std::streambuf*>(this))
  90. {
  91. old.sync(); // flush buffer so we don't need to copy it.
  92. }
  93. };
  94. //////////////////////////////////////////////////////////////////////
  95. // And now for a simplistic mailer
  96. //
  97. // This wrapper is basically needed to do the string /math/ for the
  98. // popeno constructor since the command string has to be passed to
  99. // inherited constructors prior to our constructor code being run.
  100. //
  101. // TODO: refactor to provide a cleaner and email only interface.
  102. //////////////////////////////////////////////////////////////////////
  103. popeno eMail(const std::string &to, const std::string &subject) {
  104. popeno r("mail -s "+esc(subject)+" "+esc(to));
  105. return r;
  106. }
  107. //////////////////////////////////////////////////////////////////////
  108. // EXAMPLE
  109. //////////////////////////////////////////////////////////////////////
  110. /*
  111. int main(int argc, char **argv) {
  112. eMail("jon@jfpi.net", "Test from C++")
  113. << "Greetings earthling\n"
  114. << "\n"
  115. << "- ChipMaster\n";
  116. }*/
  117. } // JFP
  118. #endif