OKlibrary  0.2.1.6
emailHandling.hpp
Go to the documentation of this file.
00001 // Oliver Kullmann, 28.9.2002 (Swansea)
00002 /* Copyright 2002 - 2007 Oliver Kullmann
00003 This file is part of the OKlibrary. OKlibrary is free software; you can redistribute
00004 it and/or modify it under the terms of the GNU General Public License as published by
00005 the Free Software Foundation and included in this library; either version 3 of the
00006 License, or any later version. */
00007 
00008 #ifndef EMAILHANDLINGWAECHTER
00009 
00010 #define EMAILHANDLINGWAECHTER
00011 
00012 #include <cstdlib> // for system
00013 #include <cstdio> // for remove
00014 
00015 #include <sys/types.h>
00016 #include <sys/stat.h> // for stat, chmod
00017 #include <dirent.h> // for directories
00018 
00019 #include <fstream>
00020 #include <sstream>
00021 #include <iterator>
00022 #include <algorithm>
00023 
00024 #include <OKlib/General/TimeHandling.hpp>
00025 #include <OKlib/General/StreamHandling.hpp>
00026 
00027 
00028 namespace emailHandling {
00029 
00030   const std::string self = "emailHandling";
00031 
00032   const std::string email_self = "O.Kullmann@Swansea.ac.uk";
00033   const std::string mail_agent = "mutt";
00034 
00035   // -------------------------------------------------------------
00036 
00037   struct sendmail {
00038 
00039     struct Error {};
00040     struct open_file : Error {};
00041     struct write_file : Error {};
00042     struct get_file_stat : Error {};
00043     struct set_file_stat : Error {};
00044 
00045     sendmail() {}
00046     // does not throw
00047 
00048     sendmail(const std::string& message, const std::string& to, const std::string& subject = "", const std::string& attachment = "", const std::string& bcc = email_self) :
00049       m(message), t(to), s(subject), a(attachment), b(bcc) {}
00050     // does not throw
00051     // attachment is a space-separated list of file names
00052     // bcc is a space-separated list of e-mail-addresses
00053 
00054     int send_direct() const {
00055 
00056       std::string options;
00057       if (! s.empty())
00058   options += " -s \"" + s + "\"";
00059       if (! a.empty()) {
00060   std::istringstream L(a);
00061   std::string attach;
00062   while (L >> attach)
00063     options += " -a " + attach;
00064       }
00065       if (! b.empty()) {
00066   std::istringstream L(b);
00067   std::string copy;
00068   while (L >> copy)
00069     options += " -b " + copy;
00070       }
00071  
00072       const std::string command = "echo \"" + m + "\" | " + mail_agent + options + " " + t;
00073       return std::system(command.c_str());
00074     }
00075     // does not throw
00076 
00077     enum user_type { user_is_other, user_is_self };
00078     void send_indirect(const std::string& filename, const user_type ut = user_is_other) const {
00079       {
00080   std::ofstream file(filename.c_str(), std::ios_base::out);
00081   if (! file)
00082     throw open_file();
00083   file << mail_agent << "\n";
00084   file << t << "\n";
00085   file << s << "\n";
00086   file << a << "\n";
00087   file << b << "\n";
00088   file << m << std::endl;
00089   if (! file)
00090     throw write_file();
00091       }
00092       // user minus write, minus read; group minus read
00093       {
00094   struct stat S;
00095   if (stat(filename.c_str(), &S))
00096     throw get_file_stat();
00097   if (ut == user_is_other) {
00098     if (chmod(filename.c_str(), S.st_mode & ~ S_IWUSR & ~ S_IRUSR & ~ S_IRGRP))
00099       throw set_file_stat();
00100   }
00101   else
00102     if (chmod(filename.c_str(), S.st_mode & ~ S_IWUSR & ~ S_IRGRP & ~ S_IROTH))
00103       throw set_file_stat();
00104       }
00105     }
00106     // throws Error; 
00107 
00108     std::string m;
00109     std::string t;
00110     std::string s;
00111     std::string a;
00112     std::string b;
00113   };
00114 
00115   // -------------------------------------------------------------
00116 
00117   class SendWaitingEmails {
00118   public :
00119     struct Error {};
00120     struct open_log_file : Error {};
00121     struct write_log_file : Error {};
00122     struct open_dir : Error {};
00123     struct get_file_stat : Error {};
00124     struct open_file : Error {};
00125     struct read_file : Error {};
00126     struct delete_file : Error {};
00127     struct send_mail : Error {};
00128 
00129     SendWaitingEmails(const std::string& logfile, const std::string& directory) :
00130       L(logfile), D(directory) {}
00131     // does not throw
00132 
00133     void send() const {
00134 
00135       std::ofstream log(L.c_str(), std::ios_base::out | std::ios_base::app);
00136       if (! log)
00137   throw open_log_file();
00138 
00139       log << "\n----------------------\n" << self << "\n" << TimeHandling::currentDateTime() << std::endl;
00140       if (! log)
00141   throw write_log_file();
00142 
00143       DIR* const dir = opendir(D.c_str());
00144       if (! dir) {
00145   log << "Error::open_dir" << std::endl;
00146   throw open_dir();
00147       }
00148       for (struct dirent* p = readdir(dir); p; p = readdir(dir)) {
00149   const char* const namep = p -> d_name;
00150   if (! std::strcmp(namep, ".") or ! std::strcmp(namep, ".."))
00151     continue;
00152   log << " file name: " << namep << "\n";
00153   const std::string fullName = D + "/" + namep;
00154   struct stat S;
00155   if (stat(fullName.c_str(), &S)) {
00156     log << "Error::get_file_stat" << std::endl;
00157     throw get_file_stat();
00158   }
00159   if (S.st_mode & S_IWUSR)
00160     continue;
00161 
00162   try {
00163     StreamHandling::FileLines file(fullName.c_str());
00164     StreamHandling::FileLines::iterator i = file.begin();
00165     if (*i != mail_agent)
00166       continue;
00167     else
00168       log << mail_agent << "\n";
00169     const std::string& to = *++i;
00170     log << "To: " << to << "\n";
00171     const std::string& sub = *++i;
00172     const std::string& a = *++i;
00173     const std::string& b = *++i;
00174     std::ostringstream message;
00175     std::copy(++i, file.end(), std::ostream_iterator<std::string>(message, "\n"));
00176     sendmail send(message.str(), to, sub, a, b);
00177     if (send.send_direct()) {
00178       log << "Error::send_mail" << std::endl;
00179       throw send_mail();
00180     }
00181   }
00182   catch (StreamHandling::Error_FileLines::open_file) {
00183     log << "Error::open_file" << std::endl;
00184     throw open_file();
00185   }
00186   catch (StreamHandling::Error_FileLines::read_file) {
00187     log << "Error::read_file" << std::endl;
00188     throw read_file();
00189   }
00190 
00191   if (std::remove(fullName.c_str())) {
00192     log << "Error::delete_file" << std::endl;
00193     throw delete_file();
00194   }
00195 
00196   if (! log)
00197     throw write_log_file();
00198       }
00199       
00200     }
00201     // throws Error
00202 
00203   private :
00204     std::string L;
00205     std::string D;
00206   };
00207 
00208   bool check_e_mail_address(const std::string& address) {
00209     return address.find('@') != std::string::npos;
00210   }
00211 
00212   // Sending a serial e-mail ------------------------------------------------
00213 
00214   class Serial_e_mail {
00215   public :
00216     struct Error {
00217       virtual ~Error() {}
00218     };
00219     struct invalid_address : Error {};
00220     struct missing_name : Error {};
00221     struct missing_text : Error {};
00222     struct empty_dirname : Error {};
00223     
00224     Serial_e_mail(const std::string& form) : formula(form) {}
00225 
00226     void read_recipients(std::istream& is) {
00227       // throws invalid_address and mising_name
00228       using namespace std;
00229       string address;
00230       string name;
00231       while (is >> address) {
00232   if (not check_e_mail_address(address))
00233     throw invalid_address();
00234   if (not (is >> name))
00235     throw missing_name();
00236   string name_rest;
00237   getline(is, name_rest, '\n');
00238   list_recipients.push_back(recipient(address, name + name_rest));
00239       }
00240     }
00241     void read_subject(const std::string& sub) {
00242       subject = sub;
00243     }
00244     void read_text(const std::string& t) {
00245       text = t;
00246     }
00247 
00248     void send_direct() const { send(direct); }
00249     void send_indirect(const std::string& d) const {
00250       if (d.empty())
00251   throw empty_dirname();
00252       dirname = d;
00253       send(indirect);
00254     }
00255 
00256   private :
00257     std::string formula;
00258     struct recipient {
00259       recipient(const std::string& a, const std::string& n) :
00260   address(a), name(n) {}
00261       std::string address;
00262       std::string name;
00263     };
00264     std::vector<recipient> list_recipients;
00265     std::string subject;
00266     std::string text;
00267     
00268     static const std::string prefix; // for send_indirect
00269     mutable std::string dirname; // for send_indirect
00270     enum options { direct, indirect };
00271 
00272     void send(options o) const {
00273       using namespace std;
00274       if (text.empty()) throw missing_text();
00275       for (vector<recipient>::const_iterator i = list_recipients.begin(); i != list_recipients.end(); ++i) {
00276   const string& message = formula + " " + i -> name + ",\n\n" + text;
00277   sendmail Send(message, i -> address, subject, "", "");
00278   switch (o) {
00279   case direct : Send.send_direct(); break;
00280   case indirect : Send.send_indirect(dirname + "/" + prefix + i -> address, sendmail::user_is_self); break;
00281   }
00282       }
00283     }
00284   };
00285   const std::string Serial_e_mail::prefix = "MassenVersendung_";
00286 
00287 }
00288 
00289 #endif