#include #include #include #include #include #include #include #include #include #include #include "tlmpmail.h" #include "tlmpmail.m" /* Print a heading entry if non empty */ static void mailformat_print_if ( SSTREAM &ss, const SSTRING &s, const char *keyword) { if (s.is_filled()){ ss.printf ("%s: %s\n",keyword,s.get()); } } /* Print a heading entry if non empty */ static void mailformat_print_if ( SSTREAM &ss, const EMAILADDRS &addrs, const char *keyword) { if (addrs.is_filled()){ const char *colon = ":"; for (int i=0; i static int mailsend_encode64 (const char *fname, SSTREAM &ss) { int ret = -1; FILE *fin = fopen (fname,"r"); if (fin == NULL){ tlmp_error (MSG_U(E_CANTREADATT,"Can't read attachement %s"),fname); }else{ struct stat st; if (fstat(fileno(fin),&st)!=-1){ size_t size = st.st_size; off_t sofar = 0; // By sending a multiple of 54, the base64 encoding // produce lines of exactly 72 bytes. Some mailer // are confused if the lines are unequal. Not sure const size_t bufsize = 54*100; if (size > bufsize) size = bufsize; // Avoid using a large stack char tmp[size]; size_t readlen; while (sofar < st.st_size && (readlen=fread (tmp,1,size,fin)) > 0){ char *enc = base64_encode (tmp,readlen); sofar += readlen; if (enc != NULL){ ss.puts (enc); // base64_encode already puts a \n if the last line is // 72 bytes long if (readlen != size) ss.puts ("\n"); free (enc); ret = 0; }else{ ret = -1; break; } } fclose (fin); } } return ret; } static int mailsend_error (const char *epath, int eerrno) { tlmp_error (MSG_U(E_GLOB,"Error while resolving attachment pattern\n" "for file %s\n" "(%s)"),epath,strerror(eerrno)); return -1; } bool mailsend_validattach (const ATTACHEMENTS &attachs, ATTACHEMENTS &result) { bool ret = true; SSTRING errors; for (int i=0; igetfname(); fname = str_skip(fname); if (fname[0] == '|'){ }else if (fname[0] != '\0'){ glob_t g; int ok = glob (fname,0,mailsend_error,&g); if (ok != 0){ ret = false; errors.appendf (MSG_U(E_MISSATT,"Missing attachment: %s\n") ,fname); }else{ for (size_t j=0; jsetfname (g.gl_pathv[j]); n->setdescription (a->getdescription()); n->setname (a->getname()); n->setmimetype (a->getmimetype()); result.add (n); } } } } if (errors.is_filled()) xconf_error ("%s",errors.get()); return ret; } void mailformat ( MAIL_MESSAGE_FULL &msg, const ATTACHEMENTS &attachs, SSTREAM &ss) { SSTRING date; miscmail_formatdate (msg.date,date); if (msg.from.is_empty()){ SSTRING myname,myaddr; prefs_getuserinfo(myname,myaddr); ss.printf ("From %s %s\n",myaddr.get(),date.get()); ss.printf ("From: %s <%s>\n",myname.get(),myaddr.get()); ss.printf ("Return-Path: %s\n",myaddr.get()); }else{ ss.printf ("From %s %s\n",msg.get_from(),date.get()); ss.printf ("From: %s\n",msg.get_from()); ss.printf ("Return-Path: %s\n",msg.getemail_from()); } ss.printf ("Date: %s\n",date.get()); mailformat_print_if (ss,msg.to,"To"); ss.printf ("Subject: %s\n",msg.getsubject()); mailformat_print_if (ss,msg.cc,"Cc"); mailformat_print_if (ss,msg.reply,"Reply-To"); SSTRING references (msg.references); references.strip_end(); if (msg.id.is_filled()){ if (references.is_filled()) references.append (" "); references.append (msg.id); } mailformat_print_if (ss,references,"References"); mailformat_print_if (ss,msg.id,"In-Reply-To"); ss.printf ("X-mailer: tlmpmail %s\n",tlmpmail_version); ss.printf ("Mime-Version: 1.0\n"); ss.printf ("Message-ID: %s\n",msg.newid.get()); if (attachs.getnb() > 0){ char boundary[100]; sprintf (boundary,"----nextpart-%lu",time(NULL)); ss.puts ("Content-Type: multipart/mixed;\n"); ss.printf ("\tboundary=\"%s\"\n",boundary); ss.puts ("\n"); ss.puts ("This is a multi-part message in MIME format.\n"); ss.puts ("\n"); ss.printf ("--%s\n",boundary); ss.printf ("Content-type: text/plain; charset=\"%s\"\n",prefs_getcharset()); ss.puts ("\n"); ss.puts (msg.text.get()); ATTACHEMENTS result; if (mailsend_validattach(attachs,result)){ for (int i=0; igetfname(); const char *rfname = strrchr(fname,'/'); if (rfname == NULL){ rfname = fname; }else{ rfname++; } const char *name = a->getname(); const char *desc = a->getdescription(); const char *type = a->getmimetype(); if (type[0] == '\0') type = "application/octet-stream"; if (name[0] == '\0') name = rfname; ss.printf ("Content-type: %s; name=\"%s\"\n",type,name); ss.puts ("Content-Transfer-Encoding: BASE64\n"); ss.printf ("Content-Description: %s\n",desc); ss.printf ("Content-Disposition: attachment; filename=\"%s\"\n",rfname); ss.puts ("\n"); mailsend_encode64 (fname,ss); } } ss.printf ("--%s--\n",boundary); }else{ ss.printf ("Content-type: text/plain; charset=\"%s\"\n",prefs_getcharset()); ss.puts ("\n"); ss.puts (msg.text.get()); } } void _F_mailsend::fail (SSTRINGS &, MAIL_MESSAGE_FULL &, const ATTACHEMENTS &) { } void _F_mailsend::ok (SSTRINGS &, MAIL_MESSAGE_FULL &, const ATTACHEMENTS &) { } int mailsend (_F_mailsend &c, MAIL_MESSAGE_FULL &msg, const ATTACHEMENTS &attachs) { int ret = -1; SSTRINGS recipients; recipients.append (msg.to.addrs); recipients.append (msg.cc.addrs); recipients.append (msg.bcc.addrs); SSTRING tmp; for (int i=0; iget()); } POPEN pop ("/usr/sbin/sendmail",tmp.get()); if (pop.isok()){ FILE *fout = pop.getfout(); SSTREAM_FILE ss(fout); msg.date = time(NULL); { struct tm *t = localtime (&msg.date); char randstr[13]; randstr[0] = '\0'; int fd = open ("/dev/urandom",O_RDONLY); if (fd != -1){ char rand[6]; // We care little about the result, crap in rand is "random" anyway read (fd,rand,sizeof(rand)); for (unsigned i=0; i" ,t->tm_year+1900,t->tm_mon+1,t->tm_mday ,t->tm_hour,t->tm_min,t->tm_sec ,randstr,host); } ret = c.send (recipients,msg,attachs,ss); if (ret != -1){ pop.closepipe(); SSTRING err; while (1){ int ok = pop.wait(1); if (ok < 0) break; char buf[1000]; while (pop.readerr (buf,sizeof(buf))!=-1){ err.appendf("%s\n",buf); } } if (!err.is_empty()){ ret = -1; xconf_notice (MSG_U(E_SENDMAILERR,"Error from sendmail:\n\n%s") ,err.get()); }else{ ret = pop.getstatus(); } } if (ret != -1){ c.ok (recipients,msg,attachs); } }else{ c.fail (recipients,msg,attachs); } return ret; }