# /* EMACS_MODES: c !fill */ /* 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 */ typedef long time_t; /* ugly! */ #include #include #include #include #include #include #include #include "task.h" #include "timer.h" #include "cmds.h" #define SMTPSOCK 25 /* smtp server socket */ #define WINDOW 1000 /* seems reasonable */ #define RESPTIME 240 /* maximum response timeout */ char cmdbuf[BUFSIZ]; /* command buffer */ int buflen; /* size of string in cmd buffer */ char cirbuf[BUFSIZ]; /* circular buffer for cmd input */ char *cirput = cirbuf; /* put ptr for circular buf */ char *cirget = cirbuf; /* get ptr for circular buf */ char termbuf[BUFSIZ]; /* termination line */ int termcode; /* program termination code */ int dbg; /* debugging flag */ int success; /* successful transfer flag */ task *tk_smtp; /* the smtp user task */ timerid 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 (), us_space(), us_fcls (); main (argc, argv) /* 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(). */ int argc; char **argv; { FILE *mlfd; /* mail file desc */ in_name fhost; /* foreign host address */ int flag; /* debug flags */ if (argc < 5 || argc > 6) { printf ("501 usage: %s [dbg]\n", argv[0]); exit (1); } if (argc == 6) { sscanf (argv[5], "%d", &flag); dbg = flag & 1; } if ((mlfd = fopen (argv[1], "r")) == NULL) { printf ("554 mail file unreadable\n"); exit (1); } if ((fhost = resolve_name (argv[3])) == 0L) { printf ("550 Never heard of host %s\n", argv[3]); exit (1); } if (!tcp_init (512, 0, us_opna, cmd_rcv, us_cls, us_fcls, us_tmo, us_space)) { printf ("554 Can't initialize tcp\n"); exit (1); } tk_smtp = tk_cur; if (flag & 2) tcpdebug (TRUE); if (!tcp_open (fhost, SMTPSOCK, WINDOW)) { printf ("554 Connection in use\n"); exit (1); } do_cmds (argv[2], argv[4], mlfd); fclose (mlfd); strcpy (termbuf, "250 OK\n"); /* successful termination */ termcode = 0; tcp_close (); tm_set (RESPTIME, sm_tmo, 0, &resptime); tst_and_clr (&rsptmo); while (!tst_and_clr (&rsptmo)) tk_block (); us_cls (); } do_cmds (from, rcpt, mlfd) /* 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. * * Arguments: */ char *from; /* from address */ char *rcpt; /* to address */ FILE *mlfd; /* mail file descriptor */ { while (!tst_and_clr (&conn_open)) tk_block (); expect (220); /* expect a service ready msg */ tputs ("HELO MIT-CSR.MIT\n"); expect (250); /* expect an OK */ tputs ("MAIL FROM:<"); parse_path (from); tputs (">\n"); expect (250); /* expect OK */ tputs ("RCPT TO:<"); parse_path (rcpt); tputs (">\n"); expect (250); /* expect OK */ tputs ("DATA\n"); expect (354); do_data (mlfd); expect (250); success = TRUE; tputs ("QUIT\n"); expect (221); } do_data (fd) /* Send the data from the specified mail file out on the current smtp * connection. Do the appropriate netascii conversion and starting '.' * padding. Send the . at completion. * * Arguments: */ register FILE *fd; /* mail file descriptor */ { register char c; /* current character */ int nlseen = FALSE; /* newline */ extern task *TCPsend; extern event sendef; while ((c = getc (fd)) != EOF) { if (nlseen) { nlseen = FALSE; if (c == '.') tputc ('.'); } if (c == '\n') { tputc ('\r'); nlseen = TRUE; } tputc (c); if (c == '\r') tputc ('\0'); } if (!nlseen) { tputc ('\r'); tputc ('\n'); } tputc ('.'); tputc ('\r'); tputc ('\n'); tk_setef (TCPsend, &sendef); /* 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; for (;;) { /* get whole reply */ if (tgets (cmdbuf, RESPTIME) > 0) { /* get input line */ if (cmdbuf[3] == '-') /* continuation line? */ continue; sscanf (cmdbuf, "%d", &retcd); /* no, last line */ if (retcd == code) return; else { strcpy (termbuf, cmdbuf); /* return the error line */ tputs ("QUIT\n"); break; } } else if (success) strcpy (termbuf, "250 OK\n"); else { strcpy (termbuf, "451 Host not responding\n"); break; } } tcp_close (); termcode = !success; /* error return */ tm_set (RESPTIME, sm_tmo, 0, &resptime); tst_and_clr (&rsptmo); while (!tst_and_clr (&rsptmo)) tk_block (); us_cls (); } 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 (tst_and_clr (&in_avail)) { tm_clear (&resptime); break; } if (tst_and_clr (&rsptmo)) 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; extern event sendef; if (dbg) printf ("%s", str); for (; *str != '\0'; str++) { if (*str == '\n') tputc ('\r'); tputc (*str); if (*str == '\r') tputc ('\0'); } tk_setef (TCPsend, &sendef); } tputc (c) /* Put the specified character out to tcp. If the output buffer is full, * block until reawakened by the tcp (via the us_space routine). * * Arguments: */ 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); } us_space () /* Called by the tcp layer when space becomes available in the tcp output * buffer. Just wakes up the smtp task if it's asleep. */ { tk_setef (tk_smtp, &spc_avail); } us_cls () { extern int opening, ourclosing, hisclosing; if (termbuf[0] == '\0') { printf ("450 termbuf null. Success = %d, opening = %d,\ ourclosing = %d, hisclosing = %d\n", success, opening, ourclosing, hisclosing); termcode = 1; } else printf ("%s", termbuf); exit (termcode); } us_fcls () { }