/* * Talk to sendmail. * * See rfc 821. */ /* * sm_conn() - connect to smtp. * 1: sm_mail_from(char *) - tell who the mail is from. * 2: sm_rcpt_to(char *) - add one recipient. * sm_data() - mark end of recipients. * 3: sm_putc(int) - add another character to the letter. * sm_send - send the letter. * * Line 2 and 3 can be repeated arbitrarily many times. When * sm_send() has been called the state machine returns to state 1 * and is ready to accept a new sm_mail_from. */ #include #include #include #include #include #include "sendmail-if.h" const int TIMEOUT=10000; /* 10 seconds. */ const int TIMEOUT_RETRY = 18; /* Number of retries. */ const char *SMTP_HOST = "lysator.liu.se"; static IscMaster *master = NULL; static IscSession *session; static enum { /* Possible next states: */ DIG0_LINE0, /* DIG1_LINE0. Remember the digit. */ DIG1_LINE0, /* DIG2_LINE0 */ DIG2_LINE0, /* DELIM_LINE0 */ DELIM_LINE0, /* JUNK_LASTLINE (if char == ' ') */ /* JUNK_LINEN (if char == '-') */ JUNK_LINEN, /* LF_LINEN (if char == '\r') */ /* JUNK_LINEN (if char != `\r') */ LF_LINEN, /* DIG0_LINEN */ DIG0_LINEN, /* DIG1_LINEN */ DIG1_LINEN, /* DIG2_LINEN */ DIG2_LINEN, /* DELIM_LINEN */ DELIM_LINEN, /* JUNK_LINEN (fi char == '-') */ /* JUNK_LASTLINE (if char == ' ') */ JUNK_LASTLINE, /* LF_LASTLINE (if char == '\r') */ /* JUNK_LASTLINE (if char != '\r`) */ LF_LASTLINE, /* EXIT. */ } state; /* * Handle one message. Return values: 0 - need another packet. -1 - * error. Any other - first digit in reply from smtp. */ static int sm_handle_message(IscEvent *event, char *errmsg) { char *s; int len; static int dig0_line0 = 0; s = event->msg->buffer; len = event->msg->length; putchar('>'); while (len > 0) { putchar(*s); switch(state) { case DIG0_LINE0: if (isdigit(*s)) dig0_line0 = *s - '0'; else { fprintf(stderr, "First digit in reply was '%c'!\n", *s); return -1; } state = DIG1_LINE0; break; case DIG1_LINE0: state = DIG2_LINE0; break; case DIG2_LINE0: state = DELIM_LINE0; break; case DELIM_LINE0: switch (*s) { case ' ': state = JUNK_LASTLINE; break; case '-': state = JUNK_LINEN; break; default: fprintf(stderr, "reg: Illegal smtp delimiter.\n"); return -1; } break; case JUNK_LINEN: if (*s == '\r') state = LF_LINEN; break; case LF_LINEN: if (*s == '\n') state = DIG0_LINEN; else state = JUNK_LINEN; break; case DIG0_LINEN: state = DIG1_LINEN; break; case DIG1_LINEN: state = DIG2_LINEN; break; case DIG2_LINEN: state = DELIM_LINEN; break; case DELIM_LINEN: switch (*s) { case ' ': state = JUNK_LASTLINE; break; case '-': state = JUNK_LINEN; break; default: fprintf(stderr, "reg: Illegal smtp delimiter.\n"); return -1; } break; case JUNK_LASTLINE: if (*s == '\r') state = LF_LASTLINE; break; case LF_LASTLINE: if (*s == '\n') { if (len != 1) fprintf(stderr, "reg: warning: too long smtp message.\n"); if (dig0_line0 != 2 && dig0_line0 != 3) { fputs(errmsg, stderr); putc('\n', stderr); /* FIXME - should write smtp reply also. */ } return dig0_line0; } else state = JUNK_LINEN; break; } s++; len--; } putchar('<'); putchar('\n'); return 0; } /* * Parse a reply from the remote SMTP. Return the code of the first digit. * Write the reply and the errmsg to stderr unless the code is 2 or 3. * Returns -1 in case of error or timeout. */ static int sm_parse(char *errmsg) { IscEvent *event; int val = 0; int retries_left = TIMEOUT_RETRY; isc_flush(session); state = DIG0_LINE0; while(val == 0) { event = isc_getnextevent(master, TIMEOUT); switch (event->event) { case ISC_EVENT_CONNECTED: fprintf(stderr, "reg: got ISC_EVENT_CONNECTED.\n"); break; case ISC_EVENT_REJECTED: fprintf(stderr, "reg: got ISC_EVENT_REJECTED.\n"); abort(); case ISC_EVENT_ERROR: if (errno != EINTR) { fprintf(stderr, "reg: ISC_EVENT_ERROR (error = '%s'):\n" " '%s'\n" " %s\n", strerror(errno), event->msg ? event->msg->buffer : "(unknown)", errmsg); val = -1; } break; case ISC_EVENT_TIMEOUT: if (retries_left-- <= 0) { fprintf(stderr, "reg: timeout talking to smtp. '%s'.\n", errmsg); val = -1; } break; case ISC_EVENT_LOGIN: case ISC_EVENT_LOGOUT: fprintf(stderr, "reg: got login/logout. Impossible.\n"); val = -1; case ISC_EVENT_MESSAGE: val = sm_handle_message(event, errmsg); retries_left = TIMEOUT_RETRY; break; } isc_dispose(event); } return val; } /* * sm_conn() - connect to the sendmail daemon. */ int sm_conn(void) { char hostname[MAXHOSTNAMELEN+1]; char domainname[65]; gethostname(hostname, MAXHOSTNAMELEN); getdomainname(domainname, 64); master = isc_initialize(NULL); if (master == NULL) { fprintf(stderr, "isc_initialize() failed.\n"); return -1; } session = isc_opentcp(master, SMTP_HOST, "smtp"); if (session == NULL) { fprintf(stderr, "isc_opentcp() failed.\n"); return -1; } if (sm_parse("Remote SMTP refused service.\n") != 2) return -1; isc_printf(session, "HELO %s%s\r\n", hostname, domainname); if (sm_parse("Remote SMTP don't accept HELO.") != 2) return -1; return 0; } /* * Give a MAIL FROM: command, and check the reply. */ int sm_mail_from(char *sender) { isc_printf(session, "MAIL FROM: <%s>\r\n", sender); if (sm_parse("Remot SMTP don't accept MAIL FROM.") != 2) return -1; return 0; } /* * Give a RCPT TO: command, and check the reply. */ int sm_rcpt_to(char *recipient) { isc_printf(session, "RCPT TO: <%s>\r\n", recipient); if (sm_parse("Remot SMTP don't accept recipient.") != 2) return -1; return 0; } /* * Give DATA command, and check the reply. */ static int bol; /* Beginning of line. */ int sm_data(void) { isc_printf(session, "DATA\r\n"); if (sm_parse("Remot SMTP don't accept DATA.") != 3) return -1; bol = 1; return 0; } /* * Output a character. * * A dot first on a line is quoted by another dot. Newlines * are converted to CRLF:s. */ void sm_putc(int c) { if (c=='.' && bol) isc_putc('.', session); if (c=='\n') { isc_putc('\r', session); bol = 1; } else bol = 0; isc_putc(c, session); } /* * Output a string. * * Repeatedly call sm_putc until a NUL character is encountered. No * newline are added. */ void sm_puts(char *s) { while(*s != '\0') sm_putc(*s++); } /* * Send the mail. */ int sm_send(void) { if (!bol) { isc_putc('\r', session); isc_putc('\n', session); } isc_putc('.', session); isc_putc('\r', session); isc_putc('\n', session); if (sm_parse("SMTP rejected data section.") != 2) return -1; return 0; } const char * strerror(int num) { static char foo[256]; sprintf(foo, "Error #%d", num); return foo; }