sendmail: update by Vladimir

build system: tweak for rare case where include/autoconf.h
  does not get updated

function                                             old     new   delta
packed_usage                                       26238   26242      +4
sendmail_main                                       1353     897    -456
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 4/-456)           Total: -452 bytes
This commit is contained in:
Denis Vlasenko 2009-03-31 23:41:53 +00:00
parent d308106245
commit 88b8f0a347
4 changed files with 127 additions and 271 deletions

View File

@ -3686,32 +3686,27 @@
"[OPTIONS] [rcpt]..." "[OPTIONS] [rcpt]..."
#define sendmail_full_usage "\n\n" \ #define sendmail_full_usage "\n\n" \
"Send an email\n" \ "Send an email\n" \
"\nOptions:" \ "\nStandard options:" \
"\n -w timeout Network timeout" \ "\n -t Read recipients from message body, add them to those on cmdline" \
"\n -N type Request delivery notification. Type is ignored" \ "\n -f sender Sender. REQUIRED!" \
"\n -f sender Sender" \ "\n -o options various options. -oi IMPLIED! others are IGNORED!" \
"\n -F fullname Sender full name. Overrides $NAME" \ "\n" \
USE_FEATURE_SENDMAIL_MAILX( \ "\nBusybox specific options:" \
"\n -s subject Subject" \ "\n -w seconds Network timeout" \
"\n -j charset Assume charset for body and subject (" CONFIG_FEATURE_MIME_CHARSET ")" \
"\n -a file File to attach. May be repeated" \
"\n -H 'prog args' Run connection helper" \ "\n -H 'prog args' Run connection helper" \
"\n Examples:" \ "\n Examples:" \
"\n -H 'exec openssl s_client -quiet -tls1 -starttls smtp" \ "\n -H 'exec openssl s_client -quiet -tls1 -starttls smtp" \
"\n -connect smtp.gmail.com:25' <email.txt" \ "\n -connect smtp.gmail.com:25' <email.txt" \
"\n [4<username_and_passwd.txt]" \ "\n [4<username_and_passwd.txt | -au<username> -ap<password>]" \
"\n -H 'exec openssl s_client -quiet -tls1" \ "\n -H 'exec openssl s_client -quiet -tls1" \
"\n -connect smtp.gmail.com:465' <email.txt" \ "\n -connect smtp.gmail.com:465' <email.txt" \
"\n [4<username_and_passwd.txt]" \ "\n [4<username_and_passwd.txt | -au<username> -ap<password>]" \
"\n -S server[:port] Server" \ "\n -S server[:port] Server" \
) \ "\n -au<username> Username for AUTH LOGIN" \
USE_FEATURE_SENDMAIL_MAILXX( \ "\n -ap<password> Password for AUTH LOGIN" \
"\n -c rcpt Cc: recipient. May be repeated" \ "\n -am<method> Authentication method. Ignored. login is implied." \
"\n -e rcpt Errors-To: recipient" \
) \
"\n -t Read recipients and subject from body" \
"\n" \ "\n" \
"\nOther options are silently ignored; -oi is implied" \ "\nOther options are silently ignored; -oi -t is implied" \
#define seq_trivial_usage \ #define seq_trivial_usage \
"[-w] [-s SEP] [FIRST [INC]] LAST" "[-w] [-s SEP] [FIRST [INC]] LAST"

View File

@ -50,20 +50,4 @@ config SENDMAIL
help help
Barebones sendmail. Barebones sendmail.
config FEATURE_SENDMAIL_MAILX
bool "Allow to specify subject, attachments, their charset etc"
default y
depends on SENDMAIL
help
Allow to specify subject, attachments and their charset.
Allow to use custom connection helper.
config FEATURE_SENDMAIL_MAILXX
bool "Allow to specify Cc: addresses and some additional headers"
default n
depends on FEATURE_SENDMAIL_MAILX
help
Allow to specify Cc: addresses and some additional headers:
Errors-To:
endmenu endmenu

View File

@ -61,35 +61,23 @@ static void rcptto(const char *s)
int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int sendmail_main(int argc UNUSED_PARAM, char **argv) int sendmail_main(int argc UNUSED_PARAM, char **argv)
{ {
#if ENABLE_FEATURE_SENDMAIL_MAILX
llist_t *opt_attachments = NULL;
const char *opt_subject;
#if ENABLE_FEATURE_SENDMAIL_MAILXX
llist_t *opt_carboncopies = NULL;
char *opt_errors_to;
#endif
#endif
char *opt_connect = opt_connect; char *opt_connect = opt_connect;
char *opt_from, *opt_fullname; char *opt_from;
char *boundary; char *s;
llist_t *l; llist_t *list = NULL;
llist_t *headers = NULL;
char *domain = sane_address(safe_getdomainname()); char *domain = sane_address(safe_getdomainname());
int code; int code;
enum { enum {
OPT_w = 1 << 0, // network timeout //--- standard options
OPT_t = 1 << 1, // read message for recipients OPT_t = 1 << 0, // read message for recipients, append them to those on cmdline
OPT_N = 1 << 2, // request notification OPT_f = 1 << 1, // sender address
OPT_f = 1 << 3, // sender address OPT_o = 1 << 2, // various options. -oi IMPLIED! others are IGNORED!
OPT_F = 1 << 4, // sender name, overrides $NAME //--- BB specific options
OPT_s = 1 << 5, // subject OPT_w = 1 << 3, // network timeout
OPT_j = 1 << 6, // assumed charset OPT_H = 1 << 4, // use external connection helper
OPT_a = 1 << 7, // attachment(s) OPT_S = 1 << 5, // specify connection string
OPT_H = 1 << 8, // use external connection helper OPT_a = 1 << 6, // authentication tokens
OPT_S = 1 << 9, // specify connection string
OPT_c = 1 << 10, // carbon copy
OPT_e = 1 << 11, // errors-to address
}; };
// init global variables // init global variables
@ -100,35 +88,42 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
G.fp0 = fdopen(3, "r"); G.fp0 = fdopen(3, "r");
// parse options // parse options
opt_complementary = "w+" USE_FEATURE_SENDMAIL_MAILX(":a::H--S:S--H") USE_FEATURE_SENDMAIL_MAILXX(":c::"); // -f is required. -H and -S are mutually exclusive
opts = getopt32(argv, opt_complementary = "f:w+:H--S:S--H:a::";
"w:t" "N:f:F:" USE_FEATURE_SENDMAIL_MAILX("s:j:a:H:S:") USE_FEATURE_SENDMAIL_MAILXX("c:e:") // N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect
"X:V:vq:R:O:o:nmL:Iih:GC:B:b:A:" // postfix compat only, ignored // -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility,
// r:Q:p:M:Dd are candidates from another man page. TODO? // it is still under development.
"46E", // ssmtp introduces another quirks. TODO?: -a[upm] (user, pass, method) to be supported opts = getopt32(argv, "tf:o:w:H:S:a::", &opt_from, NULL, &timeout, &opt_connect, &opt_connect, &list);
&timeout /* -w */, NULL, &opt_from, &opt_fullname,
USE_FEATURE_SENDMAIL_MAILX(&opt_subject, &G.opt_charset, &opt_attachments, &opt_connect, &opt_connect,)
USE_FEATURE_SENDMAIL_MAILXX(&opt_carboncopies, &opt_errors_to,)
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
);
//argc -= optind; //argc -= optind;
argv += optind; argv += optind;
// process -a[upm]<token> options
if ((opts & OPT_a) && !list)
bb_show_usage();
while (list) {
char *a = (char *) llist_pop(&list);
if ('u' == a[0])
G.user = xstrdup(a+1);
if ('p' == a[0])
G.pass = xstrdup(a+1);
// N.B. we support only AUTH LOGIN so far
//if ('m' == a[0])
// G.method = xstrdup(a+1);
}
// N.B. list == NULL here
//bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv);
// connect to server // connect to server
#if ENABLE_FEATURE_SENDMAIL_MAILX
// N.B. -H and -S are mutually exclusive so they do not spoil opt_connect
// connection helper ordered? -> // connection helper ordered? ->
if (opts & OPT_H) { if (opts & OPT_H) {
const char *args[] = { "sh", "-c", opt_connect, NULL }; const char *args[] = { "sh", "-c", opt_connect, NULL };
// plug it in // plug it in
launch_helper(args); launch_helper(args);
// vanilla connection // vanilla connection
} else } else {
#endif
{
int fd; int fd;
// host[:port] not explicitly specified ? -> use $SMTPHOST // host[:port] not explicitly specified? -> use $SMTPHOST
// no $SMTPHOST ? -> use localhost // no $SMTPHOST ? -> use localhost
if (!(opts & OPT_S)) { if (!(opts & OPT_S)) {
opt_connect = getenv("SMTPHOST"); opt_connect = getenv("SMTPHOST");
@ -145,7 +140,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
// wait for initial server OK // wait for initial server OK
// N.B. if we used openssl the initial 220 answer is already swallowed during openssl TLS init procedure // N.B. if we used openssl the initial 220 answer is already swallowed during openssl TLS init procedure
// so we need to push the server to see whether we are ok // so we need to kick the server to see whether we are ok
code = smtp_check("NOOP", -1); code = smtp_check("NOOP", -1);
// 220 on plain connection, 250 on openssl-helped TLS session // 220 on plain connection, 250 on openssl-helped TLS session
if (220 == code) if (220 == code)
@ -157,6 +152,20 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
if (250 != smtp_checkp("EHLO %s", domain, -1)) { if (250 != smtp_checkp("EHLO %s", domain, -1)) {
smtp_checkp("HELO %s", domain, 250); smtp_checkp("HELO %s", domain, 250);
} }
if (ENABLE_FEATURE_CLEAN_UP)
free(domain);
// perform authentication
if (opts & OPT_a) {
smtp_check("AUTH LOGIN", 334);
// we must read credentials unless they are given via -a[up] options
if (!G.user || !G.pass)
get_cred_or_die(4);
encode_base64(NULL, G.user, NULL);
smtp_check("", 334);
encode_base64(NULL, G.pass, NULL);
smtp_check("", 235);
}
// set sender // set sender
// N.B. we have here a very loosely defined algotythm // N.B. we have here a very loosely defined algotythm
@ -170,201 +179,25 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
// file descriptor (e.g. 4), or again from a secured file. // file descriptor (e.g. 4), or again from a secured file.
// got no sender address? -> use system username as a resort // got no sender address? -> use system username as a resort
if (!(opts & OPT_f)) { // N.B. we marked -f as required option!
// N.B. IMHO getenv("USER") can be way easily spoofed! //if (!G.user) {
G.user = xuid2uname(getuid()); // // N.B. IMHO getenv("USER") can be way easily spoofed!
opt_from = xasprintf("%s@%s", G.user, domain); // G.user = xuid2uname(getuid());
} // opt_from = xasprintf("%s@%s", G.user, domain);
if (ENABLE_FEATURE_CLEAN_UP) //}
free(domain); //if (ENABLE_FEATURE_CLEAN_UP)
// free(domain);
smtp_checkp("MAIL FROM:<%s>", opt_from, 250);
code = -1; // first try softly without authentication // process message
while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) {
// MAIL FROM failed -> authentication needed
if (334 == smtp_check("AUTH LOGIN", -1)) {
// we must read credentials
get_cred_or_die(4);
encode_base64(NULL, G.user, NULL);
smtp_check("", 334);
encode_base64(NULL, G.pass, NULL);
smtp_check("", 235);
}
// authenticated OK? -> retry to set sender
// but this time die on failure!
code = 250;
}
// recipients specified as arguments // read recipients from message and add them to those given on cmdline.
while (*argv) { // this means we scan stdin for To:, Cc:, Bcc: lines until an empty line
char *s = sane_address(*argv); // and then use the rest of stdin as message body
// loose test on email address validity code = 0; // set "analyze headers" mode
// if (strchr(s, '@')) { while ((s = xmalloc_fgetline(G.fp0)) != NULL) {
rcptto(s); // put message lines doubling leading dots
llist_add_to_end(&headers, xasprintf("To: %s", s)); if (code) {
// }
argv++;
}
#if ENABLE_FEATURE_SENDMAIL_MAILXX
// carbon copies recipients specified as -c options
for (l = opt_carboncopies; l; l = l->link) {
char *s = sane_address(l->data);
// loose test on email address validity
// if (strchr(s, '@')) {
rcptto(s);
// TODO: do we ever need to mangle the message?
//llist_add_to_end(&headers, xasprintf("Cc: %s", s));
// }
}
#endif
// if -t specified or no recipients specified -> read recipients from message
// i.e. scan stdin for To:, Cc:, Bcc: lines ...
// ... and then use the rest of stdin as message body
// N.B. subject read from body can be further overrided with one specified on command line.
// recipients are merged. Bcc: lines are deleted
// N.B. other headers are collected and will be dumped verbatim
if (opts & OPT_t || !headers) {
// fetch recipients and (optionally) subject
char *s;
while ((s = xmalloc_fgetline(G.fp0)) != NULL) {
if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Cc: ", s, 4)) {
rcptto(sane_address(s+4));
llist_add_to_end(&headers, s);
} else if (0 == strncasecmp("Bcc: ", s, 5)) {
rcptto(sane_address(s+5));
free(s);
// N.B. Bcc vanishes from headers!
} else if (0 == strncmp("Subject: ", s, 9)) {
// we read subject -> use it verbatim unless it is specified
// on command line
if (!(opts & OPT_s))
llist_add_to_end(&headers, s);
else
free(s);
} else if (s[0]) {
// misc header
llist_add_to_end(&headers, s);
} else {
free(s);
break; // stop on the first empty line
}
}
}
// enter "put message" mode
smtp_check("DATA", 354);
// put headers we could have preread with -t
for (l = headers; l; l = l->link) {
printf("%s\r\n", l->data);
if (ENABLE_FEATURE_CLEAN_UP)
free(l->data);
}
// put (possibly encoded) subject
#if ENABLE_FEATURE_SENDMAIL_MAILX
if (opts & OPT_s) {
printf("Subject: ");
if (opts & OPT_j) {
printf("=?%s?B?", G.opt_charset);
encode_base64(NULL, opt_subject, NULL);
printf("?=");
} else {
printf("%s", opt_subject);
}
printf("\r\n");
}
#endif
// put sender name, $NAME is the default
if (!(opts & OPT_F))
opt_fullname = getenv("NAME");
if (opt_fullname)
printf("From: \"%s\" <%s>\r\n", opt_fullname, opt_from);
// put notification
if (opts & OPT_N)
printf("Disposition-Notification-To: %s\r\n", opt_from);
#if ENABLE_FEATURE_SENDMAIL_MAILXX
// put errors recipient
if (opts & OPT_e)
printf("Errors-To: %s\r\n", opt_errors_to);
#endif
// make a random string -- it will delimit message parts
srand(monotonic_us());
boundary = xasprintf("%d=_%d-%d", rand(), rand(), rand());
// put common headers
// TODO: do we really need this?
// printf("Message-ID: <%s>\r\n", boundary);
#if ENABLE_FEATURE_SENDMAIL_MAILX
// have attachments? -> compose multipart MIME
if (opt_attachments) {
const char *fmt;
const char *p;
char *q;
printf(
"Mime-Version: 1.0\r\n"
"%smultipart/mixed; boundary=\"%s\"\r\n"
, "Content-Type: "
, boundary
);
// body is pseudo attachment read from stdin in first turn
llist_add_to(&opt_attachments, (char *)"-");
// put body + attachment(s)
// N.B. all these weird things just to be tiny
// by reusing string patterns!
fmt =
"\r\n--%s\r\n"
"%stext/plain; charset=%s\r\n"
"%s%s\r\n"
"%s"
;
p = G.opt_charset;
q = (char *)"";
l = opt_attachments;
while (l) {
printf(
fmt
, boundary
, "Content-Type: "
, p
, "Content-Disposition: inline"
, q
, "Content-Transfer-Encoding: base64\r\n"
);
p = "";
fmt =
"\r\n--%s\r\n"
"%sapplication/octet-stream%s\r\n"
"%s; filename=\"%s\"\r\n"
"%s"
;
encode_base64(l->data, (const char *)G.fp0, "\r");
l = l->link;
if (l)
q = bb_get_last_path_component_strip(l->data);
}
// put message terminator
printf("\r\n--%s--\r\n" "\r\n", boundary);
// no attachments? -> just dump message
} else
#endif
{
char *s;
// terminate headers
printf("\r\n");
// put plain text respecting leading dots
while ((s = xmalloc_fgetline(G.fp0)) != NULL) {
// escape leading dots // escape leading dots
// N.B. this feature is implied even if no -i (-oi) switch given // N.B. this feature is implied even if no -i (-oi) switch given
// N.B. we need to escape the leading dot regardless of // N.B. we need to escape the leading dot regardless of
@ -373,10 +206,48 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
printf("."); printf(".");
// dump read line // dump read line
printf("%s\r\n", s); printf("%s\r\n", s);
free(s);
continue;
}
// analyze headers
// To: or Cc: headers add recipients
if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Bcc: " + 1, s, 4)) {
rcptto(sane_address(s+4));
// goto addh;
llist_add_to_end(&list, s);
// Bcc: header adds blind copy (hidden) recipient
} else if (0 == strncasecmp("Bcc: ", s, 5)) {
rcptto(sane_address(s+5));
free(s);
// N.B. Bcc: vanishes from headers!
// other headers go verbatim
} else if (s[0]) {
// addh:
llist_add_to_end(&list, s);
// the empty line stops analyzing headers
} else {
free(s);
// put recipients specified on cmdline
while (*argv) {
s = sane_address(*argv);
rcptto(s);
llist_add_to_end(&list, xasprintf("To: %s", s));
argv++;
}
// enter "put message" mode
smtp_check("DATA", 354);
// dump the headers
while (list) {
printf("%s\r\n", (char *) llist_pop(&list));
}
printf("%s\r\n" + 2); // quirk for format string to be reused
// stop analyzing headers
code++;
} }
} }
// leave "put message" mode // finalize the message
smtp_check(".", 250); smtp_check(".", 250);
// ... and say goodbye // ... and say goodbye
smtp_check("QUIT", 221); smtp_check("QUIT", 221);

View File

@ -44,7 +44,13 @@ int file_write_dep(const char *name)
else else
fprintf(out, "\t%s\n", file->name); fprintf(out, "\t%s\n", file->name);
} }
fprintf(out, "\n.config include/autoconf.h: $(deps_config)\n\n$(deps_config):\n"); fprintf(out,
"\n"
".config include/autoconf.h: $(deps_config)\n"
"\n"
"include/autoconf.h: .config\n" /* bbox */
"\n"
"$(deps_config):\n");
fclose(out); fclose(out);
rename("..config.tmp", name); rename("..config.tmp", name);
return 0; return 0;