/**---------------------------------------------------------------------------*/ // File: hudmail\smtp.c // A component of the Workflow system of subsidized contract administration. // Copyright (c) 2001, Richard Heurtley. All rights reserved. /**---------------------------------------------------------------------------*/ #include "common.h" /**---------------------------------------------------------------------------*/ //#define TEST // Richard's office //#define NOXMITDELETE // don't delete \email\xmit files #define HTMLMESSAGE // do send HTML format message /**---------------------------------------------------------------------------*/ typedef struct { char *filenamep; char *postedp; char *fromp; char *top; char *ccp; char *subjp; char cno[11+1]; char pna[50+1]; char amount[1+10+1+2+1]; // $-000000005.00~ } XFILE; /**---------------------------------------------------------------------------*/ void XFileFree(XFILE *xfilep) { MemFree(xfilep->filenamep); MemFree(xfilep->postedp); MemFree(xfilep->fromp); MemFree(xfilep->top); MemFree(xfilep->ccp); MemFree(xfilep->subjp); MemFree(xfilep); RETURNVOID; } /**---------------------------------------------------------------------------*/ void detrail(char *p) { char *q; for (q = p; *q; q++); // get end of string for (q--; q >= p && *q == ' '; q--); // get last significant char *(q+1) = 0; // delimit string RETURNVOID; } /**---------------------------------------------------------------------------*/ void dezero(char *p) { char *q; if (*p == '-') // if negative p++; // operate on next character for (q = p; *q && *q == '0'; q++); // find first non-zero strcpy(p, q); // move to beginning of string if (!*p) strcpy(p, "0"); RETURNVOID; } /**---------------------------------------------------------------------------*/ char *Parse(char *p) { char *rvp; for (; *p && !isspace(*p); p++); // find whitespace rvp = p; // get return value if (!*p) // if none goto EGRESS; // return pointer to null for (; *p && isspace(*p); p++); // find content rvp = p; // get return value if (!*p) // if none goto EGRESS; // return pointer to null for (; *p; p++); // find terminator for (p--; isspace(*p); p--); // find content p++; // get whitespace *p = 0; // delimit it EGRESS: RETURN(rvp); } /**---------------------------------------------------------------------------*/ int Expect(char *replyp, int expect) { int rv = 0; int n; int code; n = sscanf(replyp, "%d", &code); if (n != 1) { LOGF("Unable to parse reply:"); TextLog(replyp); rv = 1; goto EGRESS; } if (code != expect) { LOGF("Received unexpected reply:"); TextLog(replyp); LOGF("Expected %d", expect); rv = 1; goto EGRESS; } EGRESS: RETURN(rv); } /**---------------------------------------------------------------------------*/ int VExpect(XSOCKET *xsp, int expect, char *sendp) { int rv = 0; int nbytes; static char line[1024]; rv = VLine(xsp, line, &nbytes); if (rv) { LOGF("Timeout waiting for reply to:"); TextLog(sendp); goto EGRESS; } rv = Expect(line, expect); if (rv) { LOGF("Received bad reply to:"); TextLog(sendp); goto EGRESS; } EGRESS: RETURN(rv); } /**---------------------------------------------------------------------------*/ int XVExpect(XSOCKET *xsp, int expect, char *formatp, ...) { int rv = 0; va_list arglist; static char send[1024]; va_start(arglist, formatp); vsprintf(send, formatp, arglist); va_end(arglist); rv = XLine(xsp, "%s", send); if (rv) { LOGF("Unable to send:"); TextLog(send); goto EGRESS; } rv = VExpect(xsp, expect, send); if (rv) { LOGF("Unexpected reply to:"); TextLog(send); goto EGRESS; } EGRESS: RETURN(rv); } /**---------------------------------------------------------------------------*/ int XVCode(XSOCKET *xsp, int *codep, char *formatp, ...) { int rv = 0; int n; int code; int nbytes; va_list arglist; static char send[1024]; static char line[1024]; va_start(arglist, formatp); vsprintf(send, formatp, arglist); va_end(arglist); rv = XLine(xsp, "%s", send); if (rv) { LOGF("Unable to send:"); TextLog(send); goto EGRESS; } rv = VLine(xsp, line, &nbytes); if (rv) { LOGF("Timeout waiting for reply to:"); TextLog(send); goto EGRESS; } n = sscanf(line, "%d", &code); if (n != 1) { LOGF("Unable to parse reply:"); TextLog(line); rv = 1; goto EGRESS; } if (codep) *codep = code; EGRESS: RETURN(rv); } /**---------------------------------------------------------------------------*/ char *NBSP(char *p) { if (!p || !p[0]) p = " "; RETURN(p); } /**---------------------------------------------------------------------------*/ char *Left(char *srcp, int dstlen) { int dst; int srclen; char *stringp; static int string; static char strings[8][64]; if (string >= 8) string = 0; stringp = strings[string]; string++; srclen = strlen(srcp); if (srclen > dstlen) srclen = dstlen; dst = 0; memset(stringp, ' ', dstlen); memcpy(&stringp[dst], srcp, srclen); stringp[dstlen] = 0; RETURN(stringp); } /**---------------------------------------------------------------------------*/ char *Center(char *srcp, int dstlen) { int dst; int srclen; char *stringp; static int string; static char strings[8][64]; if (string >= 8) string = 0; stringp = strings[string]; string++; srclen = strlen(srcp); if (srclen > dstlen) srclen = dstlen; dst = (dstlen - srclen) / 2; memset(stringp, ' ', dstlen); memcpy(&stringp[dst], srcp, srclen); stringp[dstlen] = 0; RETURN(stringp); } /**---------------------------------------------------------------------------*/ char *Right(char *srcp, int dstlen) { int dst; int srclen; char *stringp; static int string; static char strings[8][64]; if (string >= 8) string = 0; stringp = strings[string]; string++; srclen = strlen(srcp); if (srclen > dstlen) srclen = dstlen; dst = dstlen - srclen; memset(stringp, ' ', dstlen); memcpy(&stringp[dst], srcp, srclen); stringp[dstlen] = 0; RETURN(stringp); } /**---------------------------------------------------------------------------*/ char *Dup(char c, int dstlen) { char *stringp; static int string; static char strings[8][64]; if (string >= 8) string = 0; stringp = strings[string]; string++; dstlen += 2; memset(stringp, c, dstlen); stringp[dstlen] = 0; RETURN(stringp); } /**---------------------------------------------------------------------------*/ char *FileName(int time, int msgseq, int fileseq) { int y, m, d, h, n, s; int md; int hns; static char filename[8+1+3 +1]; DateTimeDecompose(time, &y, &m, &d, &h, &n, &s); y -= 2000; md = (m-1) * 31 + (d-1); hns = (h * 60 + n) * 60 + s; y = y % 26; d = md % 26; md /= 26; m = md % 26; s = hns % 26; hns /= 26; n = hns % 26; hns /= 26; h = hns % 26; filename[0] = 'A' + y; filename[1] = 'A' + m; filename[2] = 'A' + d; filename[3] = 'A' + h; filename[4] = 'A' + n; filename[5] = 'A' + s; filename[6] = 'A' + msgseq; filename[7] = 'A' + fileseq; strcpy(&filename[8], ".TXT"); RETURN(filename); } /**---------------------------------------------------------------------------*/ // generate the fixed message text int MessageText ( XSOCKET *xsp, LIST *yfilelistp, int time, int msgseq, int fileseq, int html ) { int rv = 0; LISTLOOP yfileloop; XFILE *yfilep; char *contentsp; char *eolp; if (html) eolp = "
\r\n"; else eolp = "\r\n"; if (html) { rv = XLine ( xsp, "\r\n" "\r\n" "\r\n" ); if (rv) goto EGRESS; } if (yfilelistp->nnodes > 1) rv = XLine(xsp, "This message contains %d attached TRACS files as follows:%s", yfilelistp->nnodes, eolp); else rv = XLine(xsp, "This message contains the following attached TRACS file:%s", eolp); if (rv) goto EGRESS; rv = XLine(xsp, "%s", eolp); if (rv) goto EGRESS; if (html) { rv = XLine(xsp, "\r\n"); if (rv) goto EGRESS; } if (!html) { rv = XLine ( xsp, "| %s | %s | %s | %s | %s |\r\n", Center("File", 12), // 12345678.123 Center("Project", 35), Center("Contract", 11), Center("Contents", 8), Center("Amount", 11) // $1222333.00 ); if (rv) goto EGRESS; rv = XLine ( xsp, "+%s+%s+%s+%s+%s+\r\n", Dup('-', 12), // 12345678.123 Dup('-', 35), Dup('-', 11), Dup('-', 8), Dup('-', 11) // $1222333.00 ); if (rv) goto EGRESS; } else { rv = XLine ( xsp, "" "" "" "" "" "" "\r\n" ); if (rv) goto EGRESS; } LISTLOOPLP(yfile) { if (*yfilep->amount) contentsp = "voucher"; else contentsp = "cert"; if (!html) { rv = XLine ( xsp, "| %s | %s | %s | %s | %s |\r\n", Center(FileName(time, msgseq, fileseq), 12), Left(yfilep->pna, 35), Center(yfilep->cno, 11), Center(contentsp, 8), Right(yfilep->amount, 11) ); } else { rv = XLine ( xsp, "" "" "" "" "" "" "\r\n", FileName(time, msgseq, fileseq), NBSP(yfilep->pna), NBSP(yfilep->cno), contentsp, NBSP(yfilep->amount) ); } if (rv) goto EGRESS; fileseq++; } if (html) { rv = XLine ( xsp, "
FileProjectContractContentsAmount
%s%s%s%s%s
\r\n" "\r\n" "\r\n" ); if (rv) goto EGRESS; } EGRESS: RETURN(rv); } /**---------------------------------------------------------------------------*/ int main(int npars, char **pars) { int rv = 0; int boundseq; int fileseq; int lasttime; int line; int msgseq; int nbytes; int nlines; int port; int rc; int time; int code; char *postedp; char *fromp; char *top; char *ccp; char *subjp; char *bufferp; char *filenamep; char *linep; char *servernamep; char *subj; char **lines; char *usernamep; char *passwordp; DIR *dirp; FILE *filep; LIST xfilelist; LIST yfilelist; LISTLOOP xfileloop; LISTLOOP yfileloop; MATFIELD *fieldp; MATFIELD *fields; MATRECORD *cursectionp; MATRECORD *detailp; MATRECORD *partialp; MATRECORD *recordp; VERSION *curversionp; VERSION *versionp; XFILE *xfilep; XFILE *yfilep; struct dirent *entryp; struct stat filestat; static struct utsname utsname; static XSOCKET xs; static char string[10+1]; static char inner[80]; static char outer[80]; static char oddpath[256]; static char xmitpath[256]; static char xmitpathfile[256]; static char pendpath[256]; static char pendpathfile[256]; static char sentpath[256]; static char sentpathfile[256]; /**---------------------------------------------------------------------------*/ // initialize dirp = 0; filep = 0; bufferp = 0; boundseq = 0; msgseq = 0; fileseq = 0; lasttime = 0; lines = 0; ListZero(&xfilelist, 8); ListZero(&yfilelist, 8); rv = VersionInit(); if (rv) goto EGRESS; /**---------------------------------------------------------------------------*/ ConsoleInit(); uname(&utsname); // get this computer's name /**---------------------------------------------------------------------------*/ #ifdef TEST usernamep = "idyllic"; servernamep = "sigma"; #else usernamep = "tracm08941"; servernamep = "tracsmail.hud.gov"; #endif passwordp = 0; port = 25; if (npars >= 2) passwordp = pars[1]; if (npars >= 3) { if (!streq(pars[2], "*")) usernamep = pars[2]; } if (npars >= 4) { if (!streq(pars[3], "*")) servernamep = pars[3]; } if (npars >= 5) { if (!streq(pars[4], "*")) { rc = sscanf(pars[4], "%d", &port); if (rc != 1) { LOGF("Error: Unable to parse port parameter \"%s\".", pars[4]); rv = 1; goto EGRESS; } } } if (!passwordp) { printf("usage: smtp [ [ [ ] ] ]\n"); printf(" default user: %s\n", usernamep); printf(" default server: %s\n", servernamep); printf(" default port: %d\n", port); goto EGRESS; } /**---------------------------------------------------------------------------*/ rv = DirectoryVerify(EMAILROOT); IF(rv,Unable to create directory.); strbuild(xmitpath, EMAILROOT, SEP, "xmit", 0); rv = DirectoryVerify(xmitpath); IF(rv,Unable to create directory.); strbuild(oddpath, EMAILROOT, SEP, "odd", 0); rv = DirectoryVerify(oddpath); IF(rv,Unable to create directory.); strappend(oddpath, SEP, DateTimeFormat0(TimeStamp()), 0); strbuild(pendpath, EMAILROOT, SEP, "pend", 0); rv = DirectoryVerify(pendpath); IF(rv,Unable to create directory.); strbuild(sentpath, EMAILROOT, SEP, "sent", 0); rv = DirectoryVerify(sentpath); IF(rv,Unable to create directory.); /**---------------------------------------------------------------------------*/ // scan through files in email\xmit dirp = opendir(xmitpath); IF(!dirp,Unable to open directory.); for (;;) // for each file { entryp = readdir(dirp); if (!entryp) break; filenamep = entryp->d_name; if (filenamep[0] == '.') // if dotted directory entry continue; // ignore strbuild(xmitpathfile, xmitpath, SEP, filenamep, 0); stat(xmitpathfile, &filestat); if (S_ISDIR(filestat.st_mode)) // if directory continue; // ignore /**---------------------------------------------------------------------------*/ // read and parse file LOGF("Parsing file %s", filenamep); rv = FileRead2(xmitpathfile, &bufferp, &nbytes); IF(rv,Unable to read file.); rv = FileLinesParse ( bufferp, nbytes, &lines, &nlines ); IF(rv,Unable to parse file lines.); /**---------------------------------------------------------------------------*/ // extract header information postedp = ""; fromp = ""; top = ""; ccp = ""; subjp = ""; for (line = 0; line < nlines; line++) { linep = lines[line]; if (strieqz(linep, "posted:")) { postedp = Parse(linep); IF(!postedp,Unable to parse Posted: line.); } else if (strieqz(linep, "from:")) { fromp = Parse(linep); IF(!fromp,Unable to parse From: line.); } else if (strieqz(linep, "to:")) { top = Parse(linep); IF(!top,Unable to parse To: line.); } else if (strieqz(linep, "cc:")) { ccp = Parse(linep); IF(!ccp,Unable to parse CC: line.); } else if (strieqz(linep, "subj:")) { subjp = Parse(linep); IF(!subjp,Unable to parse Subj: line.); } } // for (line = 0; line < nlines; line++) /**---------------------------------------------------------------------------*/ // verify file if (!top[0]) { LOGF("File %s has no To: destination. Moving to odd directory.", filenamep); rv = DirectoryVerify(oddpath); IF(rv,Unable to create directory.); rv = FileMove(xmitpath, filenamep, oddpath); IF(rv,Unable to move file.); goto FILENEXT; } if (streqz(top, "TRACS")) // if SprintMail destination goto FILENEXT; // ignore file if // if ( !( // it is not the case that streqz(top, "TRACM") // valid prefix && // and ( // either streq(&top[5], "PROD") // HUD production || // or streq(&top[5], "TEST") // HUD test || // or ( strlen(&top[5]) == 5 // string ends with five characters && // and strisdigit(&top[5]) // the characters are all digits && // and !streq(&top[5], "08941") // not sending to EPS ) ) ) ) { LOGF("File %s is addressed to \"%s\". Moving to odd directory.", filenamep, top); rv = DirectoryVerify(oddpath); IF(rv,Unable to create directory.); rv = FileMove(xmitpath, filenamep, oddpath); IF(rv,Unable to move file.); goto FILENEXT; } /**---------------------------------------------------------------------------*/ // queue file for transmission xfilep = MemGet(sizeof(XFILE)); ListObjAppend(&xfilelist, xfilep); xfilep->filenamep = StrDup(filenamep); xfilep->postedp = StrDup(postedp); xfilep->fromp = StrDup(fromp); xfilep->top = StrDup(top); xfilep->ccp = StrDup(ccp); xfilep->subjp = StrDup(subjp); /**---------------------------------------------------------------------------*/ // create pending file strbuild(pendpathfile, pendpath, SEP, xfilep->filenamep, 0); filep = fopen(pendpathfile, "w"); if (!filep) { LOGF("Unable to create file %s", pendpathfile); rv = 1; goto EGRESS; } for (line = 0; line < nlines; line++) { linep = lines[line]; if ( linep[0] // line has content && ( linep[0] == 0x1a || strieqz(linep, "posted:") || strieqz(linep, "to:") || strieqz(linep, "from:") || strieqz(linep, "cc:") || strieqz(linep, "subj:") || strieqz(linep, "text:") ) ) continue; fprintf(filep, "%s\n", linep); } fclose(filep); filep = 0; /**---------------------------------------------------------------------------*/ // parse file records curversionp = 0; cursectionp = 0; for (line = 0; line < nlines; line++) { linep = lines[line]; RecordParse(linep, &versionp, &recordp, &partialp); if (recordp) // if primary { if ( streq(recordp->recordp, "MAT10") || streq(recordp->recordp, "MAT30") ) { curversionp = versionp; cursectionp = recordp; } continue; } if (partialp) // if partial primary continue; if ( !curversionp || !cursectionp ) continue; DetailParse(linep, curversionp, cursectionp, &detailp, &partialp); if (detailp) { if ( streq(detailp->recordp, "MAT10") && streq(detailp->detailp, "2") ) { fields = curversionp->mat10_2.fields; fieldp = &fields[15]; // contract number memcpyz(xfilep->cno, &linep[fieldp->start], fieldp->len); fieldp = &fields[13]; // project name memcpyz(xfilep->pna, &linep[fieldp->start], fieldp->len); detrail(xfilep->pna); } else if ( streq(detailp->recordp, "MAT30") && streq(detailp->detailp, "2") ) { fields = curversionp->mat30_2.fields; fieldp = &fields[3]; // contract number memcpyz(xfilep->cno, &linep[fieldp->start], fieldp->len); fieldp = &fields[5]; // project name memcpyz(xfilep->pna, &linep[fieldp->start], fieldp->len); detrail(xfilep->pna); fieldp = &fields[35]; // voucher xfilep->amount memcpyz(string, &linep[fieldp->start], fieldp->len); dezero(string); sprintf(xfilep->amount, "$%s.00", string); } continue; } if (partialp) // if partial detail continue; } // for (line = 0; line < nlines; line++) FILENEXT:; MemFree(lines); lines = 0; MemFree(bufferp); bufferp = 0; /**---------------------------------------------------------------------------*/ } // for (;;) // for each file closedir(dirp); dirp = 0; /**---------------------------------------------------------------------------*/ // open connection and initialize socket #ifndef TEST LOGF("Connecting to e-mail server."); rv = PPPOpen("TRACSMail", usernamep, passwordp); IF(rv,Unable to open PPP connection.); LOGF("E-mail server connection established."); #endif rv = SocketInit(&xs, servernamep, port); IF(rv,Unable to initialize socket.); xs.xlog = 0; xs.vlog = 0; /**---------------------------------------------------------------------------*/ // dialog rv = VExpect(&xs, 220, ""); if (rv) goto EGRESS; rv = XVExpect(&xs, 250, "helo %s\r\n", utsname.nodename); if (rv) goto EGRESS; /**---------------------------------------------------------------------------*/ // send files for (;;) { // get up to 26 files with the same header xfilep = ListFirstObjGet(&xfilelist); if (!xfilep) break; postedp = xfilep->postedp; fromp = xfilep->fromp; top = xfilep->top; ccp = xfilep->ccp; subj = xfilep->subjp; LISTLOOPL(xfile) { if ( streq(xfilep->postedp, postedp) && streq(xfilep->fromp, fromp) && streq(xfilep->top, top) && streq(xfilep->ccp, ccp) && streq(xfilep->subjp, subj) ) { ListObjAppend(&yfilelist, xfilep); ListLoopObjDelete(xfile); if (yfilelist.nnodes == 26) break; } } /**---------------------------------------------------------------------------*/ // verify mailbox // HUD sometimes rejects rcpt to: commands with a 550 // complaining about an unavailable account // but all accounts return 251 in response to a vrfy. Shitheads. rv = XVCode(&xs, &code, "vrfy %s@tracsmail.hud.gov\r\n", top); if (rv) goto EGRESS; if ( code != 250 // local user && code != 251 // remote user but will forward ) { LOGF("Mailbox \"%s\" is unavailable or not found.", top); if (code != 550) { LOGF("Received unexpected reply code %d.", code); } LOGF("Moving files to odd directory."); rv = DirectoryVerify(oddpath); IF(rv,Unable to create directory.); LISTLOOPL(yfile) { LOGF("%s", yfilep->filenamep); rv = FileMove(xmitpath, yfilep->filenamep, oddpath); IF(rv,Unable to move file.); rv = FileDelete(pendpath, yfilep->filenamep); IF(rv,Unable to delete file.); } goto NOSEND; } /**---------------------------------------------------------------------------*/ // wait for the timer to update if we've sent 26 messages this second for ( time = TimeStamp(); time == lasttime && msgseq >= 26; time = TimeStamp() ) sleep(1); // reset the sequence counters if the time changed if (time != lasttime) { lasttime = time; msgseq = 0; fileseq = 0; } // build outer and inner boundary strings sprintf ( outer, "outer-outer-outer-outer-%s-%d", DateTimeFormat0(time), boundseq ); sprintf ( inner, "inner-inner-inner-inner-%s-%d", DateTimeFormat0(time), boundseq ); boundseq++; /**---------------------------------------------------------------------------*/ // begin message LOGF("%12s: Starting message with %d attachments", top, yfilelist.nnodes); #ifdef TEST rv = XVExpect(&xs, 250, "mail from: douglass@beta.dumpnet\r\n"); if (rv) goto EGRESS; rv = XVExpect(&xs, 250, "rcpt to: idyllic@sigma.dumpnet\r\n"); if (rv) goto EGRESS; #else rv = XVExpect(&xs, 250, "mail from: tracm08941@tracsmail.hud.gov\r\n"); if (rv) goto EGRESS; //rv = XVExpect(&xs, 250, "rcpt to: %s@tracsmail.hud.gov\r\n", top); //if (rv) goto EGRESS; rv = XVCode(&xs, &code, "rcpt to: %s@tracsmail.hud.gov\r\n", top); if (rv) goto EGRESS; if (code != 250) { LOGF("Mailbox \"%s\" is unavailable or not found.", top); if (code != 550) { LOGF("Received unexpected reply code %d.", code); } LOGF("Moving files to odd directory."); rv = DirectoryVerify(oddpath); IF(rv,Unable to create directory.); LISTLOOPL(yfile) { LOGF("%s", yfilep->filenamep); rv = FileMove(xmitpath, yfilep->filenamep, oddpath); IF(rv,Unable to move file.); rv = FileDelete(pendpath, yfilep->filenamep); IF(rv,Unable to delete file.); } rv = XVExpect(&xs, 250, "rset\r\n"); // undo mail from: command if (rv) goto EGRESS; goto NOSEND; } #endif rv = XVExpect(&xs, 354, "data\r\n"); if (rv) goto EGRESS; #ifdef TEST rv = XLine(&xs, "From: Douglass \r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "To: Idyllic \r\n"); if (rv) goto EGRESS; #else rv = XLine(&xs, "From: \"EPS, Inc.\" \r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "To: %s@tracsmail.hud.gov\r\n", top); if (rv) goto EGRESS; if (ccp[0]) { rv = XLine(&xs, "CC: %s@tracsmail.hud.gov\r\n", ccp); if (rv) goto EGRESS; } #endif if (*subj) rv = XLine(&xs, "Subject: %s\r\n", subj); else rv = XLine(&xs, "Subject: TRACS files\r\n"); if (rv) goto EGRESS; /**---------------------------------------------------------------------------*/ // begin outer blocking rv = XLine(&xs, "MIME-Version: 1.0\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "Content-Type: multipart/mixed;\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "\tboundary=\"%s\"\r\n", outer); if (rv) goto EGRESS; rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "This is a multi-part message in MIME format.\r\n"); if (rv) goto EGRESS; // begin inner blocking #ifdef HTMLMESSAGE rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "--%s\r\n", outer); if (rv) goto EGRESS; rv = XLine(&xs, "Content-Type: multipart/alternative;\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "\tboundary=\"%s\"\r\n", inner); if (rv) goto EGRESS; rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; #endif // plain text message rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; #ifdef HTMLMESSAGE rv = XLine(&xs, "--%s\r\n", inner); #else rv = XLine(&xs, "--%s\r\n", outer); #endif if (rv) goto EGRESS; rv = XLine(&xs, "Content-Type: text/plain;\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "\tcharset=\"us-ascii\"\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "Content-Transfer-Encoding: 7bit\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; rv = MessageText(&xs, &yfilelist, time, msgseq, fileseq, 0); if (rv) goto EGRESS; // HTML formatted message #ifdef HTMLMESSAGE rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "--%s\r\n", inner); if (rv) goto EGRESS; rv = XLine(&xs, "Content-Type: text/html;\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "\tcharset=\"us-ascii\"\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "Content-Transfer-Encoding: 7bit\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; rv = MessageText(&xs, &yfilelist, time, msgseq, fileseq, 1); if (rv) goto EGRESS; // end of inner blocking rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "--%s--\r\n", inner); if (rv) goto EGRESS; #endif /**---------------------------------------------------------------------------*/ // attach files LISTLOOPL(yfile) { LOGF("%12s: Sending file %s as attachment %s", top, yfilep->filenamep, FileName(time, msgseq, fileseq)); strbuild(pendpathfile, pendpath, SEP, yfilep->filenamep, 0); rv = FileRead2(pendpathfile, &bufferp, &nbytes); IF(rv,Unable to read file.); rv = FileLinesParse(bufferp, nbytes, &lines, &nlines); IF(rv,Unable to parse file lines.); // file attachment rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "--%s\r\n", outer); if (rv) goto EGRESS; rv = XLine(&xs, "Content-Type: text/plain;\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "\tname=\"%s\"\r\n", FileName(time, msgseq, fileseq)); if (rv) goto EGRESS; rv = XLine(&xs, "Content-Transfer-Encoding: 7bit\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "Content-Disposition: attachment;\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "\tfilename=\"%s\"\r\n", FileName(time, msgseq, fileseq)); if (rv) goto EGRESS; rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; // don't do this, HUD regenerates the Posted: line #if 0 if (*(yfilep->postedp)) { rv = XLine(&xs, "Posted: %s\r\n", yfilep->postedp); if (rv) goto EGRESS; rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; } #endif for (line = 0; line < nlines; line++) { linep = lines[line]; if (streq(linep, ".")) linep = ".."; rv = XLine(&xs, "%s\r\n", linep); if (rv) goto EGRESS; rv = XWait(&xs); // wait for line to be transmitted if (rv) goto EGRESS; } MemFree(lines); MemFree(bufferp); fileseq++; } // LISTLOOPL(yfile) /**---------------------------------------------------------------------------*/ // end of outer blocking rv = XLine(&xs, "\r\n"); if (rv) goto EGRESS; rv = XLine(&xs, "--%s--\r\n", outer); if (rv) goto EGRESS; rv = XLine(&xs, ".\r\n"); // end of message if (rv) goto EGRESS; rv = VExpect(&xs, 250, ""); if (rv) goto EGRESS; /**---------------------------------------------------------------------------*/ // at this point the message is sent LOGF("%12s: Message complete", top); LISTLOOPL(yfile) { strbuild(sentpathfile, sentpath, SEP, yfilep->filenamep, 0); filep = fopen(sentpathfile, "wb"); if (!filep) { LOGF("Unable to create file %s", sentpathfile); rv = 1; goto EGRESS; } if (*(yfilep->postedp)) fprintf(filep, "Posted: %s\r\n", yfilep->postedp); if (*(yfilep->fromp)) fprintf(filep, "From: %s\r\n", yfilep->fromp); if (*(yfilep->top)) fprintf(filep, "To: %s\r\n", yfilep->top); if (*(yfilep->ccp)) fprintf(filep, "CC: %s\r\n", yfilep->ccp); if (*(yfilep->subjp)) fprintf(filep, "Subj: %s\r\n", yfilep->subjp); fprintf(filep, "\r\n"); fclose(filep); rv = FileCat(pendpath, yfilep->filenamep, sentpath); IF(rv,Unable to move file.); #ifndef NOXMITDELETE rv = FileDelete(xmitpath, yfilep->filenamep); IF(rv,Unable to delete file.); #endif } NOSEND: LISTLOOPL(yfile) XFileFree(yfilep); ListFree(&yfilelist); msgseq++; fileseq = 0; } // for (;;) message loop // at this point all messages are sent /**---------------------------------------------------------------------------*/ // disconnect and exit rv = XVExpect(&xs, 221, "quit\r\n"); if (rv) goto EGRESS; /**---------------------------------------------------------------------------*/ // close socket and hang up EGRESS: rc = SocketExit(&xs); if (rc) { LOGF("Unable to exit socket."); } #ifndef TEST LOGF("Disconnecting from e-mail server."); rc = PPPClose(); if (rc) { LOGF("Unable to close PPP connection."); } #endif /**---------------------------------------------------------------------------*/ // delete any remaining pending files dirp = opendir(pendpath); if (dirp) { for (;;) // for each file { entryp = readdir(dirp); if (!entryp) break; filenamep = entryp->d_name; if (filenamep[0] == '.') // if dotted directory entry continue; // ignore strbuild(pendpathfile, pendpath, SEP, filenamep, 0); stat(pendpathfile, &filestat); if (S_ISDIR(filestat.st_mode)) // if directory continue; // ignore FileDelete(pendpath, filenamep); // don't check return code } closedir(dirp); dirp = 0; } /**---------------------------------------------------------------------------*/ ConsoleExit(); if (bufferp) MemFree(bufferp); if (filep) fclose(filep); if (lines) MemFree(lines); if (bufferp) MemFree(bufferp); if (dirp) closedir(dirp); LISTLOOPL(xfile) XFileFree(xfilep); ListFree(&xfilelist); LISTLOOPL(yfile) XFileFree(yfilep); ListFree(&yfilelist); MemVerify(); RETURN(rv); }