Diff for /parser3/src/classes/mail.C between versions 1.52 and 1.74

version 1.52, 2002/02/06 08:54:22 version 1.74, 2002/08/01 11:41:12
Line 1 Line 1
 /** @file  /** @file
         Parser: @b mail parser class.          Parser: @b mail parser class.
   
         Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexander Petrosyan <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
   
         $Id$  
 */  */
   
   static const char* IDENT_MAIL_C="$Date$";
   
 #include "pa_config_includes.h"  #include "pa_config_includes.h"
   
 #include "pa_common.h"  #include "pa_common.h"
Line 15 Line 15
 #include "pa_exec.h"  #include "pa_exec.h"
 #include "pa_charsets.h"  #include "pa_charsets.h"
 #include "pa_charset.h"  #include "pa_charset.h"
   #include "pa_uue.h"
   
 #ifdef _MSC_VER  #ifdef _MSC_VER
 #       include "smtp/smtp.h"  #       include "smtp/smtp.h"
Line 26 Line 27
   
 #define MAIL_NAME "MAIL"  #define MAIL_NAME "MAIL"
   
 // global variable  
   
 Methoded *mail_class;  
   
 // consts  // consts
   
 const int ATTACHMENT_WEIGHT=100;  const int ATTACHMENT_WEIGHT=100;
   
   // global variable
   
   Methoded *mail_base_class;
   
 // class  // class
   
 class MMail : public Methoded {  class MMail : public Methoded {
 public:  public:
         MMail(Pool& pool);          MMail(Pool& pool);
 public: // Methoded  public: // Methoded
         bool used_directly() { return true; }          bool used_directly() { return false; }
         void configure_user(Request& r);          void configure_user(Request& r);
 private:  private:
         String mail_name;          String mail_name;
         String content_disposition_name;  
         String content_disposition_filename_name;  
 };  };
   
 // helpers  
   
 // uuencode  
   
 static unsigned char uue_table[64] = {  
   '`', '!', '"', '#', '$', '%', '&', '\'',  
   '(', ')', '*', '+', ',', '-', '.', '/',  
   '0', '1', '2', '3', '4', '5', '6', '7',  
   '8', '9', ':', ';', '<', '=', '>', '?',  
   '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',  
   'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',  
   'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',  
   'X', 'Y', 'Z', '[', '\\',']', '^', '_'  
 };  
 static void uuencode(String& result, const char *file_name_cstr, const VFile& vfile) {  
         //header  
         result << "content-transfer-encoding: x-uuencode\n" << "\n";  
         result << "begin 644 " << file_name_cstr << "\n";  
   
         //body  
         const unsigned char *in=(const unsigned char *)vfile.value_ptr();  
         size_t in_length=vfile.value_size();  
   
         int count=45;  
         for(const unsigned char *itemp=in; itemp<(in+in_length); itemp+=count) {  
                 int index;        
   
                 if((itemp+count)>(in+in_length))   
                         count=in_length-(itemp-in);  
   
                 char *buf=(char *)result.pool().malloc(MAX_STRING);  
                 char *optr=buf;  
                   
                 /*  
                 * for UU and XX, encode the number of bytes as first character  
                 */  
                 *optr++ = uue_table[count];  
                   
                 for (index=0; index<=count-3; index+=3) {  
                         *optr++ = uue_table[itemp[index] >> 2];  
                         *optr++ = uue_table[((itemp[index  ] & 0x03) << 4) | (itemp[index+1] >> 4)];  
                         *optr++ = uue_table[((itemp[index+1] & 0x0f) << 2) | (itemp[index+2] >> 6)];  
                         *optr++ = uue_table[  itemp[index+2] & 0x3f];  
                 }  
                   
                 /*  
                 * Special handlitempg for itempcomplete litempes  
                 */  
                 if (index != count) {  
                         if (count - index == 2) {  
                                 *optr++ = uue_table[itemp[index] >> 2];  
                                 *optr++ = uue_table[((itemp[index  ] & 0x03) << 4) |   
                                         ( itemp[index+1] >> 4)];  
                                 *optr++ = uue_table[((itemp[index+1] & 0x0f) << 2)];  
                                 *optr++ = uue_table[0];  
                         }  
                         else if (count - index == 1) {  
                                 *optr++ = uue_table[ itemp[index] >> 2];  
                                 *optr++ = uue_table[(itemp[index] & 0x03) << 4];  
                                 *optr++ = uue_table[0];  
                                 *optr++ = uue_table[0];  
                         }  
                 }  
                 /*  
                 * end of line  
                 */  
                 *optr++ = '\n';   
                 *optr = 0;  
                 result << buf;  
         }  
           
         //footer  
         result.APPEND_AS_IS((const char *)uue_table, 1/* one char */, 0, 0) << "\n"  
                 "end\n";  
 }  
   
 /** ^mail:send[$attach[$type[uue|mime64] $value[DATA]]]   /** ^mail:send[$attach[$type[uue|mime64] $value[DATA]]] 
         @todo solve - bad with html attaches          @todo solve - bad with html attaches
 */  */
Line 137  static const String& attach_hash_to_stri Line 60  static const String& attach_hash_to_stri
         if(Value *value=static_cast<Value *>(attach_hash.get(*value_name)))          if(Value *value=static_cast<Value *>(attach_hash.get(*value_name)))
                 vfile=value->as_vfile(String::UL_AS_IS); // bad with html attaches. todo: solve                  vfile=value->as_vfile(String::UL_AS_IS); // bad with html attaches. todo: solve
         else          else
                 throw Exception(0, 0,                  throw Exception("parser.runtime",
                         &origin_string,                          &origin_string,
                         "has no $value");                          "has no $value");
   
Line 152  static const String& attach_hash_to_stri Line 75  static const String& attach_hash_to_stri
         String& result=*new(pool) String(pool);          String& result=*new(pool) String(pool);
   
         // content-type: application/octet-stream          // content-type: application/octet-stream
         result << "content-type: " << r.mime_type_of(file_name_cstr) << "\n";          result << "content-type: " << r.mime_type_of(file_name_cstr) 
                   << "; name=\"" << file_name_cstr << "\"\n";
         // content-disposition: attachment; filename="user_file_name"          // content-disposition: attachment; filename="user_file_name"
         result << "content-disposition: attachment; filename=\"" << file_name_cstr << "\"\n";          result << "content-disposition: attachment; filename=\"" << file_name_cstr << "\"\n";
   
         const String *type=vformat?&vformat->as_string():0;          const String *type=vformat?&vformat->as_string():0;
         if(!type/*default = uue*/ || *type=="uue") {          if(!type/*default = uue*/ || *type=="uue") {
                 uuencode(result, file_name_cstr, *vfile);                  pa_uuencode(result, file_name_cstr, *vfile);
         } else // for now          } else // for now
                 throw Exception(0, 0,                  throw Exception("parser.runtime",
                         type,                          type,
                         "unknown attachment encode format");                          "unknown attachment encode format");
                   
Line 170  static const String& attach_hash_to_stri Line 94  static const String& attach_hash_to_stri
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct Mail_info {  struct Mail_info {
         Charset *charset;          Charset *charset; const char *content_charset_name;
         String *header;          String *header;
         const String **from, **to;          const String **from, **to;
           const String *errors_to;
 };  };
 #endif  #endif
 static void add_header_attribute(const Hash::Key& aattribute, Hash::Val *ameaning,   
                                                                  void *info) {  
   
         Value& lmeaning=*static_cast<Value *>(ameaning);  const String& extractEmail(const String& string);
   static void add_header_attribute(const Hash::Key& raw_element_name, Hash::Val *aelement_value, 
                                                                    void *info) {
           Value& element_value=*static_cast<Value *>(aelement_value);
           const String& low_element_name=raw_element_name.change_case(raw_element_name.pool(), String::CC_LOWER);
         Mail_info& mi=*static_cast<Mail_info *>(info);          Mail_info& mi=*static_cast<Mail_info *>(info);
   
         // exclude one attribute [body]          // exclude one attribute [body]
         if(aattribute==BODY_NAME          if(low_element_name==BODY_NAME
                 || aattribute==CHARSET_NAME)                  || low_element_name==CHARSET_NAME)
                 return;                  return;
   
         // fetch from & to from header for SMTP          // fetch from & to from header for SMTP
         if(mi.from && aattribute=="from")          if(mi.from && low_element_name=="from")
                 *mi.from=&lmeaning.as_string();                  *mi.from=&extractEmail(element_value.as_string());
         if(mi.to && aattribute=="to")          if(mi.to && low_element_name=="to")
                 *mi.to=&lmeaning.as_string();                  *mi.to=&extractEmail(element_value.as_string());
           if(low_element_name=="errors-to")
                   mi.errors_to=&extractEmail(element_value.as_string());
   
         // append header line          // append header line
         *mi.header <<           *mi.header << 
                 aattribute << ":" <<                   low_element_name << ":" << 
                 attributed_meaning_to_string(lmeaning, String::UL_MAIL_HEADER).                  attributed_meaning_to_string(element_value, String::UL_MAIL_HEADER).
                         cstr(String::UL_UNSPECIFIED, 0, mi.charset) <<                           cstr(String::UL_UNSPECIFIED, 0, mi.charset, mi.content_charset_name) << 
                 "\n";                  "\n";
 }  }
   
Line 216  static int get_part_name_weight(const Ha Line 145  static int get_part_name_weight(const Ha
                 cstr+=6;                  cstr+=6;
                 offset=ATTACHMENT_WEIGHT;                  offset=ATTACHMENT_WEIGHT;
         } else          } else
                 throw Exception(0, 0,                  throw Exception("parser.runtime",
                         &part_name,                          &part_name,
                         "is neither text# nor attach#");                          "is neither text# nor attach#");
   
Line 237  static int key_of_part(const void *item) Line 166  static int key_of_part(const void *item)
 static int sort_cmp_string_double_value(const void *a, const void *b) {  static int sort_cmp_string_double_value(const void *a, const void *b) {
         return key_of_part(a)-key_of_part(b);          return key_of_part(a)-key_of_part(b);
 }  }
 static const String& letter_hash_to_string(Request& r, const String& method_name,   static const String& message_hash_to_string(Request& r, const String& method_name, 
                                                                                    Hash& letter_hash, int level,                                                                                     Hash& message_hash, int level,
                                                                                    const String **from, const String **to) {                                                                                     const String **from, const String **to) {
         Pool& pool=r.pool();          Pool& pool=r.pool();
   
Line 246  static const String& letter_hash_to_stri Line 175  static const String& letter_hash_to_stri
         String& result=*new(pool) String(pool);          String& result=*new(pool) String(pool);
   
         Charset *charset;          Charset *charset;
         if(Value *vcharset_name=static_cast<Value *>(letter_hash.get(*charset_name)))          if(Value *vrecodecharset_name=static_cast<Value *>(message_hash.get(*charset_name)))
                 charset=&charsets->get_charset(vcharset_name->as_string());                  charset=&charsets->get_charset(vrecodecharset_name->as_string());
         else          else
                 charset=&pool.get_source_charset();                  charset=&pool.get_source_charset();
   
           const char *content_charset_name=0;
           if(Value *vcontent_type=static_cast<Value *>(message_hash.get(*content_type_name)))
                   if(Hash *hcontent_type=vcontent_type->get_hash(0))
                           if(Value *vcontentcharset_name=static_cast<Value *>(hcontent_type->get(*charset_name)))
                                   content_charset_name=vcontentcharset_name->as_string().cstr();
   
         if(from)          if(from)
                 *from=0;                  *from=0;
         if(to)          if(to)
                 *to=0;                  *to=0;
         Mail_info mail_info={          Mail_info mail_info={
                 charset,                  charset, content_charset_name,
                 &result,                  &result,
                 from, to                  from, to
         };          };
         letter_hash.for_each(add_header_attribute, &mail_info);          message_hash.for_each(add_header_attribute, &mail_info);
           if(!mail_info.errors_to)
                   result << "errors-to: postmaster\n"; // errors-to: default
   
         if(Value *body_element=static_cast<Value *>(letter_hash.get(*body_name))) {          if(Value *body_element=static_cast<Value *>(message_hash.get(*body_name))) {
                 if(Hash *body_hash=body_element->get_hash(&method_name)) {                  if(Hash *body_hash=body_element->get_hash(&method_name)) {
                         // body parts..                          // body parts..
                         // ..collect                          // ..collect
Line 295  static const String& letter_hash_to_stri Line 232  static const String& letter_hash_to_stri
                                         if(seq[i].weight>=ATTACHMENT_WEIGHT)                                          if(seq[i].weight>=ATTACHMENT_WEIGHT)
                                                 result << attach_hash_to_string(r, *seq[i].name, *part_hash);                                                  result << attach_hash_to_string(r, *seq[i].name, *part_hash);
                                         else                                           else 
                                                 result << letter_hash_to_string(r, method_name, *part_hash,                                                   result << message_hash_to_string(r, method_name, *part_hash, 
                                                         level+1, 0, 0);                                                          level+1, 0, 0);
                                 else                                  else
                                         throw Exception(0, 0,                                          throw Exception("parser.runtime",
                                                 seq[i].name,                                                  seq[i].name,
                                                 "part is not hash");                                                  "part is not hash");
                         }                          }
Line 322  static const String& letter_hash_to_stri Line 259  static const String& letter_hash_to_stri
                         result.APPEND_CLEAN((const char*)mail_ptr, mail_size, 0, 0);                          result.APPEND_CLEAN((const char*)mail_ptr, mail_size, 0, 0);
                 }                  }
         } else           } else 
                 throw Exception(0, 0,                  throw Exception("parser.runtime",
                         &method_name,                          &method_name,
                         "has no $body");                          "has no $body");
   
Line 330  static const String& letter_hash_to_stri Line 267  static const String& letter_hash_to_stri
 }  }
   
 static void sendmail(Request& r, const String& method_name,   static void sendmail(Request& r, const String& method_name, 
                                          const String& letter,                                            const String& message, 
                                          const String *from, const String *to) {                                           const String *from, const String *to) {
         Pool& pool=r.pool();          Pool& pool=r.pool();
   
         char *letter_cstr=letter.cstr();          char *message_cstr=message.cstr();
         Hash *mail_conf=static_cast<Hash *>(r.classes_conf.get(mail_class->name()));          Hash *mail_conf=static_cast<Hash *>(r.classes_conf.get(mail_base_class->name()));
   
 #ifdef _MSC_VER  
         if(!from)          if(!from)
                 throw Exception(0, 0,                  throw Exception("parser.runtime",
                         &method_name,                          &method_name,
                         "has no 'from' header specified");                          "parameter does not specify 'from' header field");
         if(!to)          if(!to)
                 throw Exception(0, 0,                  throw Exception("parser.runtime",
                         &method_name,                          &method_name,
                         "has no 'to' header specified");                          "parameter does not specify 'to' header field");
   #ifdef _MSC_VER
   
         SMTP& smtp=*new(pool) SMTP(pool, method_name);          SMTP& smtp=*new(pool) SMTP(pool, method_name);
         Value *server_port;          Value *server_port;
Line 358  static void sendmail(Request& r, const S Line 295  static void sendmail(Request& r, const S
                 if(!port)                  if(!port)
                         port="25";                          port="25";
   
                 smtp.Send(server, port, letter_cstr, from->cstr(), to->cstr());                  smtp.Send(server, port, message_cstr, from->cstr(), to->cstr());
         } else          } else
                 throw Exception(0, 0,                  throw Exception("parser.runtime",
                         &method_name,                          &method_name,
                         "$"MAIN_CLASS_NAME":"MAIL_NAME".SMTP not defined");                          "$"MAIN_CLASS_NAME":"MAIL_NAME".SMTP not defined");
 #else  #else
         // unix          // unix
         // $MAIN:MAIL.sendmail["/usr/sbin/sendmail -t"] default          // $MAIN:MAIL.sendmail["/usr/sbin/sendmail -t -i -f postmaster"] default
         // $MAIN:MAIL.sendmail["/usr/lib/sendmail -t"] default          // $MAIN:MAIL.sendmail["/usr/lib/sendmail -t -i  -f postmaster"] default
   
         const char *sendmailkey_cstr="sendmail";  
         const String *sendmail_command;          const String *sendmail_command;
   #ifdef PA_FORCED_SENDMAIL
           sendmail_command=new(pool) String(pool, PA_FORCED_SENDMAIL);
   #else
           const char *sendmailkey_cstr="sendmail";
         if(mail_conf) {          if(mail_conf) {
                 if(Value *sendmail_value=static_cast<Value *>(mail_conf->get(String(pool, sendmailkey_cstr))))                  if(Value *sendmail_value=static_cast<Value *>(mail_conf->get(String(pool, sendmailkey_cstr))))
                         sendmail_command=&sendmail_value->as_string();                          sendmail_command=&sendmail_value->as_string();
                 else                  else
                         throw Exception(0, 0,                          throw Exception("parser.runtime",
                                 &method_name,                                  &method_name,
                                 "$"MAIN_CLASS_NAME":"MAIL_NAME".%s not defined",                                   "$"MAIN_CLASS_NAME":"MAIL_NAME".%s not defined", 
                                 sendmailkey_cstr);                                  sendmailkey_cstr);
Line 382  static void sendmail(Request& r, const S Line 322  static void sendmail(Request& r, const S
                 String *test=new(pool) String(pool, "/usr/sbin/sendmail");                  String *test=new(pool) String(pool, "/usr/sbin/sendmail");
                 if(!file_executable(*test))                  if(!file_executable(*test))
                         test=new(pool) String(pool, "/usr/lib/sendmail");                          test=new(pool) String(pool, "/usr/lib/sendmail");
                 test->APPEND_CONST(" -t");                  test->APPEND_CONST(" -t -i -f postmaster");
                 sendmail_command=test;                  sendmail_command=test;
         }          }
   #endif
   
         // we know sendmail_command here          // we know sendmail_command here, should replace "postmaster" with "$from" from message
           int at_postmaster=sendmail_command->pos("postmaster");
           if(at_postmaster>0) {
                   String& reconstructed=sendmail_command->mid(0, at_postmaster);
                   reconstructed << *from;
                   reconstructed << sendmail_command->mid(at_postmaster+10/*postmaster*/, sendmail_command->size());
                   sendmail_command=&reconstructed;
           }
   
           // execute it
         Array argv(pool);          Array argv(pool);
         const String *file_spec;          const String *file_spec;
         int after_file_spec=sendmail_command->pos(" ", 1);          int after_file_spec=sendmail_command->pos(" ", 1);
Line 399  static void sendmail(Request& r, const S Line 349  static void sendmail(Request& r, const S
         }          }
   
         if(!file_executable(*file_spec))          if(!file_executable(*file_spec))
                 throw Exception(0, 0,                  throw Exception(0,
                         file_spec,                          file_spec, 
                         "is not executable. Set $"MAIN_CLASS_NAME":"MAIL_NAME".%s with appropriate sendmail command",                           "is not executable."
                                 sendmailkey_cstr);  #ifdef PA_FORCED_SENDMAIL
                           " Use configure key \"--with-sendmail=appropriate sendmail command\""
   #else
                           " Set $"MAIN_CLASS_NAME":"MAIL_NAME".%s to appropriate sendmail command", 
                                   sendmailkey_cstr
   #endif
                   );
   
         String in(pool, letter_cstr); String out(pool); String err(pool);  
         int exit_status=pa_exec(*file_spec,          String in(pool, message_cstr); String out(pool); String err(pool);
           int exit_status=pa_exec(
                   // forced_allow
   #ifdef PA_FORCED_SENDMAIL
                   true
   #else
                   false
   #endif
                   , *file_spec,
                 0/*default env*/,                  0/*default env*/,
                 &argv,                  &argv,
                 in, out, err);                  in, out, err);
         if(exit_status || err.size())          if(exit_status || err.size())
                 throw Exception(0, 0,                  throw Exception(0,
                         &method_name,                          &method_name,
                         "'%s' reported problem: %s (%d)",                          "'%s' reported problem: %s (%d)",
                                 file_spec->cstr(),                                  file_spec->cstr(),
Line 428  static void _send(Request& r, const Stri Line 392  static void _send(Request& r, const Stri
         Value& vhash=params->as_no_junction(0, "message must not be code");          Value& vhash=params->as_no_junction(0, "message must not be code");
         Hash *hash=vhash.get_hash(&method_name);          Hash *hash=vhash.get_hash(&method_name);
         if(!hash)          if(!hash)
                 throw Exception(0, 0,                  throw Exception("parser.runtime",
                         &method_name,                          &method_name,
                         "message must be hash");                          "message must be hash");
   
         const String *from, *to;          const String *from, *to;
         const String& letter=letter_hash_to_string(r, method_name, *hash, 0, &from, &to);          const String& message=hash->get(*body_name)/*old format*/?
                   message_hash_to_string(r, method_name, *hash, 0, &from, &to) : // old
                   static_cast<VMail *>(r.self)->message_hash_to_string(r, &method_name, hash, 0, &from, &to); // new
   
 //      r.write_assign_lang(*new(pool) VString(letter));          //r.write_pass_lang(message);
         sendmail(r, method_name, letter, from, to);          sendmail(r, method_name, message, from, to);
 }  }
   
 // constructor & configurator  // constructor & configurator
   
 MMail::MMail(Pool& apool) : Methoded(apool),  MMail::MMail(Pool& apool) : Methoded(apool, MAIL_CLASS_NAME),
         mail_name(apool, MAIL_NAME),          mail_name(apool, MAIL_NAME)
         content_disposition_name(apool, CONTENT_DISPOSITION_NAME),  
         content_disposition_filename_name(apool, CONTENT_DISPOSITION_FILENAME_NAME)  
 {  {
         set_name(*NEW String(pool(), MAIL_CLASS_NAME));  
   
         // ^mail:send{hash}          // ^mail:send{hash}
         add_native_method("send", Method::CT_STATIC, _send, 1, 1);          add_native_method("send", Method::CT_STATIC, _send, 1, 1);
 }  }
Line 460  void MMail::configure_user(Request& r) { Line 422  void MMail::configure_user(Request& r) {
                 if(Hash *mail_conf=mail_element->get_hash(0))                  if(Hash *mail_conf=mail_element->get_hash(0))
                         r.classes_conf.put(name(), mail_conf);                          r.classes_conf.put(name(), mail_conf);
                 else                  else
                         throw Exception(0, 0,                          if( !mail_element->is_string() )
                                 0,                                  throw Exception("parser.runtime",
                                 "$" MAIL_CLASS_NAME ":" MAIL_NAME " is not hash");                                          0,
                                           "$" MAIL_CLASS_NAME ":" MAIL_NAME " is not hash");
 }  }
   
 // creator  // creator
   
 Methoded *MMail_create(Pool& pool) {  Methoded *MMail_create(Pool& pool) {
         return mail_class=new(pool) MMail(pool);          return mail_base_class=new(pool) MMail(pool);
 }  }

Removed from v.1.52  
changed lines
  Added in v.1.74


E-mail: