/** * All functions below are pretty self explanitory. * They basically just preform some simple header printing * for our email 'message'. I'm not doing to comment every * function from here because you should be able to read the * damn functions and get a great idea **/ static void printMimeHeaders(const char *b, dstrbuf *msg, CharSetType charset) { dsbPrintf(msg, "Mime-Version: 1.0\r\n"); if (Mopts.gpg_opts & GPG_ENC) { dsbPrintf(msg, "Content-Type: multipart/encrypted; " "protocol=\"application/pgp-encrypted\"; " "boundary=\"%s\"\r\n", b); } else if (Mopts.gpg_opts & GPG_SIG) { dsbPrintf(msg, "Content-Type: multipart/signed; " "micalg=pgp-sha1; protocol=\"application/pgp-signature\"; " "boundary=\"%s\"\r\n", b); } else if (Mopts.attach) { dsbPrintf(msg, "Content-Type: multipart/mixed; boundary=\"%s\"\r\n", b); } else { if (charset == IS_UTF8 || charset == IS_PARTIAL_UTF8) { dsbPrintf(msg, "Content-Type: text/plain; charset=utf-8\r\n"); if (charset == IS_PARTIAL_UTF8) { dsbPrintf(msg, "Content-Transfer-Encoding: quoted-printable\r\n"); } else { dsbPrintf(msg, "Content-Transfer-Encoding: base64\r\n"); } dsbPrintf(msg, "Content-Disposition: inline\r\n"); } else if (Mopts.html) { dsbPrintf(msg, "Content-Type: text/html\r\n"); } else { dsbPrintf(msg, "Content-Type: text/plain\r\n"); } } }
/** * This function takes the current content that was copied * in to us and creates a final message with the email header * and the appended content. It will also attach any files * that were specified at the command line. **/ static void printHeaders(const char *border, dstrbuf *msg, CharSetType msg_cs) { char *subject=Mopts.subject; char *user_name = getConfValue("MY_NAME"); char *email_addr = getConfValue("MY_EMAIL"); char *sm_bin = getConfValue("SENDMAIL_BIN"); char *smtp_serv = getConfValue("SMTP_SERVER"); char *reply_to = getConfValue("REPLY_TO"); dstrbuf *dsb=NULL; if (subject) { if (Mopts.encoding) { CharSetType cs = getCharSet((u_char *)subject); if (cs == IS_UTF8) { dsb = encodeUtf8String((u_char *)subject, false); subject = dsb->str; } else if (cs == IS_PARTIAL_UTF8) { dsb = encodeUtf8String((u_char *)subject, true); subject = dsb->str; } } dsbPrintf(msg, "Subject: %s\r\n", subject); if (dsb) { dsbDestroy(dsb); } } printFromHeaders(user_name, email_addr, msg); printToHeaders(Mopts.to, Mopts.cc, msg); /** * We want to check here to see if we are sending mail by invoking sendmail * If so, We want to add the BCC line to the headers. Sendmail checks this * Line and makes sure it sends the mail to the BCC people, and then remove * the BCC addresses... Keep in mind that sending to an smtp servers takes * presidence over sending to sendmail incase both are mentioned. */ if (sm_bin && !smtp_serv) { printBccHeaders(Mopts.bcc, msg); } /* The rest of the standard headers */ printDateHeaders(msg); if (reply_to) { dsbPrintf(msg, "Reply-To: <%s>\r\n", reply_to); } printMimeHeaders(border, msg, msg_cs); dsbPrintf(msg, "X-Mailer: Cleancode.email v%s \r\n", EMAIL_VERSION); if (Mopts.priority) { dsbPrintf(msg, "X-Priority: 1\r\n"); } printExtraHeaders(Mopts.headers, msg); dsbPrintf(msg, "\r\n"); }
/** * just prints the to headers, and cc headers if available **/ static void printToHeaders(dlist to, dlist cc, dstrbuf *msg) { struct addr *a = (struct addr *)dlGetNext(to); dsbPrintf(msg, "To: "); while (a) { dstrbuf *tmp = formatEmailAddr(a->name, a->email); dsbPrintf(msg, "%s", tmp->str); dsbDestroy(tmp); a = (struct addr *)dlGetNext(to); if (a != NULL) { dsbPrintf(msg, ", "); } else { dsbPrintf(msg, "\r\n"); } } if (cc != NULL) { dsbPrintf(msg, "Cc: "); a = (struct addr *)dlGetNext(cc); while (a) { dstrbuf *tmp = formatEmailAddr(a->name, a->email); dsbPrintf(msg, "%s", tmp->str); dsbDestroy(tmp); a = (struct addr *)dlGetNext(cc); if (a != NULL) { dsbPrintf(msg, ", "); } else { dsbPrintf(msg, "\r\n"); } } } }
/** Print From Headers **/ static void printFromHeaders(char *name, char *address, dstrbuf *msg) { dstrbuf *addr = formatEmailAddr(name, address); dsbPrintf(msg, "From: %s\r\n", addr->str); dsbDestroy(addr); }
static void printExtraHeaders(dlist headers, dstrbuf *msg) { char *hdr=NULL; while ((hdr = (char *)dlGetNext(headers)) != NULL) { dsbPrintf(msg, "%s\r\n", hdr); } }
/** * Makes a boundary for Mime emails **/ dstrbuf * mimeMakeBoundary(void) { dstrbuf *buf=DSB_NEW; dstrbuf *rstr=randomString(15); dsbPrintf(buf, "=-%s", rstr->str); return buf; }
/** * set up the appropriate MIME and Base64 headers for * the attachment of file specified in Mopts.attach **/ static int attachFiles(const char *boundary, dstrbuf *out) { dstrbuf *file_name = NULL; dstrbuf *file_type = NULL; char *next_file = NULL; /* * What we will do here is parse Mopts.attach for comma delimited file * names. If there was only one file specified with no comma, then strtok() * will just return that file and the next call to strtok() will be NULL * which will allow use to break out of our loop of attaching base64 stuff. */ while ((next_file = (char *)dlGetNext(Mopts.attach)) != NULL) { FILE *current = fopen(next_file, "r"); if (!current) { #if 1 //Ren: to skip nonexistent file fprintf(stderr, "email: skip %s\n", next_file); continue; #else fatal("Could not open attachment: %s", next_file); return (ERROR); #endif } /* If the user specified an absolute path, just get the file name */ file_type = mimeFiletype(next_file); file_name = mimeFilename(next_file); /* Set our MIME headers */ dsbPrintf(out, "\r\n--%s\r\n", boundary); dsbPrintf(out, "Content-Transfer-Encoding: base64\r\n"); dsbPrintf(out, "Content-Type: %s; name=\"%s\"\r\n", file_type->str, file_name->str); dsbPrintf(out, "Content-Disposition: attachment; filename=\"%s\"\r\n", file_name->str); dsbPrintf(out, "\r\n"); /* Encode to 'out' */ mimeB64EncodeFile(current, out); dsbDestroy(file_type); dsbDestroy(file_name); } return SUCCESS; }
/** * Makes a message type specifically for gpg encryption and * signing. Specific MIME message descriptions are needed * when signing/encrypting a file before it is actuall signed * or encrypted. This function takes care of that. **/ static int makeGpgMessage(dstrbuf *in, dstrbuf *out, const char *border) { dstrbuf *qp=NULL; assert(in != NULL); assert(out != NULL); assert(border != NULL); if (Mopts.attach) { dsbPrintf(out, "Content-Type: multipart/mixed; " "boundary=\"%s\"\r\n\r\n", border); dsbPrintf(out, "\r\n--%s\r\n", border); } if (Mopts.html) { dsbPrintf(out, "Content-Type: text/html\r\n"); } else { dsbPrintf(out, "Content-Type: text/plain\r\n"); } dsbPrintf(out, "Content-Transfer-Encoding: quoted-printable\r\n\r\n"); qp = mimeQpEncodeString((u_char *)in->str, true); dsbnCat(out, qp->str, qp->len); dsbDestroy(qp); if (Mopts.attach) { attachFiles(border, out); dsbPrintf(out, "\r\n--%s--\r\n", border); } return 0; }
/** * Makes a standard plain text message while taking into * account the MIME message types and boundary's needed * if and when a file is attached. **/ static int makeMessage(dstrbuf *in, dstrbuf *out, const char *border, CharSetType charset) { dstrbuf *enc=NULL; if (Mopts.attach) { dsbPrintf(out, "--%s\r\n", border); if (charset == IS_UTF8 || charset == IS_PARTIAL_UTF8) { dsbPrintf(out, "Content-Type: text/plain; charset=utf-8\r\n"); if (IS_PARTIAL_UTF8) { dsbPrintf(out, "Content-Transfer-Encoding: quoted-printable\r\n"); enc = mimeQpEncodeString((u_char *)in->str, true); } else { dsbPrintf(out, "Content-Transfer-Encoding: base64\r\n"); enc = mimeB64EncodeString((u_char *)in->str, in->len, true); } dsbPrintf(out, "Content-Disposition: inline\r\n\r\n"); } else if (Mopts.html) { dsbPrintf(out, "Content-Type: text/html\r\n\r\n"); enc = DSB_NEW; dsbCat(enc, in->str); } else { dsbPrintf(out, "Content-Type: text/plain\r\n\r\n"); enc = DSB_NEW; dsbCat(enc, in->str); } } else { if (charset == IS_UTF8) { enc = mimeB64EncodeString((u_char *)in->str, in->len, true); } else if (charset == IS_PARTIAL_UTF8) { enc = mimeQpEncodeString((u_char *)in->str, true); } else { enc = DSB_NEW; dsbCat(enc, in->str); } } dsbPrintf(out, "%s\r\n", enc->str); if (Mopts.attach) { if (attachFiles(border, out) == ERROR) { return ERROR; } dsbPrintf(out, "\r\n\r\n--%s--\r\n", border); } dsbDestroy(enc); return 0; }
dstrbuf * encodeUtf8String(const u_char *str, bool use_qp) { const u_int max_blk_len = 45; u_int i=max_blk_len; dstrbuf *enc, *dsb = DSB_NEW; size_t len = strlen((char *)str); if (use_qp) { // TODO: We need to break this up so that we're not // creating extra long strings. enc = mimeQpEncodeString(str, false); dsbPrintf(dsb, "=?utf-8?q?%s?=", enc->str); i = len; // Just reset for now. } else { enc = mimeB64EncodeString(str, (len > max_blk_len ? max_blk_len : len), false); dsbPrintf(dsb, "=?utf-8?b?%s?=", enc->str); } dsbDestroy(enc); /* If we have anymore data to encode, we have to do it by adding a newline plus a space because each section can only be 75 chars long. */ while (i < len) { size_t newlen = strlen((char *)str + i); /* only allow max_blk_len sections */ if (newlen > max_blk_len) { newlen = max_blk_len; } enc = mimeB64EncodeString(str + i, newlen, false); dsbPrintf(dsb, "\r\n =?utf-8?b?%s?=", enc->str); dsbDestroy(enc); i += newlen; } return dsb; }
/** Print Date Headers **/ static void printDateHeaders(dstrbuf *msg) { time_t set_time; struct tm *lt; char buf[MAXBUF] = { 0 }; set_time = time(&set_time); #ifdef USE_GMT lt = gmtime(&set_time); #else lt = localtime(&set_time); #endif #ifdef USE_GNU_STRFTIME strftime(buf, MAXBUF, "%a, %d %b %Y %H:%M:%S %z", lt); #else strftime(buf, MAXBUF, "%a, %d %b %Y %H:%M:%S %Z", lt); #endif dsbPrintf(msg, "Date: %s\r\n", buf); }
/** * Makes a message type specifically for gpg encryption and * signing. Specific MIME message descriptions are needed * when signing/encrypting a file before it is actuall signed * or encrypted. This function takes care of that. **/ static int makeGpgMessage(dstrbuf *in, dstrbuf *out, const char *border) { char *ptr=NULL; dstrbuf *qp=NULL; assert(in != NULL); assert(out != NULL); assert(border != NULL); if (Mopts.attach) { dsbPrintf(out, "Content-Type: multipart/mixed; " "boundary=\"%s\"\r\n\r\n", border); dsbPrintf(out, "\r\n--%s\r\n", border); } if (Mopts.html) { dsbPrintf(out, "Content-Type: text/html\r\n"); } else { dsbPrintf(out, "Content-Type: text/plain\r\n"); } dsbPrintf(out, "Content-Transfer-Encoding: quoted-printable\r\n\r\n"); qp = mimeQpEncodeString((u_char *)in->str, true); /* Fix single dot on it's on line so we don't terminate the message prematurely. */ dstrbuf *formatted = DSB_NEW; char previous='\0'; for (ptr = qp->str; ptr && *ptr != '\0'; previous=*ptr, ptr++) { dsbCatChar(formatted, *ptr); /* If we have a dot starting on a newline. */ if ((previous == '\n' || previous == '\r') && *ptr == '.') { dsbCatChar(formatted, '.'); } } dsbDestroy(qp); dsbnCat(out, formatted->str, formatted->len); dsbDestroy(formatted); if (Mopts.attach) { attachFiles(border, out); dsbPrintf(out, "\r\n--%s--\r\n", border); } return 0; }
/** * Creates a signed message with gpg and takes into * account the correct MIME message types to add to * the message. **/ static dstrbuf * createGpgEmail(dstrbuf *msg, GpgCallType gpg_type) { dstrbuf *tmpbuf=DSB_NEW; dstrbuf *gpgdata=NULL, *buf=DSB_NEW; dstrbuf *border1=NULL, *border2=NULL; assert(msg != NULL); /* Create two borders if we're attaching files */ border1 = mimeMakeBoundary(); if (Mopts.attach) { border2 = mimeMakeBoundary(); } else { border2 = DSB_NEW; } if (makeGpgMessage(msg, tmpbuf, border2->str) < 0) { dsbDestroy(buf); buf=NULL; goto end; } gpgdata = callGpg(tmpbuf, gpg_type); if (!gpgdata) { dsbDestroy(buf); buf=NULL; goto end; } printHeaders(border1->str, buf, IS_ASCII); dsbPrintf(buf, "\r\n--%s\r\n", border1->str); if (gpg_type & GPG_ENC) { dsbPrintf(buf, "Content-Type: application/pgp-encrypted\r\n\r\n"); dsbPrintf(buf, "Version: 1\r\n"); dsbPrintf(buf, "\r\n--%s\r\n", border1->str); dsbPrintf(buf, "Content-type: application/octet-stream; " "name=encrypted.asc\r\n\r\n"); } else if (gpg_type & GPG_SIG) { dsbPrintf(buf, "%s\r\n", tmpbuf->str); dsbPrintf(buf, "\r\n--%s\r\n", border1->str); dsbPrintf(buf, "Content-Type: application/pgp-signature\r\n"); dsbPrintf(buf, "Content-Description: This is a digitally signed message\r\n\r\n"); } dsbPrintf(buf, "%s", gpgdata->str); dsbPrintf(buf, "\r\n--%s--\r\n", border1->str); end: dsbDestroy(tmpbuf); dsbDestroy(gpgdata); dsbDestroy(border1); dsbDestroy(border2); return buf; }
/** * Makes a standard plain text message while taking into * account the MIME message types and boundary's needed * if and when a file is attached. **/ static int makeMessage(dstrbuf *in, dstrbuf *out, const char *border, CharSetType charset) { char *ptr=NULL; dstrbuf *enc=NULL; if (Mopts.attach) { dsbPrintf(out, "--%s\r\n", border); if (charset == IS_UTF8 || charset == IS_PARTIAL_UTF8) { if (Mopts.html) { dsbPrintf(out, "Content-Type: text/html; charset=utf-8\r\n"); } else { dsbPrintf(out, "Content-Type: text/plain; charset=utf-8\r\n"); } if (IS_PARTIAL_UTF8) { dsbPrintf(out, "Content-Transfer-Encoding: quoted-printable\r\n"); enc = mimeQpEncodeString((u_char *)in->str, true); } else { dsbPrintf(out, "Content-Transfer-Encoding: base64\r\n"); enc = mimeB64EncodeString((u_char *)in->str, in->len, true); } dsbPrintf(out, "Content-Disposition: inline\r\n\r\n"); } else if (Mopts.html) { dsbPrintf(out, "Content-Type: text/html\r\n\r\n"); enc = DSB_NEW; dsbCat(enc, in->str); } else { dsbPrintf(out, "Content-Type: text/plain\r\n\r\n"); enc = DSB_NEW; dsbCat(enc, in->str); } } else { if (charset == IS_UTF8) { enc = mimeB64EncodeString((u_char *)in->str, in->len, true); } else if (charset == IS_PARTIAL_UTF8) { enc = mimeQpEncodeString((u_char *)in->str, true); } else { enc = DSB_NEW; dsbCat(enc, in->str); } } /* Fix single dot on it's on line so we don't terminate the message prematurely. */ dstrbuf *formatted = DSB_NEW; char previous='\0'; for (ptr = enc->str; ptr && *ptr != '\0'; previous=*ptr, ptr++) { dsbCatChar(formatted, *ptr); /* If we have a dot starting on a newline. */ if ((previous == '\n' || previous == '\r') && *ptr == '.') { dsbCatChar(formatted, '.'); } } dsbDestroy(enc); dsbPrintf(out, "%s\r\n", formatted->str); if (Mopts.attach) { if (attachFiles(border, out) == ERROR) { return ERROR; } dsbPrintf(out, "\r\n\r\n--%s--\r\n", border); } dsbDestroy(formatted); return 0; }
static int smtpAuthPlain(dsocket *sd, const char *user, const char *pass) { int retval = 0; dstrbuf *data=NULL; dstrbuf *up = DSB_NEW; dstrbuf *rbuf = DSB_NEW; if (writeResponse(sd, "AUTH PLAIN\r\n") < 0) { smtpSetErr("Socket write error: smtp_auth_plain"); retval = ERROR; goto end; } #ifdef DEBUG_SMTP printf("--> AUTH PLAIN\n"); fflush(stdout); #endif retval = readResponse(sd, rbuf); if (retval != 334) { if (retval != ERROR) { smtpSetErr(rbuf->str); } retval = ERROR; goto end; } #ifdef DEBUG_SMTP printf("<-- %s\n", rbuf->str); fflush(stdout); #endif dsbPrintf(up, "%c%s%c%s", '\0', user, '\0', pass); data = mimeB64EncodeString((u_char *)up->str, up->len, false); if (writeResponse(sd, "%s\r\n", data->str) < 0) { smtpSetErr("Socket write error: smtp_auth_plain"); retval = ERROR; goto end; } #ifdef DEBUG_SMTP printf("--> %s\n", data->str); fflush(stdout); #endif dsbClear(rbuf); retval = readResponse(sd, rbuf); if (retval != 235) { if (retval != ERROR) { smtpSetErr(rbuf->str); } retval = ERROR; goto end; } #ifdef DEBUG_SMTP printf("<-- %s\n", rbuf->str); fflush(stdout); #endif end: dsbDestroy(up); dsbDestroy(data); dsbDestroy(rbuf); return retval; }