Index: networking/Kbuild =================================================================== --- networking/Kbuild (revision 19393) +++ networking/Kbuild (working copy) @@ -28,6 +28,7 @@ lib-$(CONFIG_PSCAN) += pscan.o lib-$(CONFIG_ROUTE) += route.o lib-$(CONFIG_SLATTACH) += slattach.o +lib-$(CONFIG_SENDMAIL) += sendmail.o lib-$(CONFIG_TELNET) += telnet.o lib-$(CONFIG_TELNETD) += telnetd.o lib-$(CONFIG_TFTP) += tftp.o Index: networking/Config.in =================================================================== --- networking/Config.in (revision 19393) +++ networking/Config.in (working copy) @@ -564,6 +564,13 @@ help slattach is a small utility to attach network interfaces to serial lines. +config SENDMAIL + bool "sendmail" + default n + help + Utility to simply forward mail to a configured mailhost. Config file + is /etc/ssmtp/ssmtp.conf + config TELNET bool "telnet" default n Index: networking/sendmail.c =================================================================== --- networking/sendmail.c (revision 0) +++ networking/sendmail.c (revision 0) @@ -0,0 +1,1999 @@ +/* + * Copyright (C) + David Collier-Brown, davecb@hobbes.ss.org, davecb@sni.ca or dave@lethe.uucp + Christoph Lameter, clameter@debian.org, clameter@waterf.org, clameter@i-m-f.org + Hugo Haas, hugo@debian.org, hugo@larve.net, hugo@via.ecp.fr + Matt Ryan, mryan@debian.org, matt.ryan@banana.org.uk + +*/ +#include +#include +#include + +#define BUF_SZ (1024 * 2) /* A pretty large buffer, but not outrageous */ + +#define MAXWAIT (10 * 60) /* Maximum wait between commands, in seconds */ +#define MEDWAIT (5 * 60) + +#define MAXSYSUID 999 /* Highest UID which is a system account */ + +#ifndef _POSIX_ARG_MAX +#define MAXARGS 4096 +#else +#define MAXARGS _POSIX_ARG_MAX +#endif + +#define REVALIASES_FILE "/etc/ssmtp/revaliases" +#define SSMTPCONFDIR "/etc/ssmtp" +#define CONFIGURATION_FILE SSMTPCONFDIR "/ssmtp.conf" + + +/* + * base64.c -- base-64 conversion routines. + * + * For license terms, see the file COPYING in this directory. + * + * This base 64 encoding is defined in RFC2045 section 6.8, + * "Base64 Content-Transfer-Encoding", but lines must not be broken in the + * scheme used here. + */ + +/* + * This code borrowed from fetchmail sources by + * Eric S. Raymond . + */ +#include + + +static const char base64digits[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define BAD -1 +static const char base64val[] = { + BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD, 62, BAD,BAD,BAD, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,BAD,BAD, BAD,BAD,BAD,BAD, + BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,BAD, BAD,BAD,BAD,BAD, + BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,BAD, BAD,BAD,BAD,BAD +}; +#define DECODE64(c) (isascii(c) ? base64val[c] : BAD) + +static void to64frombits(unsigned char *out, const unsigned char *in, int inlen) +/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ +{ + for (; inlen >= 3; inlen -= 3) + { + *out++ = base64digits[in[0] >> 2]; + *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; + *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; + *out++ = base64digits[in[2] & 0x3f]; + in += 3; + } + if (inlen > 0) + { + unsigned char fragment; + + *out++ = base64digits[in[0] >> 2]; + fragment = (in[0] << 4) & 0x30; + if (inlen > 1) + fragment |= in[1] >> 4; + *out++ = base64digits[fragment]; + *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c]; + *out++ = '='; + } + *out = '\0'; +} + +/* base64.c ends here */ +/* + * arpadate.c - get_arpadate() is a function returning the date in the + * ARPANET format (see RFC822 and RFC1123) + * + * Inspired by smail source code by Ronald S. Karr and Landon Curt Noll + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define ARPADATE_LENGTH 32 + +#include +#include +#include + +static void +get_arpadate (char *d_string) +{ + struct tm *date; +#ifdef USE_OLD_ARPADATE + static char *week_day[] = + {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + static char *month[] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec"}; + static char timezone[3]; + + time_t current; + int offset, gmt_yday, gmt_hour, gmt_min; + + /* Get current time */ + (void) time (¤t); + + /* Get GMT and then local dates */ + date = gmtime ((const time_t *) ¤t); + gmt_yday = date->tm_yday; + gmt_hour = date->tm_hour; + gmt_min = date->tm_min; + date = localtime ((const time_t *) ¤t); + + /* Calculates offset */ + + offset = (date->tm_hour - gmt_hour) * 60 + (date->tm_min - gmt_min); + /* Be careful, there can be problems if the day has changed between the + evaluation of local and gmt's one */ + if (date->tm_yday != gmt_yday) + { + if (date->tm_yday == (gmt_yday + 1)) + offset += 1440; + else if (date->tm_yday == (gmt_yday - 1)) + offset -= 1440; + else + offset += (date->tm_yday > gmt_yday) ? -1440 : 1440; + } + + if (offset >= 0) + sprintf (timezone, "+%02d%02d", offset / 60, offset % 60); + else + sprintf (timezone, "-%02d%02d", -offset / 60, -offset % 60); + + sprintf (d_string, "%s, %d %s %04d %02d:%02d:%02d %s", + week_day[date->tm_wday], + date->tm_mday, month[date->tm_mon], date->tm_year + 1900, + date->tm_hour, date->tm_min, date->tm_sec, timezone); +#else + time_t now; + + /* RFC822 format string borrowed from GNU shellutils date.c */ + const char *format = "%a, %e %b %Y %H:%M:%S %z"; + + now = time(NULL); + + date = localtime((const time_t *)&now); + (void)strftime(d_string, ARPADATE_LENGTH, format, date); +#endif +} + +typedef enum { False, True } bool_t; + +struct string_list { + char *string; + struct string_list *next; +}; + +typedef struct string_list headers_t; +typedef struct string_list rcpt_t; + + +/* arpadate.c */ +void get_arpadate(char *); + +/* base64.c */ +void to64frombits(unsigned char *, const unsigned char *, int); +int from64tobits(char *, const char *); +/* + + $Id: ssmtp.c,v 2.60 2003/08/17 14:17:57 matt Exp $ + + sSMTP -- send messages via SMTP to a mailhub for local delivery or forwarding. + This program is used in place of /usr/sbin/sendmail, called by "mail" (et all). + sSMTP does a selected subset of sendmail's standard tasks (including exactly + one rewriting task), and explains if you ask it to do something it can't. It + then sends the mail to the mailhub via an SMTP connection. Believe it or not, + this is nothing but a filter + + See COPYRIGHT for the license + +*/ +#define VERSION "2.60.4" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SSL +#include +#include +#include +#include +#include +#endif +#ifdef MD5AUTH +#include "md5auth/hmac_md5.h" +#endif +#include "libbb.h" + + +bool_t have_date = False; +bool_t have_from = False; +#ifdef HASTO_OPTION +bool_t have_to = False; +#endif +bool_t minus_t = False; +bool_t minus_v = False; +bool_t override_from = False; +bool_t rewrite_domain = False; +bool_t use_tls = False; /* Use SSL to transfer mail to HUB */ +bool_t use_starttls = False; /* SSL only after STARTTLS (RFC2487) */ +bool_t use_cert = False; /* Use a certificate to transfer SSL mail */ + +#define ARPADATE_LENGTH 32 /* Current date in RFC format */ +char arpadate[ARPADATE_LENGTH]; +char *auth_user = (char) NULL; +char *auth_pass = (char) NULL; +char *auth_method = (char) NULL; /* Mechanism for SMTP authentication */ +char *mail_domain = (char) NULL; +char *from = (char) NULL; /* Use this as the From: address */ +char hostname[MAXHOSTNAMELEN] = "localhost"; +const char *mailhost = "mailhub"; +char *minus_f = (char) NULL; +char *minus_F = (char) NULL; +char *gecos; +char *root = NULL; +#ifdef HAVE_FEATURE_SSMTP_SSL_ENABLED +char *tls_cert = "/etc/ssl/certs/ssmtp.pem"; /* Default Certificate */ +#endif +char *uad = (char) NULL; + +headers_t headers, *ht; + +#ifdef DEBUG +int log_level = 1; +#else +int log_level = 0; +#endif +int port = 25; +#ifdef INET6 +int p_family = PF_UNSPEC; /* Protocol family used in SMTP connection */ +#endif + +jmp_buf TimeoutJmpBuf; /* Timeout waiting for input from network */ + +rcpt_t rcpt_list, *rt; + +#ifdef HAVE_SSL +SSL *ssl; +#endif + +#ifdef MD5AUTH +static char hextab[] = "0123456789abcdef"; +#endif + + +/* +log_event() -- Write event to syslog (or log file if defined) +*/ +void log_event(int priority, const char *format, ...); +void log_event(int priority, const char *format, ...) +{ + char buf[(BUF_SZ + 1)]; + va_list ap; + + va_start(ap, format); + (void) vsnprintf(buf, BUF_SZ, format, ap); + va_end(ap); + +#ifdef LOGFILE + FILE *fp; + + if ((fp = fopen("/tmp/ssmtp.log", "a")) != (FILE *) NULL) { + (void) fprintf(fp, "%s\n", buf); + (void) fclose(fp); + } else { + (void) fprintf(stderr, "Can't write to /tmp/ssmtp.log\n"); + } +#endif + +#ifdef HAVE_SYSLOG_H +#if OLDSYSLOG + openlog("sSMTP", LOG_PID); +#else + openlog("sSMTP", LOG_PID, LOG_MAIL); +#endif + syslog(priority, "%s", buf); + closelog(); +#endif +} + +/* +void smtp_write(int fd, char *format, ...); +int smtp_read(int fd, char *response); +int smtp_read_all(int fd, char *response); +int smtp_okay(int fd, char *response); +*/ + +/* +dead_letter() -- Save stdin to ~/dead.letter if possible +*/ +static void dead_letter(void) +{ + char path[(MAXPATHLEN + 1)], buf[(BUF_SZ + 1)]; + struct passwd *pw; + uid_t uid; + FILE *fp; + + uid = getuid(); + pw = getpwuid(uid); + + if (isatty(fileno(stdin))) { + if (log_level > 0) { + log_event(LOG_ERR, + "stdin is a TTY - not saving to %s/dead.letter", pw->pw_dir); + } + return; + } + + if (pw == (struct passwd *) NULL) { + /* Far to early to save things */ + if (log_level > 0) { + log_event(LOG_ERR, "No sender failing horribly!"); + } + return; + } + + if (snprintf(path, BUF_SZ, "%s/dead.letter", pw->pw_dir) == -1) { + /* Can't use die() here since dead_letter() is called from die() */ + exit(1); + } + + if ((fp = fopen(path, "a")) == (FILE *) NULL) { + /* Perhaps the person doesn't have a homedir... */ + if (log_level > 0) { + log_event(LOG_ERR, "Can't open %s failing horribly!", path); + } + return; + } + + /* We start on a new line with a blank line separating messages */ + (void) fprintf(fp, "\n\n"); + + while (fgets(buf, sizeof(buf), stdin)) { + (void) fputs(buf, fp); + } + + if (fclose(fp) == -1) { + if (log_level > 0) { + log_event(LOG_ERR, + "Can't close %s/dead.letter, possibly truncated", + pw->pw_dir); + } + } +} + +/* +die() -- Write error message, dead.letter and exit +*/ +static void die(const char *format, ...) +{ + char buf[(BUF_SZ + 1)]; + va_list ap; + + va_start(ap, format); + (void) vsnprintf(buf, BUF_SZ, format, ap); + va_end(ap); + + (void) fprintf(stderr, "%s: %s\n", applet_name, buf); + log_event(LOG_ERR, "%s", buf); + + /* Send message to dead.letter */ + (void) dead_letter(); + + exit(1); +} + +/* +basename() -- Return last element of path +char *basename(char *str) +{ + char buf[MAXPATHLEN + 1], *p; + + if ((p = strrchr(str, '/'))) { + if (strncpy(buf, ++p, MAXPATHLEN) == (char *) NULL) { + die("basename() -- strncpy() failed"); + } + } else { + if (strncpy(buf, str, MAXPATHLEN) == (char *) NULL) { + die("basename() -- strncpy() failed"); + } + } + buf[MAXPATHLEN] = (char) NULL; + + return (strdup(buf)); +} +*/ + +/* +strip_pre_ws() -- Return pointer to first non-whitespace character +*/ +static char *strip_pre_ws(char *str) +{ + char *p; + + p = str; + while (*p && isspace(*p)) + p++; + + return (p); +} + +/* +strip_post_ws() -- Return pointer to last non-whitespace character +*/ +static char *strip_post_ws(char *str) +{ + char *p; + + p = (str + strlen(str)); + while (isspace(*--p)) { + *p = (char) NULL; + } + + return (p); +} + +/* +addr_parse() -- Parse from full email address +*/ +static char *addr_parse(char *str) +{ + char *p, *q; + + /* Simple case with email address enclosed in <> */ + if ((p = strdup(str)) == (char *) NULL) { + die("addr_parse(): strdup()"); + } + + if ((q = strchr(p, '<'))) { + q++; + if ((p = strchr(q, '>'))) { + *p = (char) NULL; + } + return (q); + } + + q = strip_pre_ws(p); + if (*q == '(') { + while ((*q++ != ')')); + } + p = strip_pre_ws(q); + + q = strip_post_ws(p); + if (*q == ')') { + while ((*--q != '(')); + *q = (char) NULL; + } + (void) strip_post_ws(p); + + return (p); +} + +/* +append_domain() -- Fix up address with @domain.com +*/ +static char *append_domain(char *str) +{ + char buf[(BUF_SZ + 1)]; + + if (strchr(str, '@') == (char *) NULL) { + if (snprintf(buf, BUF_SZ, "%s@%s", str, +#ifdef REWRITE_DOMAIN + rewrite_domain == True ? mail_domain : hostname +#else + hostname +#endif + ) == -1) { + die("append_domain() -- snprintf() failed"); + } + return (strdup(buf)); + } + + return (strdup(str)); +} + +/* +standardise() -- Trim off '\n's and double leading dots +*/ +static void standardise(char *str) +{ + size_t sl; + char *p; + + if ((p = strchr(str, '\n'))) { + *p = (char) NULL; + } + + /* Any line beginning with a dot has an additional dot inserted; + not just a line consisting solely of a dot. Thus we have to slide + the buffer down one */ + sl = strlen(str); + + if (*str == '.') { + if ((sl + 2) > BUF_SZ) { + die("standardise() -- Buffer overflow"); + } + (void) memmove((str + 1), str, (sl + 1)); /* Copy trailing \0 */ + + *str = '.'; + } +} + +/* +revaliases() -- Parse the reverse alias file + Fix globals to use any entry for sender +*/ +static void revaliases(struct passwd *pw) +{ + char buf[(BUF_SZ + 1)], *p; + FILE *fp; + + /* Try to open the reverse aliases file */ + if ((fp = fopen(REVALIASES_FILE, "r"))) { + /* Search if a reverse alias is defined for the sender */ + while (fgets(buf, sizeof(buf), fp)) { + /* Make comments invisible */ + if ((p = strchr(buf, '#'))) { + *p = (char) NULL; + } + + /* Ignore malformed lines and comments */ + if (strchr(buf, ':') == (char *) NULL) { + continue; + } + + /* Parse the alias */ + if (((p = strtok(buf, ":"))) && !strcmp(p, pw->pw_name)) { + if ((p = strtok(NULL, ": \t\r\n"))) { + if ((uad = strdup(p)) == (char *) NULL) { + die("revaliases() -- strdup() failed"); + } + } + + if ((p = strtok(NULL, " \t\r\n:"))) { + if ((mailhost = strdup(p)) == (char *) NULL) { + die("revaliases() -- strdup() failed"); + } + + if ((p = strtok(NULL, " \t\r\n:"))) { + port = atoi(p); + } + + if (log_level > 0) { + log_event(LOG_INFO, "Set MailHub=\"%s\"\n", + mailhost); + log_event(LOG_INFO, + "via SMTP Port Number=\"%d\"\n", port); + } + } + } + } + + fclose(fp); + } +} + +/* +from_strip() -- Transforms "Name " into "login@host" or "login@host (Real name)" +*/ +static char *from_strip(char *str) +{ + char *p; + + if (strncmp("From:", str, 5) == 0) { + str += 5; + } + + /* Remove the real name if necessary - just send the address */ + if ((p = addr_parse(str)) == (char *) NULL) { + die("from_strip() -- addr_parse() failed"); + } + return (strdup(p)); +} + +/* +from_format() -- Generate standard From: line +*/ +static char *from_format(char *str) +{ + char buf[(BUF_SZ + 1)]; + + if (override_from) { + if (minus_f) { + str = append_domain(minus_f); + } + + if (minus_F) { + if (snprintf(buf, BUF_SZ, "\"%s\" <%s>", minus_F, str) == -1) { + die("from_format() -- snprintf() failed"); + } + } else if (gecos) { + if (snprintf(buf, BUF_SZ, "\"%s\" <%s>", gecos, str) == -1) { + die("from_format() -- snprintf() failed"); + } + } else { + if (snprintf(buf, BUF_SZ, "%s", str) == -1) { + die("from_format() -- snprintf() failed"); + } + } + } else { + if (gecos) { + if (snprintf(buf, BUF_SZ, "\"%s\" <%s>", gecos, str) == -1) { + die("from_format() -- snprintf() failed"); + } + } + } + return (strdup(buf)); +} + +/* +rcpt_save() -- Store entry into RCPT list +*/ +static void rcpt_save(char *str) +{ + char *p; + +# if 1 + /* Horrible botch for group stuff */ + p = str; + while (*p) + p++; + + if (*--p == ';') { + return; + } +#endif + + /* Ignore missing usernames */ + if (*str == (char) NULL) { + return; + } + + if ((rt->string = strdup(str)) == (char *) NULL) { + die("rcpt_save() -- strdup() failed"); + } + + rt->next = (rcpt_t *) malloc(sizeof(rcpt_t)); + if (rt->next == (rcpt_t *) NULL) { + die("rcpt_save() -- malloc() failed"); + } + rt = rt->next; + + rt->next = (rcpt_t *) NULL; +} + +/* +rcpt_parse() -- Break To|Cc|Bcc into individual addresses +*/ +static void rcpt_parse(char *str) +{ + bool_t in_quotes = False, got_addr = False; + char *p, *q, *r; + + if ((p = strdup(str)) == (char *) NULL) { + die("rcpt_parse(): strdup() failed"); + } + q = p; + + /* Replace , and */ + while (*q) { + switch (*q) { + case '\t': + case '\n': + case '\r': + *q = ' '; + } + q++; + } + q = p; + r = q; + while (*q) { + if (*q == '"') { + in_quotes = (in_quotes ? False : True); + } + + /* End of string? */ + if (*(q + 1) == (char) NULL) { + got_addr = True; + } + + /* End of address? */ + if ((*q == ',') && (in_quotes == False)) { + got_addr = True; + + *q = (char) NULL; + } + + if (got_addr) { + while (*r && isspace(*r)) + r++; + + rcpt_save(addr_parse(r)); + r = (q + 1); + got_addr = False; + } + q++; + } + free(p); +} + +#ifdef MD5AUTH +static int crammd5(char *challengeb64, char *username, char *password, + char *responseb64) +{ + int i; + unsigned char digest[MD5_DIGEST_LEN]; + unsigned char digascii[MD5_DIGEST_LEN * 2]; + unsigned char challenge[(BUF_SZ + 1)]; + unsigned char response[(BUF_SZ + 1)]; + unsigned char secret[(MD5_BLOCK_LEN + 1)]; + + memset(secret, 0, sizeof(secret)); + memset(challenge, 0, sizeof(challenge)); + strncpy(secret, password, sizeof(secret)); + if (!challengeb64 || strlen(challengeb64) > sizeof(challenge) * 3 / 4) + return 0; + from64tobits(challenge, challengeb64); + + hmac_md5(challenge, strlen(challenge), secret, strlen(secret), digest); + + for (i = 0; i < MD5_DIGEST_LEN; i++) { + digascii[2 * i] = hextab[digest[i] >> 4]; + digascii[2 * i + 1] = hextab[(digest[i] & 0x0F)]; + } + digascii[MD5_DIGEST_LEN * 2] = '\0'; + + if (sizeof(response) <= strlen(username) + sizeof(digascii)) + return 0; + + strncpy(response, username, sizeof(response) - sizeof(digascii) - 2); + strcat(response, " "); + strcat(response, digascii); + to64frombits(responseb64, response, strlen(response)); + + return 1; +} +#endif + +/* +rcpt_remap() -- Alias systems-level users to the person who + reads their mail. This is variously the owner of a workstation, + the sysadmin of a group of stations and the postmaster otherwise. + We don't just mail stuff off to root on the mailhub :-) +*/ +static char *rcpt_remap(char *str) +{ + struct passwd *pw; + if ((root == NULL) || strlen(root) == 0 || strchr(str, '@') || + ((pw = getpwnam(str)) == NULL) || (pw->pw_uid > MAXSYSUID)) { + return (append_domain(str)); /* It's not a local systems-level user */ + } else { + return (append_domain(root)); + } +} + +/* +header_save() -- Store entry into header list +*/ +static void header_save(char *str) +{ + char *p; + + if ((p = strdup(str)) == (char *) NULL) { + die("header_save() -- strdup() failed"); + } + ht->string = p; + + if (strncasecmp(ht->string, "From:", 5) == 0) { +#if 1 + /* Hack check for NULL From: line */ + if (*(p + 6) == (char) NULL) { + return; + } +#endif + +#ifdef REWRITE_DOMAIN + if (override_from == True) { + uad = from_strip(ht->string); + } else { + return; + } +#endif + have_from = True; + } +#ifdef HASTO_OPTION + else if (strncasecmp(ht->string, "To:", 3) == 0) { + have_to = True; + } +#endif + else if (strncasecmp(ht->string, "Date:", 5) == 0) { + have_date = True; + } + + if (minus_t) { + /* Need to figure out recipients from the e-mail */ + if (strncasecmp(ht->string, "To:", 3) == 0) { + p = (ht->string + 3); + rcpt_parse(p); + } else if (strncasecmp(ht->string, "Bcc:", 4) == 0) { + p = (ht->string + 4); + rcpt_parse(p); + } else if (strncasecmp(ht->string, "CC:", 3) == 0) { + p = (ht->string + 3); + rcpt_parse(p); + } + } + + ht->next = (headers_t *) malloc(sizeof(headers_t)); + if (ht->next == (headers_t *) NULL) { + die("header_save() -- malloc() failed"); + } + ht = ht->next; + + ht->next = (headers_t *) NULL; +} + +/* +header_parse() -- Break headers into seperate entries +*/ +static void header_parse(FILE * stream) +{ + size_t size = BUF_SZ, len = 0; + char *p = (char *) NULL, *q = NULL; + bool_t in_header = True; + char l = (char) NULL; + int c; + + while (in_header && ((c = fgetc(stream)) != EOF)) { + /* Must have space for up to two more characters, since we + may need to insert a '\r' */ + if ((p == (char *) NULL) || (len >= (size - 1))) { + size += BUF_SZ; + + p = (char *) realloc(p, (size * sizeof(char))); + if (p == (char *) NULL) { + die("header_parse() -- realloc() failed"); + } + q = (p + len); + } + len++; + + if (l == '\n') { + switch (c) { + case ' ': + case '\t': + /* Must insert '\r' before '\n's embedded in header + fields otherwise qmail won't accept our mail + because a bare '\n' violates some RFC */ + + *(q - 1) = '\r'; /* Replace previous \n with \r */ + *q++ = '\n'; /* Insert \n */ + len++; + + break; + + case '\n': + in_header = False; + + default: + *q = (char) NULL; + if ((q = strrchr(p, '\n'))) { + *q = (char) NULL; + } + header_save(p); + + q = p; + len = 0; + } + } + *q++ = c; + + l = c; + } + (void) free(p); +} + +/* +read_config() -- Open and parse config file and extract values of variables +*/ +static bool_t read_config(void) +{ + char buf[(BUF_SZ + 1)], *p, *q, *r; + FILE *fp; + + if ((fp = fopen(CONFIGURATION_FILE, "r")) == NULL) { + return (False); + } + + while (fgets(buf, sizeof(buf), fp)) { + /* Make comments invisible */ + if ((p = strchr(buf, '#'))) { + *p = (char) NULL; + } + + /* Ignore malformed lines and comments */ + if (strchr(buf, '=') == (char *) NULL) + continue; + + /* Parse out keywords */ + if (((p = strtok(buf, "= \t\n")) != (char *) NULL) + && ((q = strtok(NULL, "= \t\n:")) != (char *) NULL)) { + if (strcasecmp(p, "Root") == 0) { + if ((root = strdup(q)) == (char *) NULL) { + die("parse_config() -- strdup() failed"); + } + + if (log_level > 0) { + log_event(LOG_INFO, "Set Root=\"%s\"\n", root); + } + } else if (strcasecmp(p, "MailHub") == 0) { + if ((mailhost = strdup(q)) == (char *) NULL) { + die("parse_config() -- strdup() failed"); + } + + if ((r = strtok(NULL, "= \t\n:")) != NULL) { + port = atoi(r); + } + + if (log_level > 0) { + log_event(LOG_INFO, "Set MailHub=\"%s\"\n", mailhost); + log_event(LOG_INFO, "Set RemotePort=\"%d\"\n", port); + } + } else if (strcasecmp(p, "HostName") == 0) { + if (strncpy(hostname, q, MAXHOSTNAMELEN) == NULL) { + die("parse_config() -- strncpy() failed"); + } + + if (log_level > 0) { + log_event(LOG_INFO, "Set HostName=\"%s\"\n", hostname); + } + } +#ifdef REWRITE_DOMAIN + else if (strcasecmp(p, "RewriteDomain") == 0) { + if ((p = strrchr(q, '@'))) { + mail_domain = strdup(++p); + + log_event(LOG_ERR, + "Set RewriteDomain=\"%s\" is invalid\n", q); + log_event(LOG_ERR, + "Set RewriteDomain=\"%s\" used\n", + mail_domain); + } else { + mail_domain = strdup(q); + } + + if (mail_domain == (char *) NULL) { + die("parse_config() -- strdup() failed"); + } + rewrite_domain = True; + + if (log_level > 0) { + log_event(LOG_INFO, + "Set RewriteDomain=\"%s\"\n", mail_domain); + } + } +#endif + else if (strcasecmp(p, "FromLineOverride") == 0) { + if (strcasecmp(q, "YES") == 0) { + override_from = True; + } else { + override_from = False; + } + + if (log_level > 0) { + log_event(LOG_INFO, + "Set FromLineOverride=\"%s\"\n", + override_from ? "True" : "False"); + } + } else if (strcasecmp(p, "RemotePort") == 0) { + port = atoi(q); + + if (log_level > 0) { + log_event(LOG_INFO, "Set RemotePort=\"%d\"\n", port); + } + } +#ifdef HAVE_SSL + else if (strcasecmp(p, "UseTLS") == 0) { + if (strcasecmp(q, "YES") == 0) { + use_tls = True; + } else { + use_tls = False; + use_starttls = False; + } + + if (log_level > 0) { + log_event(LOG_INFO, + "Set UseTLS=\"%s\"\n", + use_tls ? "True" : "False"); + } + } else if (strcasecmp(p, "UseSTARTTLS") == 0) { + if (strcasecmp(q, "YES") == 0) { + use_starttls = True; + use_tls = True; + } else { + use_starttls = False; + } + + if (log_level > 0) { + log_event(LOG_INFO, + "Set UseSTARTTLS=\"%s\"\n", + use_tls ? "True" : "False"); + } + } else if (strcasecmp(p, "UseTLSCert") == 0) { + if (strcasecmp(q, "YES") == 0) { + use_cert = True; + } else { + use_cert = False; + } + + if (log_level > 0) { + log_event(LOG_INFO, + "Set UseTLSCert=\"%s\"\n", + use_cert ? "True" : "False"); + } + } else if (strcasecmp(p, "TLSCert") == 0) { + if ((tls_cert = strdup(q)) == (char *) NULL) { + die("parse_config() -- strdup() failed"); + } + + if (log_level > 0) { + log_event(LOG_INFO, "Set TLSCert=\"%s\"\n", tls_cert); + } + } +#endif + /* Command-line overrides these */ + else if (strcasecmp(p, "AuthUser") == 0 && !auth_user) { + if ((auth_user = strdup(q)) == (char *) NULL) { + die("parse_config() -- strdup() failed"); + } + + if (log_level > 0) { + log_event(LOG_INFO, "Set AuthUser=\"%s\"\n", + auth_user); + } + } else if (strcasecmp(p, "AuthPass") == 0 && !auth_pass) { + if ((auth_pass = strdup(q)) == (char *) NULL) { + die("parse_config() -- strdup() failed"); + } + + if (log_level > 0) { + log_event(LOG_INFO, "Set AuthPass=\"%s\"\n", + auth_pass); + } + } else if (strcasecmp(p, "AuthMethod") == 0 && !auth_method) { + if ((auth_method = strdup(q)) == (char *) NULL) { + die("parse_config() -- strdup() failed"); + } + + if (log_level > 0) { + log_event(LOG_INFO, "Set AuthMethod=\"%s\"\n", + auth_method); + } + } else { + log_event(LOG_INFO, "Unable to set %s=\"%s\"\n", p, q); + } + } + } + (void) fclose(fp); + + return (True); +} + +/* +smtp_open() -- Open connection to a remote SMTP listener +*/ +static int smtp_open(const char *host) +{ +#ifdef INET6 + struct addrinfo hints, *ai0, *ai; + char servname[NI_MAXSERV]; + int s; +#else + struct sockaddr_in name; + struct hostent *hent; + int s, namelen; +#endif + +#ifdef HAVE_SSL + int err; + char buf[(BUF_SZ + 1)]; + + /* Init SSL stuff */ + SSL_CTX *ctx; + SSL_METHOD *meth; + X509 *server_cert; + + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + meth = SSLv23_client_method(); + ctx = SSL_CTX_new(meth); + if (!ctx) { + log_event(LOG_ERR, "No SSL support initiated\n"); + return (-1); + } + + if (use_cert == True) { + if (SSL_CTX_use_certificate_chain_file(ctx, tls_cert) <= 0) { + perror("Use certfile"); + return (-1); + } + + if (SSL_CTX_use_PrivateKey_file(ctx, tls_cert, SSL_FILETYPE_PEM) <= + 0) { + perror("Use PrivateKey"); + return (-1); + } + + if (!SSL_CTX_check_private_key(ctx)) { + log_event(LOG_ERR, + "Private key does not match the certificate public key\n"); + return (-1); + } + } +#endif + +#ifdef INET6 + memset(&hints, 0, sizeof(hints)); + hints.ai_family = p_family; + hints.ai_socktype = SOCK_STREAM; + snprintf(servname, sizeof(servname), "%d", port); + + /* Check we can reach the host */ + if (getaddrinfo(host, servname, &hints, &ai0)) { + log_event(LOG_ERR, "Unable to locate %s", host); + return (-1); + } + + for (ai = ai0; ai; ai = ai->ai_next) { + /* Create a socket for the connection */ + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s < 0) { + continue; + } + + if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) { + s = -1; + continue; + } + break; + } + + if (s < 0) { + log_event(LOG_ERR, + "Unable to connect to \"%s\" port %d.\n", host, port); + + return (-1); + } +#else + /* Check we can reach the host */ + if ((hent = gethostbyname(host)) == (struct hostent *) NULL) { + log_event(LOG_ERR, "Unable to locate %s", host); + return (-1); + } + + if (hent->h_length > sizeof(hent->h_addr)) { + log_event(LOG_ERR, "Buffer overflow in gethostbyname()"); + return (-1); + } + + /* Create a socket for the connection */ + if ((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + log_event(LOG_ERR, "Unable to create a socket"); + return (-1); + } + + /* This SHOULD already be in Network Byte Order from gethostbyname() */ + name.sin_addr.s_addr = ((struct in_addr *) (hent->h_addr))->s_addr; + name.sin_family = hent->h_addrtype; + name.sin_port = htons(port); + + namelen = sizeof(struct sockaddr_in); + if (connect(s, (struct sockaddr *) &name, namelen) < 0) { + log_event(LOG_ERR, "Unable to connect to %s:%d", host, port); + return (-1); + } +#endif + +#ifdef HAVE_SSL + if (use_tls == True) { + log_event(LOG_INFO, "Creating SSL connection to host"); + + if (use_starttls == True) { + use_tls = False; /* need to write plain text for a while */ + + if (smtp_okay(s, buf)) { + smtp_write(s, "EHLO %s", hostname); + if (smtp_okay(s, buf)) { + smtp_write(s, "STARTTLS"); /* assume STARTTLS regardless */ + if (!smtp_okay(s, buf)) { + log_event(LOG_ERR, "STARTTLS not working"); + return (-1); + } + } else { + log_event(LOG_ERR, "Invalid response: %s (%s)", buf, + hostname); + } + } else { + log_event(LOG_ERR, + "Invalid response SMTP Server (STARTTLS)"); + return (-1); + } + use_tls = True; /* now continue as normal for SSL */ + } + + ssl = SSL_new(ctx); + if (!ssl) { + log_event(LOG_ERR, "SSL not working"); + return (-1); + } + SSL_set_fd(ssl, s); + + err = SSL_connect(ssl); + if (err < 0) { + perror("SSL_connect"); + return (-1); + } + + if (log_level > 0 || 1) { + log_event(LOG_INFO, "SSL connection using %s", + SSL_get_cipher(ssl)); + } + + server_cert = SSL_get_peer_certificate(ssl); + if (!server_cert) { + return (-1); + } + X509_free(server_cert); + + /* TODO: Check server cert if changed! */ + } +#endif + + return (s); +} + +/* +fd_getc() -- Read a character from an fd +*/ +static ssize_t fd_getc(int fd, void *c) +{ +#ifdef HAVE_SSL + if (use_tls == True) { + return (SSL_read(ssl, c, 1)); + } +#endif + return (read(fd, c, 1)); +} + +/* +fd_gets() -- Get characters from a fd instead of an fp +*/ +static char *fd_gets(char *buf, int size, int fd) +{ + int i = 0; + char c; + + while ((i < size) && (fd_getc(fd, &c) == 1)) { + if (c == '\r'); /* Strip */ + else if (c == '\n') { + break; + } else { + buf[i++] = c; + } + } + buf[i] = (char) NULL; + + return (buf); +} + +/* +smtp_read() -- Get a line and return the initial digit +*/ +static int smtp_read(int fd, char *response) +{ + do { + if (fd_gets(response, BUF_SZ, fd) == NULL) { + return (0); + } + } + while (response[3] == '-'); + + if (log_level > 0) { + log_event(LOG_INFO, "%s\n", response); + } + + if (minus_v) { + (void) fprintf(stderr, "[<-] %s\n", response); + } + + return (atoi(response) / 100); +} + +/* +smtp_okay() -- Get a line and test the three-number string at the beginning + If it starts with a 2, it's OK +*/ +static int smtp_okay(int fd, char *response) +{ + return ((smtp_read(fd, response) == 2) ? 1 : 0); +} + +/* +fd_puts() -- Write characters to fd +*/ +static ssize_t fd_puts(int fd, const void *buf, size_t count) +{ +#ifdef HAVE_SSL + if (use_tls == True) { + return (SSL_write(ssl, buf, count)); + } +#endif + return (write(fd, buf, count)); +} + +/* +smtp_write() -- A printf to an fd and append +*/ +static void smtp_write(int fd, const char *format, ...) +{ + char buf[(BUF_SZ + 1)]; + va_list ap; + + va_start(ap, format); + if (vsnprintf(buf, (BUF_SZ - 2), format, ap) == -1) { + die("smtp_write() -- vsnprintf() failed"); + } + va_end(ap); + + if (log_level > 0) { + log_event(LOG_INFO, "%s\n", buf); + } + + if (minus_v) { + (void) fprintf(stderr, "[->] %s\n", buf); + } + (void) strcat(buf, "\r\n"); + + (void) fd_puts(fd, buf, strlen(buf)); +} + +/* +handler() -- A "normal" non-portable version of an alarm handler + Alas, setting a flag and returning is not fully functional in + BSD: system calls don't fail when reading from a ``slow'' device + like a socket. So we longjump instead, which is erronious on + a small number of machines and ill-defined in the language +*/ +static void handler(void) +{ + extern jmp_buf TimeoutJmpBuf; + + longjmp(TimeoutJmpBuf, (int) 1); +} + +/* +ssmtp() -- send the message (exactly one) from stdin to the mailhub SMTP port +*/ +static int ssmtp(char *argv[]) +{ + char buf[(BUF_SZ + 1)], *p, *q; +#ifdef MD5AUTH + char challenge[(BUF_SZ + 1)]; +#endif + struct passwd *pw; + int i, sock; + uid_t uid; + + uid = getuid(); + if ((pw = getpwuid(uid)) == (struct passwd *) NULL) { + die("Could not find password entry for UID %d", uid); + } + get_arpadate(arpadate); + + if (read_config() == False) { + log_event(LOG_INFO, "%s/ssmtp.conf not found", SSMTPCONFDIR); + } + + if ((p = strtok(pw->pw_gecos, ";,"))) { + if ((gecos = strdup(p)) == (char *) NULL) { + die("ssmtp() -- strdup() failed"); + } + } + revaliases(pw); + + /* revaliases() may have defined this */ + if (uad == (char *) NULL) { + uad = append_domain(pw->pw_name); + } + + ht = &headers; + rt = &rcpt_list; + + header_parse(stdin); + +#if 1 + /* With FromLineOverride=YES set, try to recover sane MAIL FROM address */ + uad = append_domain(uad); +#endif + + from = from_format(uad); + + /* Now to the delivery of the message */ + signal(SIGALRM, (sighandler_t)handler); /* Catch SIGALRM */ + alarm((unsigned) MAXWAIT); /* Set initial timer */ + if (setjmp(TimeoutJmpBuf) != 0) { + /* Then the timer has gone off and we bail out */ + die("Connection lost in middle of processing"); + } + + if ((sock = smtp_open(mailhost)) == -1) { + die("Cannot open %s:%d", mailhost, port); + } else if (use_starttls == False) { /* no initial response after STARTTLS */ + if (smtp_okay(sock, buf) == False) + die("Invalid response SMTP server"); + } + + /* If user supplied username and password, then try ELHO */ + if (auth_user) { + smtp_write(sock, "EHLO %s", hostname); + } else { + smtp_write(sock, "HELO %s", hostname); + } + (void) alarm((unsigned) MEDWAIT); + + if (smtp_okay(sock, buf) == False) { + die("%s (%s)", buf, hostname); + } + + /* Try to log in if username was supplied */ + if (auth_user) { +#ifdef MD5AUTH + if (auth_pass == (char *) NULL) { + auth_pass = strdup(""); + } + + if (strcasecmp(auth_method, "cram-md5") == 0) { + smtp_write(sock, "AUTH CRAM-MD5"); + (void) alarm((unsigned) MEDWAIT); + + if (smtp_read(sock, buf) != 3) { + die("Server rejected AUTH CRAM-MD5 (%s)", buf); + } + strncpy(challenge, strchr(buf, ' ') + 1, sizeof(challenge)); + + memset(buf, 0, sizeof(buf)); + crammd5(challenge, auth_user, auth_pass, buf); + } else { +#endif + memset(buf, 0, sizeof(buf)); + to64frombits(buf, auth_user, strlen(auth_user)); + smtp_write(sock, "AUTH LOGIN %s", buf); + + (void) alarm((unsigned) MEDWAIT); + if (smtp_read(sock, buf) != 3) { + die("Server didn't accept AUTH LOGIN (%s)", buf); + } + memset(buf, 0, sizeof(buf)); + + to64frombits(buf, auth_pass, strlen(auth_pass)); +#ifdef MD5AUTH + } +#endif + smtp_write(sock, "%s", buf); + (void) alarm((unsigned) MEDWAIT); + + if (smtp_okay(sock, buf) == False) { + die("Authorization failed (%s)", buf); + } + } + + /* Send "MAIL FROM:" line */ + smtp_write(sock, "MAIL FROM:<%s>", uad); + + (void) alarm((unsigned) MEDWAIT); + + if (smtp_okay(sock, buf) == 0) { + die("%s", buf); + } + + /* Send all the To: adresses */ + /* Either we're using the -t option, or we're using the arguments */ + if (minus_t) { + if (rcpt_list.next == (rcpt_t *) NULL) { + die("No recipients specified although -t option used"); + } + rt = &rcpt_list; + + while (rt->next) { + p = rcpt_remap(rt->string); + smtp_write(sock, "RCPT TO:<%s>", p); + + (void) alarm((unsigned) MEDWAIT); + + if (smtp_okay(sock, buf) == 0) { + die("RCPT TO:<%s> (%s)", p, buf); + } + + rt = rt->next; + } + } else { + for (i = 1; (argv[i] != NULL); i++) { + p = strtok(argv[i], ","); + while (p) { + /* RFC822 Address -> "foo@bar" */ + q = rcpt_remap(addr_parse(p)); + smtp_write(sock, "RCPT TO:<%s>", q); + + (void) alarm((unsigned) MEDWAIT); + + if (smtp_okay(sock, buf) == 0) { + die("RCPT TO:<%s> (%s)", q, buf); + } + + p = strtok(NULL, ","); + } + } + } + + /* Send DATA */ + smtp_write(sock, "DATA"); + (void) alarm((unsigned) MEDWAIT); + + if (smtp_read(sock, buf) != 3) { + /* Oops, we were expecting "354 send your data" */ + die("%s", buf); + } + + smtp_write(sock, + "Received: by %s (sSMTP sendmail emulation); %s", hostname, + arpadate); + + if (have_from == False) { + smtp_write(sock, "From: %s", from); + } + + if (have_date == False) { + smtp_write(sock, "Date: %s", arpadate); + } +#ifdef HASTO_OPTION + if (have_to == False) { + smtp_write(sock, "To: postmaster"); + } +#endif + + ht = &headers; + while (ht->next) { + smtp_write(sock, "%s", ht->string); + ht = ht->next; + } + + (void) alarm((unsigned) MEDWAIT); + + /* End of headers, start body */ + smtp_write(sock, ""); + + while (fgets(buf, sizeof(buf), stdin)) { + /* Trim off \n, double leading .'s */ + standardise(buf); + + smtp_write(sock, "%s", buf); + + (void) alarm((unsigned) MEDWAIT); + } + /* End of body */ + + smtp_write(sock, "."); + (void) alarm((unsigned) MAXWAIT); + + if (smtp_okay(sock, buf) == 0) { + die("%s", buf); + } + + /* Close conection */ + (void) signal(SIGALRM, SIG_IGN); + + smtp_write(sock, "QUIT"); + (void) smtp_okay(sock, buf); + (void) close(sock); + + log_event(LOG_INFO, "Sent mail for %s (%s)", from_strip(uad), buf); + + return (0); +} + +/* +paq() - Write error message and exit +*/ +static void paq(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + (void) vfprintf(stderr, format, ap); + va_end(ap); + + exit(0); +} + +/* +parse_options() -- Pull the options out of the command-line + Process them (special-case calls to mailq, etc) and return the rest +*/ +static char **parse_options(int argc, char *argv[]) +{ + static char Version[] = VERSION; + char **new_argv = xmalloc(MAXARGS * sizeof(char *)); + int i, j, add, new_argc; + + new_argv[0] = argv[0]; + new_argc = 1; + +/* I don't think we want support mailq */ +#if 0 + if (strcmp(applet_name, "mailq") == 0) { + /* Someone wants to know the queue state... */ + paq("mailq: Mail queue is empty\n"); + } else if (strcmp(prog, "newaliases") == 0) { + /* Someone wanted to rebuild aliases */ + paq("newaliases: Aliases are not used in sSMTP\n"); + } +#endif + + i = 1; + while (i < argc) { + if (argv[i][0] != '-') { + new_argv[new_argc++] = argv[i++]; + continue; + } + j = 0; + + add = 1; + while (argv[i][++j] != (char) NULL) { + switch (argv[i][j]) { +#ifdef INET6 + case '6': + p_family = PF_INET6; + continue; + + case '4': + p_family = PF_INET; + continue; +#endif + + case 'a': + switch (argv[i][++j]) { + case 'u': + if ((!argv[i][(j + 1)]) + && argv[(i + 1)]) { + auth_user = strdup(argv[i + 1]); + if (auth_user == (char *) NULL) { + die("parse_options() -- strdup() failed"); + } + add++; + } else { + auth_user = strdup(argv[i] + j + 1); + if (auth_user == (char *) NULL) { + die("parse_options() -- strdup() failed"); + } + } + goto exit; + + case 'p': + if ((!argv[i][(j + 1)]) + && argv[(i + 1)]) { + auth_pass = strdup(argv[i + 1]); + if (auth_pass == (char *) NULL) { + die("parse_options() -- strdup() failed"); + } + add++; + } else { + auth_pass = strdup(argv[i] + j + 1); + if (auth_pass == (char *) NULL) { + die("parse_options() -- strdup() failed"); + } + } + goto exit; + +/* +#ifdef MD5AUTH +*/ + case 'm': + if (!argv[i][j + 1]) { + auth_method = strdup(argv[i + 1]); + add++; + } else { + auth_method = strdup(argv[i] + j + 1); + } + } + goto exit; +/* +#endif +*/ + + case 'b': + switch (argv[i][++j]) { + + case 'a': /* ARPANET mode */ + paq("-ba is not supported by sSMTP\n"); + case 'd': /* Run as a daemon */ + paq("-bd is not supported by sSMTP\n"); + case 'i': /* Initialise aliases */ + paq("%s: Aliases are not used in sSMTP\n", applet_name); + case 'm': /* Default addr processing */ + continue; + + case 'p': /* Print mailqueue */ + paq("%s: Mail queue is empty\n", applet_name); + case 's': /* Read SMTP from stdin */ + paq("-bs is not supported by sSMTP\n"); + case 't': /* Test mode */ + paq("-bt is meaningless to sSMTP\n"); + case 'v': /* Verify names only */ + paq("-bv is meaningless to sSMTP\n"); + case 'z': /* Create freeze file */ + paq("-bz is meaningless to sSMTP\n"); + } + + /* Configfile name */ + case 'C': + goto exit; + + /* Debug */ + case 'd': + log_level = 1; + /* Almost the same thing... */ + minus_v = True; + + continue; + + /* Insecure channel, don't trust userid */ + case 'E': + continue; + + case 'R': + /* Amount of the message to be returned */ + if (!argv[i][j + 1]) { + add++; + goto exit; + } else { + /* Process queue for recipient */ + continue; + } + + /* Fullname of sender */ + case 'F': + if ((!argv[i][(j + 1)]) && argv[(i + 1)]) { + minus_F = strdup(argv[(i + 1)]); + if (minus_F == (char *) NULL) { + die("parse_options() -- strdup() failed"); + } + add++; + } else { + minus_F = strdup(argv[i] + j + 1); + if (minus_F == (char *) NULL) { + die("parse_options() -- strdup() failed"); + } + } + goto exit; + + /* Set from/sender address */ + case 'f': + /* Obsolete -f flag */ + case 'r': + if ((!argv[i][(j + 1)]) && argv[(i + 1)]) { + minus_f = strdup(argv[(i + 1)]); + if (minus_f == (char *) NULL) { + die("parse_options() -- strdup() failed"); + } + add++; + } else { + minus_f = strdup(argv[i] + j + 1); + if (minus_f == (char *) NULL) { + die("parse_options() -- strdup() failed"); + } + } + goto exit; + + /* Set hopcount */ + case 'h': + continue; + + /* Ignore originator in adress list */ + case 'm': + continue; + + /* Use specified message-id */ + case 'M': + goto exit; + + /* DSN options */ + case 'N': + add++; + goto exit; + + /* No aliasing */ + case 'n': + continue; + + case 'o': + switch (argv[i][++j]) { + + /* Alternate aliases file */ + case 'A': + goto exit; + + /* Delay connections */ + case 'c': + continue; + + /* Run newaliases if required */ + case 'D': + paq("%s: Aliases are not used in sSMTP\n", applet_name); + + /* Deliver now, in background or queue */ + /* This may warrant a diagnostic for b or q */ + case 'd': + continue; + + /* Errors: mail, write or none */ + case 'e': + j++; + continue; + + /* Set tempfile mode */ + case 'F': + goto exit; + + /* Save ``From ' lines */ + case 'f': + continue; + + /* Set group id */ + case 'g': + goto exit; + + /* Helpfile name */ + case 'H': + continue; + + /* DATA ends at EOF, not \n.\n */ + case 'i': + continue; + + /* Log level */ + case 'L': + goto exit; + + /* Send to me if in the list */ + case 'm': + continue; + + /* Old headers, spaces between adresses */ + case 'o': + paq("-oo is not supported by sSMTP\n"); + + /* Queue dir */ + case 'Q': + goto exit; + + /* Read timeout */ + case 'r': + goto exit; + + /* Always init the queue */ + case 's': + continue; + + /* Stats file */ + case 'S': + goto exit; + + /* Queue timeout */ + case 'T': + goto exit; + + /* Set timezone */ + case 't': + goto exit; + + /* Set uid */ + case 'u': + goto exit; + + /* Set verbose flag */ + case 'v': + minus_v = True; + continue; + } + break; + + /* Process the queue [at time] */ + case 'q': + paq("%s: Mail queue is empty\n", applet_name); + + /* Read message's To/Cc/Bcc lines */ + case 't': + minus_t = True; + continue; + + /* minus_v (ditto -ov) */ + case 'v': + minus_v = True; + break; + + /* Say version and quit */ + /* Similar as die, but no logging */ + case 'V': + paq("sSMTP %s (Not sendmail at all)\n", Version); + } + } + + exit: + i += add; + } + new_argv[new_argc] = NULL; + + if (new_argc <= 1 && !minus_t) { + paq("%s: No recipients supplied - mail will not be sent\n", applet_name); + } + + if (new_argc > 1 && minus_t) { + paq("%s: recipients with -t option not supported\n", applet_name); + } + + return (&new_argv[0]); +} + +/* +main() -- make the program behave like sendmail, then call ssmtp +*/ +int sendmail_main(int argc, char **argv); +int sendmail_main(int argc, char **argv) +{ + char **new_argv; + + /* Try to be bulletproof :-) */ + (void) signal(SIGHUP, SIG_IGN); + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGTTIN, SIG_IGN); + (void) signal(SIGTTOU, SIG_IGN); + + /* Set the globals */ + if (gethostname(hostname, MAXHOSTNAMELEN) == -1) { + die("Cannot get the name of this machine"); + } + new_argv = parse_options(argc, argv); + + exit(ssmtp(new_argv)); + if (ENABLE_FEATURE_CLEAN_UP) + free(new_argv); +} Index: include/usage.h =================================================================== --- include/usage.h (revision 19393) +++ include/usage.h (working copy) @@ -3028,6 +3028,17 @@ #define last_full_usage \ "Show listing of the last users that logged into the system" +#define sendmail_trivial_usage \ + "[-dX] [-Fftv] RCPTs..." +#define sendmail_full_usage \ + "Send a message using smtp\n"\ + "\nOptions:" \ + "\n -dX Set debug level to X"\ + "\n -Fname Set the full name of the sender" \ + "\n -fname Sets the name of the ``from'' person"\ + "\n -t Read recipients from message headers (To, CC, BCC)" \ + "\n -v Verbose mode" \ + #define sha1sum_trivial_usage \ "[OPTION] [FILEs...]" \ USE_FEATURE_MD5_SHA1_SUM_CHECK("\n or: sha1sum [OPTION] -c [FILE]") Index: include/applets.h =================================================================== --- include/applets.h (revision 19393) +++ include/applets.h (working copy) @@ -300,6 +300,7 @@ USE_FEATURE_SH_IS_MSH(APPLET_NOUSAGE(sh, msh, _BB_DIR_BIN, _BB_SUID_NEVER)) USE_SHA1SUM(APPLET_ODDNAME(sha1sum, md5_sha1_sum, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sha1sum)) USE_SLATTACH(APPLET(slattach, _BB_DIR_SBIN, _BB_SUID_NEVER)) +USE_SENDMAIL(APPLET(sendmail, _BB_DIR_SBIN, _BB_SUID_NEVER)) USE_SLEEP(APPLET_NOFORK(sleep, sleep, _BB_DIR_BIN, _BB_SUID_NEVER, sleep)) USE_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER, softlimit)) USE_SORT(APPLET_NOEXEC(sort, sort, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sort))