home *** CD-ROM | disk | FTP | other *** search
- /******** SMTP **********
- * Client process for *
- * Simple Mail Transfer *
- * Protocol *
- * for the IBM PC *
- *************************/
-
- /* Copyright 1987, 1988 by USC Information Sciences Institute (ISI) */
- /* See permission and disclaimer notice in file "isi-note.h" */
- /*#include "isi-note.h" */
-
- /* Copyright 1984 by the Massachusetts Institute of Technology */
- /* See permission and disclaimer notice in file "notice.h" */
- /* #include <notice.h> */
-
- /*#2 4/16/84 Change do_data to do netascii newline xformations. <DMC>
- *
- *#12 Fixup for loop logic in expect. 5/29/84 dmc
- *
- *#14 Increase timeout and improve handling. 5/31/84 dmc
- *
- *#16 In tgets, don't allocate resptime every call, do it in main instead;
- * increase RESPTIME if dbg is on. 6/5/84 dmc
- *
- *#19 Replace wakes of TCPsend with calls to tcp_ex(), which does PUSH.
- * 6/11/84 dmc
- *
- *#84362 Get mail-from field out of mail file instead of cmd line
- *
- *#85028 Init cirput,cirget to cirbuf!!! Also replace "short timeout" msg
- *#85035 Init termcode early in main
- *
- *Added some #includes so it'll compile under Microsoft C 3.0. - koji
- *
- * 3/1/1991 JDM changed code substantially to read lists of files to
- * post and change the way the "to" and "from" lines are handled. The
- * previous code for this was bizarre to say the least.
- */
-
- /* NEEDS THE PCIP PACKAGE (Harvard flavor) TO LINK!!!! */
-
- /* smtp.c
- *
- * User mode smtp. Takes a source and a recipient and tries to
- * deliver the mail to each. Writes the returned error codes out to the
- * standard output for the use of its caller. Note that this routine uses
- * the small tcp and hence must run under tasking.
- *
- * Calling sequence:
- * smtp <mail file> <dest-host> <dbg>
- */
-
- /* The mail file begins with a single "From: x@a.b.c.d" line and has
- * as many "To: y@q.w.e.r" lines as desired
- */
-
-
- typedef long time_t; /* ugly! */
-
- #include <stdio.h>
- #include <types.h>
- #include <netq.h>
- #include <task.h>
- #include <ip.h>
- #include <timer.h>
- #include <custom.h>
- #include <string.h>
-
- #define NONE 0 /* no such command */
- #define HELO 1
- #define MAIL 2
- #define RCPT 3
- #define DATA 4
- #define QUIT 5
- #define RSET 6
- #define NOOP 7
-
- struct cmdtab {
- char *c_name; /* command name */
- int c_len; /* command length */
- } cmdtab[] = {
- { "", 0, },
- { "HELO", 4, },
- { "MAIL FROM:", 10 },
- { "RCPT TO:", 8, },
- { "DATA", 4, },
- { "QUIT", 4, },
- { "RSET", 4, },
- { "NOOP", 4, },
- { 0, 0, } /* end of table marker */
- };
-
-
- #define SMTPSOCK 25 /* smtp server socket */
- #define WINDOW 1000 /* seems reasonable */
- #define RESPTIME 30 /*#14 maximum response timeout */
- #define BUFSIZ 512
-
- char cmdbuf[BUFSIZ]; /* command buffer */
- char mpath[BUFSIZ]; /*#84362 recipient buffer */
- int buflen; /* size of string in cmd buffer */
- char cirbuf[BUFSIZ]; /* circular buffer for cmd input */
- char *cirput; /* put ptr for circular buf */
- char *cirget; /* get ptr for circular buf */
- char termbuf[BUFSIZ]; /* termination line */
- char *smtpaddr; /* Global holder for rmt host argv */
- int termcode; /* program termination code */
- int dbg; /* debugging flag */
- int success; /* successful transfer flag */
- task *tk_smtp; /* the smtp user task */
- timer *resptime; /* current response timer */
- event conn_open; /* connection open event */
- event in_avail; /* input available event */
- event spc_avail; /* output buffer space available */
- event rsptmo; /* response timeout event */
-
- extern int cmd_rcv(), sm_tmo();
- extern int us_opna(), us_cls(), us_tmo(), bfr(), us_yld(), pr_dot();
-
-
- /* Insure that the mail file is readable, then open a tcp connection to
- * the server smtp at the destination site. If successful, send the
- * mail by calling the routine do_cmds().
- */
-
- main(argc, argv)
- int argc;
- char **argv; {
- FILE *mlfd, *lffd; /* mail file desc */
- in_name fhost; /* foreign host address */
- int flag; /* debug flags */
- char fbuf[128];
- int i;
- char *ptr;
-
- if(argc < 3 || argc > 4) {
- printf("501 usage: smtp <file> <dest-host> [dbg]\n");
- exit(1);
- }
-
- if(argc == 4) {
- flag = atoi(argv[3]);
- dbg = flag & 1;
- printf("Debug mode: %d\n",dbg);
- }
-
- if((lffd = fopen(argv[1], "ra")) == NULL) {
- printf("554 mail file unreadable\n");
- exit(1);
- }
-
- termcode = 1; /*#85035 Assume failure */
- tcp_init(512, us_opna, cmd_rcv, us_yld, us_cls, us_tmo, pr_dot, bfr);
-
- smtpaddr = argv[2];
- if((fhost = resolve_name(smtpaddr)) == 0L) {
- printf("550 Never heard of host %s\n", smtpaddr);
- exit(1);
- }
-
-
- resptime = tm_alloc(); /*#16 Do this once only, here */
- cirput = cirget = cirbuf; /*#85028 Init these, too */
- tk_smtp = tk_cur;
-
- tcp_open(&fhost, SMTPSOCK, tcp_sock(), WINDOW, WINDOW/2);
- while(!conn_open)
- tk_block();
- conn_open = 0;
- expect(220); /* expect a service ready msg */
-
- if(strstr(argv[1],"sendmail.lst") == NULL) {
- do_cmds(lffd);
- fclose(lffd);
- } else {
- while(1){
- if(fgets(fbuf,126,lffd) == NULL)break;
- ptr = fbuf;
- while(*ptr == ' ')ptr++;
- for(i = 0; i < 128 && ptr[i]; i++)if(ptr[i] == '\n')ptr[i] = 0;
- if((mlfd = fopen(ptr,"r")) == NULL)continue;
- do_cmds(mlfd);
- remove(ptr);
- }
- fclose(lffd);
- remove(argv[1]);
- }
-
-
-
-
- tputs("QUIT\n");
- expect(221);
- termcode = 0;
- tcp_close();
- close_wait(RESPTIME);
- }
-
-
- /* Do the necessary commands for a smtp transfer. Start by waiting for the
- * connection to open, then send HELO, MAIL, RCPT, and DATA. Check the
- * reply codes and give up if needed.
- */
-
- do_cmds(mlfd)
- FILE *mlfd; { /* mail file descriptor */
-
- tputs("HELO ");
- tputs(custom.c_user); /* Use name from here */
- tputs("\n");
- expect(250); /* expect an OK */
-
- if(mparse_path(mlfd,'f') != 1) {
- printf("didn't find \"From:\" \n");
- }
- fseek(mlfd,0l,0);
- if(mparse_path(mlfd,'t') == 0){
- printf("didn't find \"To:\" \n");
- }
- fseek(mlfd,0l,0);
-
- tputs("DATA\n");
-
- expect(354);
- printf("DATA\n");
- do_data(mlfd);
-
- printf("data sent\n");
-
- expect(250);
-
-
- success = TRUE;
- }
-
-
- int mparse_path(mfile, flag)
- register FILE *mfile;
- int flag;
-
- {
- char buf[256];
- register char *p, *q;
- int i, r;
- char c;
-
- i = 0;
- while(1) {
- if(fgets(buf,254,mfile) == NULL)return i;
- if(*buf == '\n') return i;
-
- if(flag == 'f') {
- if(strnicmp("From:",buf,5) == 0){
- p = &buf[5];
- while(*p == ' ')p++;
- q = p;
- while(*q != ' ' && *q != '\n' && *q != 0)q++;
- if (p == q) continue;
- *q = 0;
- tputs("MAIL FROM:<");
- tputs(p);
- tputs(">\n");
- printf("MAIL FROM:<%s>\n",p);
- expect(250);
- return 1;
- }
- } else {
- if(strnicmp("To:",buf,3) == 0){
- p = &buf[3];
- while(*p == ' ')p++;
- q = p;
- while(*q != ' ' && *q != '\n' && *q != 0)q++;
- if (p == q) continue;
- *q = 0;
- tputs("RCPT TO:<");
- tputs(p);
- tputs(">\n");
- printf("RCPT TO:<%s>\n",p);
- expect(250);
- i++;
- }
- }
- }
- }
-
-
- /* Send the data from the specified mail file out on the current smtp
- * connection. Do the appropriate netascii conversion and starting '.'
- * padding. Send the <CRLF>.<CRLF> at completion.
- */
-
- do_data(fd)
- register FILE *fd; { /* mail file descriptor */
- register char c; /* current character */
- extern task *TCPsend;
- event sendef;
-
- while((c = getc(fd)) != EOF) { /*#2 Do netascii xformations */
- if(c == '\n')
- tputc('\r');
- tputc(c);
- if(c == '\r')
- tputc('\0');
- }
-
- tputc('\r');
- tputc('\n');
- tputc('.');
- tputc('\r');
- tputc('\n');
-
- tcp_ex(); /*#19 make sure it gets sent */
- }
-
-
- expect(code)
-
- /* Expect a reply message with the specified code. If the specified code
- * is received return TRUE; otherwise print the error message out on the
- * standard output and give up. Note that the reply can be a multiline
- * message.
- *
- * Arguments:
- */
-
- int code;
- {
- int retcd;
- char *data = cmdbuf;
- int i,errtmo; /*#16 Add errtmo to vary timeout */
-
- for(;;) { /* get whole reply */
- errtmo = (dbg) ? 4*RESPTIME : RESPTIME; /*#16 longer if dbg */
- if(tgets(cmdbuf, errtmo) > 0) { /*#16 get input line */
- if(cmdbuf[3] == '-') /* continuation line? */
- continue;
-
- for(i=0; i<30; i++)
- if(*data >= '0' && *data <= '9') break;
- else data++;
-
- retcd = atoi(data);
- /* sscanf(cmdbuf, "%d", &retcd); no, last line */
- if(retcd == code) /* If equal, */
- return; /* just return */
- i = code - code % 100; /* else check if */
- /* in right range */
- if ( retcd >= i && retcd <= i+99)
- return; /* It is */
-
- if(dbg)
- printf("Expected: %u, got %u.\n",code, retcd);
- strcpy(termbuf, cmdbuf); /* return the error line */
- tputs("QUIT\n");
- break; /*#12 Get out of for loop */
-
- } else if(success) { /* Here if not tgets(... */
- if(dbg) printf("Expect: Couldnt get input line, but transfer successful\n"); /*#12*/
- strcpy(termbuf, "250 OK\n");
- return; /*#12 Get out of for loop */
- }
- else { /* Here if not success */
- if (dbg) printf("Expect: TGets failed\n");/*#14*/
- strcpy(termbuf, "451 Receive timeout\n");
- break;
- }
- } /* End for(;;) */
- termcode = !success; /* error return */
- tcp_close();
- close_wait(RESPTIME);
- }
-
-
- us_opna()
-
- /* Called by the tcp receiver task when a connection is successfully opened.
- * Just wakes up the smtp task and returns
- */
- {
- tk_setef(tk_smtp, &conn_open);
- }
-
-
- cmd_rcv(buf, len, urg)
-
- /* Called by the tcp receive task on receipt of a command from the net
- * Just copy the data into the circular buffer and wake up the smtp task
- * to process the data.
- *
- * Arguments:
- */
-
- char *buf; /* data buffer */
- int len; /* data length */
- int urg; /* urgent ptr(unused) */
- {
- register char *p, *q;
- register int i;
-
- for(p = cirput, q = buf, i = len; i > 0; i--) { /* copy the data */
- if(p == &cirbuf[BUFSIZ])
- p = cirbuf;
- *p++ = *q++;
- }
-
- cirput = p;
-
- tk_setef(tk_smtp, &in_avail);
- }
-
-
- tgets(buf, tmo)
-
- /* Wait for a full command line to be input. As the characters come in,
- * they are copied into the circular buffer. This routine takes them
- * out and copies them into the specified buffer for processing. It
- * performs the netascii conversion on the fly. Returns the number of
- * characters input, or -1 on response timeout.
- *
- * Arguments:
- */
-
- char *buf; /* buffer address */
- int tmo; /* response timeout */
- {
- register char *p, *q; /* temp pointers */
- register int i; /* length counter */
- static int crseen = FALSE; /* for netascii conversion */
-
- for(q = buf, i = 0;;) {
- for(p = cirget; p != cirput; p++) {
- if(p == &cirbuf[BUFSIZ])
- p = cirbuf;
- if(crseen) { /* need to de-netascify */
- crseen = FALSE;
- if(*p == '\n') {
- *q++ = '\n'; /* end of line */
- *q = '\0'; /* null terminate */
- i++;
- cirget = ++p; /* clean up */
- if(dbg)
- printf("%s", buf);
- return(i);
- } else {
- *q++ = '\r';
- i++;
- if(*p != '\0') {
- *q++ = *p;
- i++;
- }
- }
- } else if(*p == '\r') {
- crseen = TRUE;
- } else {
- *q++ = *p;
- i++;
- }
- }
-
- cirget = p; /* fix get ptr. back up */
-
- /* Full line not in yet; wait for character input */
-
- tm_set(tmo, sm_tmo, 0, resptime);
-
- for(;;) {
- if(in_avail) {
- in_avail = 0;
- rsptmo = 0; /*#14 In case both */
- tm_clear(resptime);
- break;
- }
- if(rsptmo) {
- rsptmo = 0;
- return(-1); }
- tk_block();
- }
- }
- }
-
-
- tputs(str)
-
- /* Send the specified string out to the net. Do the appropriate netascii
- * conversion as it goes.
- *
- * Arguments:
- */
-
- register char *str;
- {
- extern task *TCPsend;
- event sendef;
-
- if(dbg)
- printf("%s", str);
-
- for(; *str != '\0'; str++) {
- if(*str == '\n')
- tputc('\r');
- tputc(*str);
- if(*str == '\r')
- tputc('\0');
- }
-
- tcp_ex(); /*#19 make sure it gets sent */
- }
-
-
- /* Put the specified character out to tcp. If the output buffer is full,
- * block until reawakened by the tcp (via the us_space routine).
- */
-
- tputc(c)
- char c; { /* character to output */
-
- while(tc_put(c)) tk_block();
- }
-
-
- us_tmo() {
- printf("451 Host not responding\n");
- exit(1);
- }
-
-
- sm_tmo() {
- tk_setef(tk_smtp, &rsptmo);
- }
-
- /* Called by the tcp layer when space becomes available in the tcp output
- * buffer. Just wakes up the smtp task if it's asleep.
- */
-
- bfr() {
-
- tk_setef(tk_smtp, &spc_avail);
- }
-
- pr_dot() {
- printf(".");
- }
-
-
- us_cls() {
- int opening=0, ourclosing=0, hisclosing=0;
-
- /* if(termbuf[0] == '\0')
- printf("450 termbuf null.\n success = %d, opening = %d,\
- ourclosing = %d, hisclosing = %d\n", success, opening, ourclosing, hisclosing);
- else
- printf("%s", termbuf);
- */ exit(termcode);
- }
-
- us_yld() {
- return 0;
- }
-
- close_wait(tmo)
- int tmo; {
-
- rsptmo = 0;
- tm_set(tmo, sm_tmo, 0, resptime);
- while(!rsptmo)
- tk_block(); /* don't come back */
- printf("Timed out waiting for close\n");
- exit(termcode);
- }
-