svn commit: trunk/busybox: include networking

vda at busybox.net vda at busybox.net
Tue Feb 26 21:13:17 UTC 2008


Author: vda
Date: 2008-02-26 13:13:17 -0800 (Tue, 26 Feb 2008)
New Revision: 21120

Log:
sendmail: update from maintainer



Modified:
   trunk/busybox/include/usage.h
   trunk/busybox/networking/sendmail.c


Changeset:
Modified: trunk/busybox/include/usage.h
===================================================================
--- trunk/busybox/include/usage.h	2008-02-26 20:23:02 UTC (rev 21119)
+++ trunk/busybox/include/usage.h	2008-02-26 21:13:17 UTC (rev 21120)
@@ -1026,17 +1026,19 @@
        "	-v		Give fdisk version"
 
 #define fetchmail_trivial_usage \
-       "[-C dir] [-w timeout] [-U user] -P password [-X] [-t] [-z] server[:port]"
+       "[-w timeout] [-U user] -P password [-X] [-t] [-z] server[:port] maildir [prog]"
 #define fetchmail_full_usage \
        "Fetch content of remote mailbox to local Maildir." \
        "\n\nOptions:\n" \
-       "	-C dir		Set Maildir location\n" \
        "	-w timeout	Set timeout on network operations\n" \
        "	-U username	Authenticate with specified username\n" \
        "	-P password	Authenticate with specified password\n" \
        "	-X		Use openssl connection helper for secured servers\n" \
        "	-t		Get only headers\n" \
-       "	-z		Delete messages on server"
+       "	-z		Delete messages on server" \
+USE_FEATURE_FETCHMAIL_FILTER("\n" \
+       "        prog		Run prog <message file> on message delivery" \
+)
 
 #define findfs_trivial_usage \
        "LABEL=label or UUID=uuid"
@@ -3258,18 +3260,16 @@
 #define selinuxenabled_full_usage
 
 #define sendmail_trivial_usage \
-       "[-C dir] [-w timeout] [-U user] [-P password] [-X]\n" \
-       "-t to [-t to] -f from [-n] [-s subject] [-c charset] server[:port] [body] [attachment ...]"
+       "[-w timeout] [-U user] [-P password] [-X]\n" \
+       "-t to [-t to]... [-n] [-s subject] [-c charset] server[:port] from [body] [attachment ...]"
 #define sendmail_full_usage \
        "Send an email with optional attachments." \
        "\n\nOptions:\n" \
-       "	-C dir		Change current directory to dir\n" \
        "	-w timeout	Set timeout on network operations\n" \
        "	-U username	Authenticate with specified username\n" \
        "	-P password	Authenticate with specified password\n" \
+       "	-t address	Recipient(s). May be multiple\n" \
        "	-X		Use openssl connection helper for secured servers\n" \
-       "	-t to		Recipient email. May be multiple\n" \
-       "	-f from		Sender address\n" \
        "	-n		Request delivery notification to sender\n" \
        "	-s subject	Subject\n" \
        "	-c charset	Assumed charset for body and subject [utf-8]"

Modified: trunk/busybox/networking/sendmail.c
===================================================================
--- trunk/busybox/networking/sendmail.c	2008-02-26 20:23:02 UTC (rev 21119)
+++ trunk/busybox/networking/sendmail.c	2008-02-26 21:13:17 UTC (rev 21120)
@@ -27,9 +27,13 @@
 		if (NOT_LONE_DASH(fname))
 			fd = xopen(fname, O_RDONLY);
 		src_buf = bb_common_bufsiz1;
-	} else {
+	// N.B. strlen(NULL) segfaults!
+	} else if (text) {
+		// though we do not call uuencode(NULL, NULL) explicitly
+		// still we do not want to break things suddenly
 		len = strlen(text);
-	}
+	} else
+		return;
 
 	fflush(stdout); // sync stdio and unistd output
 	while (1) {
@@ -59,8 +63,39 @@
 		close(fd);
 }
 
-static pid_t helper_pid;
+static const char *const init_xargs[9] = {
+	"openssl", "s_client", "-quiet", "-connect",
+	NULL, "-tls1", "-starttls", "smtp"
+};
 
+struct globals {
+	pid_t helper_pid;
+	unsigned timeout;
+	// arguments for SSL connection helper
+	const char *xargs[9];
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+	// arguments for postprocess helper
+	const char *fargs[3];
+#endif
+};
+#define G (*ptr_to_globals)
+#define helper_pid      (G.helper_pid)
+#define timeout         (G.timeout   )
+#define xargs           (G.xargs     )
+#define fargs           (G.fargs     )
+#define INIT_G() do { \
+	PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
+	memcpy(xargs, init_xargs, sizeof(init_xargs)); \
+	fargs[0] = "utf-8"; \
+} while (0)
+
+#define opt_connect	  (xargs[4])
+#define opt_after_connect (xargs[5])
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+#define opt_charset	  (fargs[0])
+#define opt_subject	  (fargs[1])
+#endif
+
 static void kill_helper(void)
 {
 	// TODO!!!: is there more elegant way to terminate child on program failure?
@@ -71,7 +106,7 @@
 // generic signal handler
 static void signal_handler(int signo)
 {
-	int err;
+#define err signo
 
 	if (SIGALRM == signo) {
 		kill_helper();
@@ -107,20 +142,19 @@
 				close(pipes[i]);
 	if (!helper_pid) {
 		// child - try to execute connection helper
-		BB_EXECVP(argv[0], (char **)argv);
+//		close(STDERR_FILENO);
+		BB_EXECVP(*argv, (char **)argv);
 		_exit(127);
 	}
 	// parent - check whether child is alive
-	bb_signals_recursive(0
-			+ (1 << SIGCHLD)
-			+ (1 << SIGALRM)
-			, signal_handler);
+	bb_signals(0
+		+ (1 << SIGCHLD)
+		+ (1 << SIGALRM)
+		, signal_handler);
 	signal_handler(SIGCHLD);
 	// child seems OK -> parent goes on
 }
 
-static unsigned timeout;
-
 static char *command(const char *fmt, const char *param)
 {
 	char *msg = (char *)fmt;
@@ -145,7 +179,9 @@
 	// if code != -1 then checks whether the number equals the code
 	// if not equal -> die saying msg
 #if ENABLE_FEATURE_SENDMAIL_EHLO
-	while ((answer = xmalloc_getline(stdin)) && '-' == answer[3]) ;
+	while ((answer = xmalloc_getline(stdin)) != NULL)
+		if (strlen(answer) <= 3 || '-' != answer[3])
+			break;
 #else
 	answer = xmalloc_getline(stdin);
 #endif
@@ -169,15 +205,31 @@
 	return smtp_checkp(fmt, NULL, code);
 }
 
+// strip argument of bad chars
+static char *sane(char *str)
+{
+	char *s = str;
+	char *p = s;
+	while (*s) {
+		if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) {
+			*p++ = *s;
+		}
+		s++;
+	}
+	*p = '\0';
+	return str;
+}
+
+#if ENABLE_FETCHMAIL
 static void pop3_checkr(const char *fmt, const char *param, char **ret)
 {
 	char *msg = command(fmt, param);
 	char *answer = xmalloc_getline(stdin);
-	if (answer && '+' == answer[0]) {
+	if (answer && '+' == *answer) {
 		alarm(0);
 		if (ret)
-			*ret = answer;
-		else
+			*ret = answer+4; // skip "+OK "
+		else if (ENABLE_FEATURE_CLEAN_UP)
 			free(answer);
 		return;
 	}
@@ -190,28 +242,16 @@
 	pop3_checkr(fmt, param, NULL);
 }
 
-// strip argument of bad chars
-static char *sane(char *str)
+static void pop3_message(const char *filename)
 {
-	char *s = str;
-	char *p = s;
-	while (*s) {
-		if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) {
-			*p++ = *s;
-		}
-		s++;
-	}
-	*p = '\0';
-	return str;
-}
-
-static void pop3_message(int fd)
-{
+	int fd;
 	char *answer;
-	// read stdin, copy to file fd
+	// create and open file filename
+	// read stdin, copy to created file
+	fd = xopen(filename, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL);
 	while ((answer = xmalloc_fgets_str(stdin, "\r\n"))) {
 		char *s = answer;
-		if ('.' == answer[0]) {
+		if ('.' == *answer) {
 			if ('.' == answer[1])
 				s++;
 			else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
@@ -222,64 +262,56 @@
 	}
 	close(fd);
 }
+#endif
 
-static const char *args[] = {
-	"openssl", "s_client", "-quiet", "-connect", NULL, "-tls1", "-starttls", "smtp", NULL
-};
-#define opt_connect	args[4]
-#define opt_after_connect args[5]
-
 int sendgetmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int sendgetmail_main(int argc, char **argv)
 {
-	llist_t *recipients = NULL;
-	char *from;
-	const char *subject;
-	char *charset = (char *)"utf-8";
+	llist_t *opt_recipients = NULL;
+#if !ENABLE_FEATURE_FETCHMAIL_FILTER
+	char *opt_subject;
+	char *opt_charset = (char *)"utf-8";
+#endif
 
 	const char *opt_user;
 	const char *opt_pass;
-	const char *opt_timeout;
-	const char *opt_chdir;
 
 	enum {
-		OPT_C = 1 << 0,         // chdir
-		OPT_w = 1 << 1,         // network timeout
-		OPT_U = 1 << 2,         // user
-		OPT_P = 1 << 3,         // password
-		OPT_X = 1 << 4,         // use openssl connection helper
+		OPT_w = 1 << 0,         // network timeout
+		OPT_U = 1 << 1,         // user
+		OPT_P = 1 << 2,         // password
+		OPT_X = 1 << 3,         // connect using openssl s_client helper
 
-		OPTS_t = 1 << 5,        // sendmail "to"
-		OPTF_t = 1 << 5,        // fetchmail "TOP"
+		OPTS_n = 1 << 4,        // sendmail: request notification
+		OPTF_t = 1 << 4,        // fetchmail: use "TOP" not "RETR"
 
-		OPTS_f = 1 << 6,        // sendmail "from"
-		OPTF_z = 1 << 6,        // fetchmail "delete"
+		OPTS_s = 1 << 5,        // sendmail: subject
+		OPTF_z = 1 << 5,        // fetchmail: delete from server
 
-		OPTS_n = 1 << 7,        // notification
-		OPTS_s = 1 << 8,        // subject given
-		OPTS_c = 1 << 9,        // charset for subject and body
+		OPTS_c = 1 << 6,        // sendmail: assumed charset
+		OPTS_t = 1 << 7,        // sendmail: recipient(s)
 	};
 
 	const char *options;
 	unsigned opts;
 
-	// SENDMAIL
-	if ('s' == applet_name[0]) {
-		// save initial stdin
+	INIT_G();
+
+	if (!ENABLE_FETCHMAIL || 's' == applet_name[0]) {
+		// SENDMAIL
+		// save initial stdin (body or attachements can be piped!)
 		xdup2(STDIN_FILENO, INITIAL_STDIN_FILENO);
-		// -f must be specified
-		// -t may be multiple
-		opt_complementary = "-1:f:t::";
-		options = "C:w:U:P:X" "t:f:ns:c:";
-	// FETCHMAIL
+		opt_complementary = "-2:w+:t:t::"; // count(-t) > 0
+		options = "w:U:P:X" "ns:c:t:";
 	} else {
+		// FETCHMAIL
 		opt_after_connect = NULL;
-		opt_complementary = "=1:P";
-		options = "C:w:U:P:X" "tz";
+		opt_complementary = "-2:w+:P";
+		options = "w:U:P:X" "tz";
 	}
 	opts = getopt32(argv, options,
-		&opt_chdir, &opt_timeout, &opt_user, &opt_pass,
-		&recipients, &from, &subject, &charset
+		&timeout, &opt_user, &opt_pass,
+		&opt_subject, &opt_charset, &opt_recipients
 	);
 
 	//argc -= optind;
@@ -288,16 +320,9 @@
 	// first argument is remote server[:port]
 	opt_connect = *argv++;
 
-	if (opts & OPT_w)
-		timeout = xatou(opt_timeout);
-
-	// chdir
-	if (opts & OPT_C)
-		xchdir(opt_chdir);
-
 	// connect to server
 	if (opts & OPT_X) {
-		launch_helper(args);
+		launch_helper(xargs);
 	} else {
 		// no connection helper provided -> make plain connect
 		int fd = create_and_connect_stream_or_die(opt_connect, 0);
@@ -305,24 +330,30 @@
 		xdup2(STDIN_FILENO, STDOUT_FILENO);
 	}
 
-	// randomize
-	srand(time(NULL));
-
-	// SENDMAIL
-	if (recipients) {
+#if ENABLE_FETCHMAIL
+	if (opt_after_connect)
+#endif
+	{
+		// SENDMAIL
+		char *opt_from;
 		int code;
 		char *boundary;
+		const char *fmt;
+		const char *p;
+		char *q;
 
 		// wait for initial OK on plain connect
 		if (!(opts & OPT_X))
 			smtp_check(NULL, 220);
 
-		sane(from);
+		// get specified sender
+		opt_from = sane(*argv++);
+
 		// introduce to server
 		// should we respect modern (but useless here) EHLO?
 		// or should they respect we wanna be tiny?!
-		if (!ENABLE_FEATURE_SENDMAIL_EHLO || 250 != smtp_checkp("EHLO %s", from, -1)) {
-			smtp_checkp("HELO %s", from, 250);
+		if (!ENABLE_FEATURE_SENDMAIL_EHLO || 250 != smtp_checkp("EHLO %s", opt_from, -1)) {
+			smtp_checkp("HELO %s", opt_from, 250);
 		}
 
 		// set sender
@@ -330,13 +361,13 @@
 		// no authentication is possible
 		code = (opts & OPT_P) ? -1 : 250;
 		// first try softly without authentication
-		while (250 != smtp_checkp("MAIL FROM:<%s>", from, code)) {
+		while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) {
 			// MAIL FROM failed -> authentication needed
 			// do we have username?
 			if (!(opts & OPT_U)) {
 				// no! fetch it from "from" option
 				//opts |= OPT_U;
-				opt_user = xstrdup(from);
+				opt_user = xstrdup(opt_from);
 				*strchrnul(opt_user, '@') = '\0';
 			}
 			// now it seems we have username
@@ -351,30 +382,35 @@
 			// but now die on failure
 			code = 250;
 		}
+
 		// set recipients
-		for (llist_t *to = recipients; to; to = to->link) {
+		for (llist_t *to = opt_recipients; to; to = to->link) {
 			smtp_checkp("RCPT TO:<%s>", sane(to->data), 250);
 		}
 
 		// now put message
 		smtp_check("DATA", 354);
 		// put address headers
-		printf("From: %s\r\n", from);
-		for (llist_t *to = recipients; to; to = to->link) {
+		printf("From: %s\r\n", opt_from);
+		for (llist_t *to = opt_recipients; to; to = to->link) {
 			printf("To: %s\r\n", to->data);
 		}
 		// put encoded subject
 		if (opts & OPTS_c)
-			sane(charset);
+			sane((char *)opt_charset);
 		if (opts & OPTS_s) {
-			printf("Subject: =?%s?B?", charset);
-			uuencode(NULL, subject);
+			printf("Subject: =?%s?B?", opt_charset);
+			uuencode(NULL, opt_subject);
 			printf("?=\r\n");
 		}
 		// put notification
 		if (opts & OPTS_n)
-			printf("Disposition-Notification-To: %s\r\n", from);
+			printf("Disposition-Notification-To: %s\r\n", opt_from);
 		// put common headers and body start
+		// randomize
+#if ENABLE_FEATURE_SENDMAIL_BLOATY
+		srand(time(NULL));
+#endif
 		boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
 		printf(
 			USE_FEATURE_SENDMAIL_BLOATY("X-Mailer: busybox " BB_VER " sendmail\r\n")
@@ -386,16 +422,16 @@
 			, boundary
 		);
 		// put body + attachment(s)
-	{
-		const char *fmt =
+
+		fmt =
 			"\r\n--%s\r\n"
 			"%stext/plain; charset=%s\r\n"
 			"%s%s\r\n"
 			"%s"
 		;
-		const char *p = charset;
-		char *q = (char *)"";
-		while (argv[0]) {
+		p = opt_charset;
+		q = (char *)"";
+		while (*argv) {
 			printf(
 				fmt
 				, boundary
@@ -414,9 +450,9 @@
 			;
 			uuencode(*argv, NULL);
 			if (*(++argv))
-				q = bb_get_last_path_component_strip(argv[0]);
+				q = bb_get_last_path_component_strip(*argv);
 		}
-	}
+
 		// put terminator
 		printf("\r\n--%s--\r\n" "\r\n", boundary);
 		if (ENABLE_FEATURE_CLEAN_UP)
@@ -426,11 +462,25 @@
 		smtp_check(".", 250);
 		smtp_check("QUIT", 221);
 
-	// FETCHMAIL
+#if ENABLE_FETCHMAIL
 	} else {
-		// authenticate
+		// FETCHMAIL
 		char *buf;
 		unsigned nmsg;
+		char *hostname;
+		pid_t pid;
+		// cache fetch command
+		const char *retr = (opts & OPTF_t) ? "TOP %u 0" : "RETR %u";
+
+		// goto maildir
+		xchdir(*argv++);
+
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+		// cache postprocess program
+		*fargs = *argv;
+#endif
+		
+		// authenticate
 		if (!(opts & OPT_U)) {
 			//opts |= OPT_U;
 			opt_user = getenv("USER");
@@ -438,25 +488,29 @@
 #if ENABLE_FEATURE_FETCHMAIL_APOP
 		pop3_checkr(NULL, NULL, &buf);
 		// server supports APOP?
-		if ('<' == buf[4]) {
+		if ('<' == *buf) {
 			md5_ctx_t md5;
-			uint8_t hex[16*2 + 1];
 			// yes. compose <stamp><password>
 			char *s = strchr(buf, '>');
 			if (s)
 				strcpy(s+1, opt_pass);
-			s = buf+4;
+			s = buf;
 			// get md5 sum of <stamp><password>
 			md5_begin(&md5);
 			md5_hash(s, strlen(s), &md5);
 			md5_end(s, &md5);
-			bin2hex(hex, s, 16);
+			// NOTE: md5 struct contains enough space
+			// so we reuse md5 space instead of xzalloc(16*2+1)
+#define md5_hex ((uint8_t *)&md5)
+//			uint8_t *md5_hex = (uint8_t *)&md5;
+			*bin2hex(md5_hex, s, 16) = '\0';
 			// APOP
-			s = xasprintf("%s %s", opt_user, hex);
+			s = xasprintf("%s %s", opt_user, md5_hex);
+#undef md5_hex
 			pop3_check("APOP %s", s);
 			if (ENABLE_FEATURE_CLEAN_UP) {
 				free(s);
-				free(buf);
+				free(buf-4); // buf is "+OK " away from malloc'ed string
 			}
 		} else {
 #else
@@ -472,60 +526,64 @@
 		// get statistics
 		pop3_checkr("STAT", NULL, &buf);
 
+		// prepare message filename suffix
+		hostname = xzalloc(MAXHOSTNAMELEN+1);
+		gethostname(hostname, MAXHOSTNAMELEN);
+		pid = getpid();
+
 		// get number of messages
-		nmsg = atoi(buf+4);
+		// NOTE: we don't use xatou(buf) since buf is "nmsg nbytes"
+		// we only need nmsg and atoi is just exactly what we need
+		// if atoi fails to convert buf into number it returns 0
+		// in this case the following loop simply will not be executed 
+		nmsg = atoi(buf);
 		if (ENABLE_FEATURE_CLEAN_UP)
-			free(buf);
+			free(buf-4); // buf is "+OK " away from malloc'ed string
 
-		// lock maildir
-		////USE_FEATURE_CLEAN_UP(close)(xopen(".lock", O_CREAT | O_WRONLY | O_TRUNC | O_EXCL));
-		
-		// make tempnam(dir, salt) respect dir argument
-		unsetenv("TMPDIR");
-
-		// TODO: piping through external filter argv... if *argv
-
-		// cache fetch command
-	{
-		const char *retr = (opts & OPTF_t) ? "TOP %u 0" : "RETR %u";
 		// loop through messages
 		for (; nmsg; nmsg--) {
-			int fd;
-			char tmp_name[sizeof("tmp/XXXXXX")];
-			char new_name[sizeof("new/XXXXXX")];
-
+			char *filename = xasprintf("tmp/%llu.%u.%s", monotonic_us(), pid, hostname);
+			char *target;
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+		int rc;
+#endif
 			// retrieve message in ./tmp
-			strcpy(tmp_name, "tmp/XXXXXX");
-			fd = mkstemp(tmp_name);
-			if (fd < 0)
-				bb_perror_msg_and_die("cannot create unique file");
 			pop3_check(retr, (const char *)nmsg);
-			pop3_message(fd); // NB: closes fd
-
-			// move file to ./new atomically
-			strncpy(new_name, "new", 3);
-			strcpy(new_name + 3, tmp_name + 3);
-			if (rename(tmp_name, new_name) < 0) {
-				// rats! such file exists! try to make unique name
-				strcpy(new_name + 3, "tmp/XXXXXX" + 3);
-				fd = mkstemp(new_name);
-				if (fd < 0)
-					bb_perror_msg_and_die("cannot create unique file");
-				close(fd);
-				xrename(tmp_name, new_name);
-			}
-
+			pop3_message(filename);
 			// delete message from server
 			if (opts & OPTF_z)
 				pop3_check("DELE %u", (const char*)nmsg);
+
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+			// run postprocessing program
+			if (*fargs) {
+				fargs[1] = filename;
+				rc = wait4pid(spawn((char **)fargs));
+				if (99 == rc)
+					break;
+				if (1 == rc)
+					goto skip;
+			}
+#endif
+			// atomically move message to ./new
+			target = xstrdup(filename);
+			strncpy(target, "new", 3);
+			// ... or just stop receiving on error
+			if (rename_or_warn(filename, target))
+				break;
+			free(target);
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+ skip:
+#endif
+			free(filename);
 		}
-	}
 
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(hostname);
+
 		// Bye
 		pop3_check("QUIT", NULL);
-
-		// unlock maildir
-		////unlink(".lock");
+#endif // ENABLE_FETCHMAIL
 	}
 
 	return 0;




More information about the busybox-cvs mailing list