svn commit: trunk/busybox: include networking

vda at busybox.net vda at busybox.net
Fri Feb 8 18:24:54 UTC 2008


Author: vda
Date: 2008-02-08 10:24:54 -0800 (Fri, 08 Feb 2008)
New Revision: 20964

Log:
sendmail: update from maintainer



Modified:
   trunk/busybox/include/applets.h
   trunk/busybox/include/usage.h
   trunk/busybox/networking/Config.in
   trunk/busybox/networking/Kbuild
   trunk/busybox/networking/sendmail.c


Changeset:
Modified: trunk/busybox/include/applets.h
===================================================================
--- trunk/busybox/include/applets.h	2008-02-08 17:30:39 UTC (rev 20963)
+++ trunk/busybox/include/applets.h	2008-02-08 18:24:54 UTC (rev 20964)
@@ -151,6 +151,7 @@
 USE_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_NEVER, fdflush))
 USE_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_FETCHMAIL(APPLET_ODDNAME(fetchmail, sendgetmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, fetchmail))
 USE_FEATURE_GREP_FGREP_ALIAS(APPLET_NOUSAGE(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_NEVER, find))
 //USE_FINDFS(APPLET_NOUSAGE(findfs, tune2fs, _BB_DIR_SBIN, _BB_SUID_NEVER))
@@ -299,7 +300,7 @@
 USE_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER))
-USE_SENDMAIL(APPLET(sendmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SENDMAIL(APPLET_ODDNAME(sendmail, sendgetmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sendmail))
 USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
 USE_SEQ(APPLET_NOFORK(seq, seq, _BB_DIR_USR_BIN, _BB_SUID_NEVER, seq))
 USE_SESTATUS(APPLET(sestatus, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))

Modified: trunk/busybox/include/usage.h
===================================================================
--- trunk/busybox/include/usage.h	2008-02-08 17:30:39 UTC (rev 20963)
+++ trunk/busybox/include/usage.h	2008-02-08 18:24:54 UTC (rev 20964)
@@ -1017,6 +1017,19 @@
        "	-S SECTORS	Set the number of sectors\n" \
        "	-v		Give fdisk version"
 
+#define fetchmail_trivial_usage \
+       "[-C dir] [-w timeout] [-U user] -P password [-X] [-t] [-z] server[:port]"
+#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"
+
 #define find_trivial_usage \
        "[PATH...] [EXPRESSION]"
 #define find_full_usage \
@@ -3138,29 +3151,21 @@
 #define selinuxenabled_full_usage
 
 #define sendmail_trivial_usage \
-       "{-t to}+ {-f from} [-n[notify]] [-s subject] [-b file]*\n" \
-       "[-a attachment]* [-c charset]" \
-       USE_FEATURE_SENDMAIL_NETWORK("\n" \
-       " [-d] [-w timeout] [-h server] [-p port] [-U user] [-P password]" \
-       )
+       "[-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 ...]"
 #define sendmail_full_usage \
-       "Send an email <from> <to> with <subject> and optional attachments." \
-       "\n\nArguments:\n" \
+       "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" \
+       "	-X		Use openssl connection helper for secured servers\n" \
        "	-t to		Recipient email. May be multiple\n" \
        "	-f from		Sender address\n" \
-       "	-n[notify]	Optional notification address. If just -n given then notifies the sender\n" \
-       "	-s subject	Optional subject\n" \
-       "	-b filename	Optional body content file. May be multiple\n" \
-       "	-a filename	Optional file attachment. May be multiple\n" \
-       "	-c charset	Assumed charset for body and subject [koi8-r]" \
-       USE_FEATURE_SENDMAIL_NETWORK("\n" \
-       "	-d		Just dump composed message\n" \
-       "	-w timeout	Set timeout on network operations\n" \
-       "	-h server	Optional mail server name or IP [127.0.0.1]\n" \
-       "	-p port		Optional mail server port [25]\n" \
-       "	-U username	Authenticate using AUTH LOGIN with specified username\n" \
-       "	-P password	Authenticate using AUTH LOGIN with specified password" \
-       )
+       "	-n		Request delivery notification to sender\n" \
+       "	-s subject	Subject\n" \
+       "	-c charset	Assumed charset for body and subject [utf-8]"
 
 #define seq_trivial_usage \
        "[first [increment]] last"

Modified: trunk/busybox/networking/Config.in
===================================================================
--- trunk/busybox/networking/Config.in	2008-02-08 17:30:39 UTC (rev 20963)
+++ trunk/busybox/networking/Config.in	2008-02-08 18:24:54 UTC (rev 20964)
@@ -671,13 +671,40 @@
 	help
 	  Barebones sendmail.
 
-config FEATURE_SENDMAIL_NETWORK
-	bool "Support network connectivity"
-	default y
+config FEATURE_SENDMAIL_EHLO
+	bool "Support EHLO command"
+	default n
 	depends on SENDMAIL
 	help
-	  Add ability to send, not only compose messages.
+	  Support ESMTP EHLO command.
 
+config FEATURE_SENDMAIL_BLOATY
+	bool "Be more verbose"
+	default n
+	depends on SENDMAIL
+	help
+	  Should be turned off.
+
+config FETCHMAIL
+	bool "fetchmail"
+	default n
+	help
+	  Barebones fetchmail.
+
+config FEATURE_FETCHMAIL_APOP
+	bool "Support APOP authentication"
+	default y
+	depends on FETCHMAIL
+	help
+	  Support secure APOP authentication.
+
+config FEATURE_FETCHMAIL_FILTER
+	bool "Pipe thru external filter"
+	default n
+	depends on FETCHMAIL
+	help
+	  Support piping incoming messages thru external filter.
+
 config SLATTACH
 	bool "slattach"
 	default n

Modified: trunk/busybox/networking/Kbuild
===================================================================
--- trunk/busybox/networking/Kbuild	2008-02-08 17:30:39 UTC (rev 20963)
+++ trunk/busybox/networking/Kbuild	2008-02-08 18:24:54 UTC (rev 20964)
@@ -11,6 +11,7 @@
 lib-$(CONFIG_DNSD)         += dnsd.o
 lib-$(CONFIG_ETHER_WAKE)   += ether-wake.o
 lib-$(CONFIG_FAKEIDENTD)   += isrv_identd.o isrv.o
+lib-$(CONFIG_FETCHMAIL)    += sendmail.o
 lib-$(CONFIG_FTPGET)       += ftpgetput.o
 lib-$(CONFIG_FTPPUT)       += ftpgetput.o
 lib-$(CONFIG_HOSTNAME)     += hostname.o

Modified: trunk/busybox/networking/sendmail.c
===================================================================
--- trunk/busybox/networking/sendmail.c	2008-02-08 17:30:39 UTC (rev 20963)
+++ trunk/busybox/networking/sendmail.c	2008-02-08 18:24:54 UTC (rev 20964)
@@ -1,6 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /*
- * bare bones sendmail
+ * bare bones sendmail/fetchmail
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov at gmail.com>
  *
@@ -8,23 +8,24 @@
  */
 #include "libbb.h"
 
-/*
-   Extracted from BB uuencode.c
- */
-enum {
-	SRC_BUF_SIZE = 45,  /* This *MUST* be a multiple of 3 */
-	DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
-};
+#define INITIAL_STDIN_FILENO 3
 
 static void uuencode(char *fname, const char *text)
 {
+	enum {
+		SRC_BUF_SIZE = 45,  /* This *MUST* be a multiple of 3 */
+		DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
+	};
+
 #define src_buf text
 	int fd;
 #define len fd
 	char dst_buf[DST_BUF_SIZE + 1];
 
 	if (fname) {
-		fd = xopen(fname, O_RDONLY);
+		fd = INITIAL_STDIN_FILENO;
+		if (NOT_LONE_DASH(fname))
+			fd = xopen(fname, O_RDONLY);
 		src_buf = bb_common_bufsiz1;
 	} else {
 		len = strlen(text);
@@ -44,71 +45,149 @@
 		}
 		if (!size)
 			break;
-		// Encode the buffer we just read in
+		// encode the buffer we just read in
 		bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
 		if (fname) {
-			xwrite(STDOUT_FILENO, "\n", 1);
+			xwrite(STDOUT_FILENO, "\r\n", 2);
 		} else {
 			src_buf += size;
 			len -= size;
 		}
 		xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3));
 	}
-	if (ENABLE_FEATURE_CLEAN_UP && fname)
+	if (fname)
 		close(fd);
 }
 
-#if ENABLE_FEATURE_SENDMAIL_NETWORK
+static pid_t helper_pid;
+
+static void kill_helper(void)
+{
+	// TODO!!!: is there more elegant way to terminate child on program failure?
+	if (helper_pid > 0)
+		kill(helper_pid, SIGTERM);
+}
+
 // generic signal handler
 static void signal_handler(int signo)
 {
 	int err;
 
-	if (SIGALRM == signo)
+	if (SIGALRM == signo) {
+		kill_helper();
 		bb_error_msg_and_die("timed out");
+	}
 
 	// SIGCHLD. reap zombies
 	if (wait_any_nohang(&err) > 0)
 		if (WIFEXITED(err) && WEXITSTATUS(err))
+#if ENABLE_FEATURE_SENDMAIL_BLOATY
 			bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err));
+#else
+			bb_error_msg_and_die("child failed");
+#endif
 }
 
-static pid_t helper_pid;
+static void launch_helper(const char **argv)
+{
+	// setup vanilla unidirectional pipes interchange
+	int idx;
+	int pipes[4];
+	xpipe(pipes);
+	xpipe(pipes+2);
+	helper_pid = vfork();
+	if (helper_pid < 0)
+		bb_perror_msg_and_die("vfork");
+	idx = (!helper_pid)*2;
+	xdup2(pipes[idx], STDIN_FILENO);
+	xdup2(pipes[3-idx], STDOUT_FILENO);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		for (int i = 4; --i >= 0; )
+			if (pipes[i] > STDOUT_FILENO)
+				close(pipes[i]);
+	if (!helper_pid) {
+		// child - try to execute connection helper
+		BB_EXECVP(argv[0], (char **)argv);
+		_exit(127);
+	}
+	// parent - check whether child is alive
+	sig_catch(SIGCHLD, signal_handler);
+	sig_catch(SIGALRM, signal_handler);
+	signal_handler(SIGCHLD);
+	// child seems OK -> parent goes on
+}
 
-// read stdin, parses first bytes to a number, i.e. server response
-// if code = -1 then just return this number
-// if code != -1 then checks whether the number equals the code
-// if not equal -> die saying errmsg
-static int check(int code, const char *errmsg)
+static unsigned timeout;
+
+static char *command(const char *fmt, const char *param)
 {
+	char *msg = (char *)fmt;
+	alarm(timeout);
+	if (msg) {
+//		if (param)
+			msg = xasprintf(fmt, param);
+		printf("%s\r\n", msg);
+	}
+	fflush(stdout);
+	return msg;
+}
+
+static int smtp_checkp(const char *fmt, const char *param, int code)
+{
 	char *answer;
-
-	// read a string and match it against the set of available answers
-	fflush(stdout);
+	char *msg = command(fmt, param);
+	// read stdin
+	// if the string has a form \d\d\d- -- read next string. E.g. EHLO response
+	// parse first bytes to a number
+	// if code = -1 then just return this number
+	// 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]) ;
+#else
 	answer = xmalloc_getline(stdin);
+#endif
 	if (answer) {
 		int n = atoi(answer);
+		alarm(0);
+		if (ENABLE_FEATURE_CLEAN_UP) {
+			free(msg);
+			free(answer);
+		}
 		if (-1 == code || n == code) {
-			free(answer);
 			return n;
 		}
 	}
-	// TODO: is there more elegant way to terminate child on program failure?
-	if (helper_pid > 0)
-		kill(helper_pid, SIGTERM);
-	if (!answer)
-		answer = (char*)"EOF";
-	else
-		*strchrnul(answer, '\r') = '\0';
-	bb_error_msg_and_die("error at %s: got '%s' instead", errmsg, answer);
+	kill_helper();
+	bb_error_msg_and_die("%s failed", msg);
 }
 
-static int puts_and_check(const char *msg, int code, const char *errmsg)
+static int smtp_check(const char *fmt, int code)
 {
-	printf("%s\r\n", msg);
-	return check(code, errmsg);
+	return smtp_checkp(fmt, NULL, code);
 }
 
+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]) {
+		alarm(0);
+		if (ret)
+			*ret = answer;
+		else
+			free(answer);
+		return;
+	}
+	kill_helper();
+	bb_error_msg_and_die("%s failed", msg);
+}
+
+static void pop3_check(const char *fmt, const char *param)
+{
+	pop3_checkr(fmt, param, NULL);
+}
+
 // strip argument of bad chars
 static char *sane(char *str)
 {
@@ -123,215 +202,332 @@
 	*p = '\0';
 	return str;
 }
-#endif
 
-int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int sendmail_main(int argc, char **argv)
+static void pop3_message(int fd)
 {
+	char *answer;
+	// read stdin, copy to file fd
+	while ((answer = xmalloc_fgets_str(stdin, "\r\n"))) {
+		char *s = answer;
+		if ('.' == answer[0]) {
+			if ('.' == answer[1])
+				s++;
+			else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
+				break;
+		}
+		xwrite(fd, s, strlen(s));
+		free(answer);
+	}
+	close(fd);
+}
+
+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;
-	llist_t *bodies = NULL;
-	llist_t *attachments = NULL;
 	char *from;
-	char *notify = NULL;
 	const char *subject;
-	char *charset = (char*)"utf-8";
-#if ENABLE_FEATURE_SENDMAIL_NETWORK
-	const char *wsecs = "10";
-	const char *server = "127.0.0.1";
-	const char *port = NULL;
+	char *charset = (char *)"utf-8";
+
 	const char *opt_user;
 	const char *opt_pass;
-	unsigned timeout;
-#endif
-	char *boundary;
-	unsigned opts;
+	const char *opt_timeout;
+	const char *opt_chdir;
+
 	enum {
-		OPT_f = 1 << 0,         // sender
-		OPT_n = 1 << 2,         // notification
-		OPT_s = 1 << 3,         // subject given
-		OPT_c = 1 << 6,         // charset
-		OPT_d = 1 << 7,         // dry run - no networking
-		OPT_w = 1 << 8,         // network timeout
-		OPT_h = 1 << 9,         // server
-		OPT_p = 1 << 10,        // port
-		OPT_U = 1 << 11,        // user specified
-		OPT_P = 1 << 12,        // password specified
+		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
+
+		OPTS_t = 1 << 5,        // sendmail "to"
+		OPTF_t = 1 << 5,        // fetchmail "TOP"
+
+		OPTS_f = 1 << 6,        // sendmail "from"
+		OPTF_z = 1 << 6,        // fetchmail "delete"
+
+		OPTS_n = 1 << 7,        // notification
+		OPTS_s = 1 << 8,        // subject given
+		OPTS_c = 1 << 9,        // charset for subject and body
 	};
 
-	// -f must be specified
-	// -t, -b, -a may be multiple
-	opt_complementary = "f:t::b::a::";
-	opts = getopt32(argv,
-		"f:t:n::s:b:a:c:" USE_FEATURE_SENDMAIL_NETWORK("dw:h:p:U:P:"),
-		&from, &recipients, &notify, &subject, &bodies, &attachments, &charset
-		USE_FEATURE_SENDMAIL_NETWORK(, &wsecs, &server, &port, &opt_user, &opt_pass)
+	const char *options;
+	unsigned opts;
+
+	// SENDMAIL
+	if ('s' == applet_name[0]) {
+		// save initial stdin
+		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
+	} else {
+		opt_after_connect = NULL;
+		opt_complementary = "=1:P";
+		options = "C:w:U:P:X" "tz";
+	}
+	opts = getopt32(argv, options,
+		&opt_chdir, &opt_timeout, &opt_user, &opt_pass,
+		&recipients, &from, &subject, &charset
 	);
+
 	//argc -= optind;
 	argv += optind;
 
-	// sanitize user input
-	sane(from);
-	if (opts & OPT_c)
-		sane(charset);
+	// first argument is remote server[:port]
+	opt_connect = *argv++;
 
-	// establish connection
-#if ENABLE_FEATURE_SENDMAIL_NETWORK
-	timeout = xatou(wsecs);
-	if (!(opts & OPT_d)) {
-		// ask password if we need to and while we're still have terminal
-		// TODO: get password directly from /dev/tty? or from a secret file?
-		if ((opts & (OPT_U+OPT_P)) == OPT_U) {
-			if (!isatty(STDIN_FILENO) || !(opt_pass = bb_askpass(0, "Password: "))) {
-				bb_error_msg_and_die("no password");
-			}
+	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);
+	} else {
+		// no connection helper provided -> make plain connect
+		int fd = create_and_connect_stream_or_die(opt_connect, 0);
+		xmove_fd(fd, STDIN_FILENO);
+		xdup2(STDIN_FILENO, STDOUT_FILENO);
+	}
+
+	// randomize
+	srand(time(NULL));
+
+	// SENDMAIL
+	if (recipients) {
+		int code;
+		char *boundary;
+
+		// wait for initial OK on plain connect
+		if (!(opts & OPT_X))
+			smtp_check(NULL, 220);
+
+		sane(from);
+		// 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);
 		}
-		// set chat timeout
-		alarm(timeout);
-		// connect to server
-		if (argv[0]) {
-			// if connection helper given
-			// setup vanilla unidirectional pipes interchange
-			int idx;
-			int pipes[4];
-			xpipe(pipes);
-			xpipe(pipes+2);
-			helper_pid = vfork();
-			if (helper_pid < 0)
-				bb_perror_msg_and_die("vfork");
-			idx = (!helper_pid)*2;
-			xdup2(pipes[idx], STDIN_FILENO);
-			xdup2(pipes[3-idx], STDOUT_FILENO);
-			if (ENABLE_FEATURE_CLEAN_UP)
-				for (int i = 4; --i >= 0;)
-					if (pipes[i] > STDOUT_FILENO)
-						close(pipes[i]);
-			// replace child with connection helper
-			if (!helper_pid) {
-				// child - try to execute connection helper
-				BB_EXECVP(argv[0], argv);
-				_exit(127);
+
+		// set sender
+		// NOTE: if password has not been specified ->
+		// no authentication is possible
+		code = (opts & OPT_P) ? -1 : 250;
+		// first try softly without authentication
+		while (250 != smtp_checkp("MAIL FROM:<%s>", 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);
+				*strchrnul(opt_user, '@') = '\0';
 			}
-			// parent - check whether child is alive
-			sig_catch(SIGCHLD, signal_handler);
-			sig_catch(SIGALRM, signal_handler);
-			signal_handler(SIGCHLD);
-			// child seems OK -> parent goes on SMTP chat
-		} else {
-			// no connection helper provided -> make plain connect
-			int fd = create_and_connect_stream_or_die(
-				server,
-				bb_lookup_port(port, "tcp", 25)
-			);
-			xmove_fd(fd, STDIN_FILENO);
-			xdup2(STDIN_FILENO, STDOUT_FILENO);
-			// wait for OK
-			check(220, "INIT");
+			// now it seems we have username
+			// try to authenticate
+			if (334 == smtp_check("AUTH LOGIN", -1)) {
+				uuencode(NULL, opt_user);
+				smtp_check("", 334);
+				uuencode(NULL, opt_pass);
+				smtp_check("", 235);
+			}
+			// authenticated -> retry set sender
+			// but now die on failure
+			code = 250;
 		}
-		// mail user specified? try modern AUTHentication
-		if ((opts & OPT_U)
-		 && (334 == puts_and_check("auth login", -1, "auth login"))
-		) {
-			uuencode(NULL, opt_user);
-			puts_and_check("", 334, "AUTH");
-			uuencode(NULL, opt_pass);
-			puts_and_check("", 235, "AUTH");
-		// no mail user specified or modern AUTHentication is not supported?
-		} else {
-			// fallback to simple HELO authentication
-			// fetch domain name (defaults to local)
-			const char *domain = strchr(from, '@');
-			if (domain)
-				domain++;
-			else
-				domain = "local";
-			printf("helo %s\r\n", domain);
-			check(250, "HELO");
+		// set recipients
+		for (llist_t *to = recipients; to; to = to->link) {
+			smtp_checkp("RCPT TO:<%s>", sane(to->data), 250);
 		}
 
-		// set addresses
-		printf("mail from:<%s>\r\n", from);
-		check(250, "MAIL FROM");
+		// 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("rcpt to:<%s>\r\n", sane(to->data));
-			check(250, "RCPT TO");
+			printf("To: %s\r\n", to->data);
 		}
-		puts_and_check("data", 354, "DATA");
-		// no timeout while sending message
-		alarm(0);
-	}
-#endif
-
-	// now put message
-	// put address headers
-	printf("From: %s\r\n", from);
-	for (llist_t *to = recipients; to; to = to->link) {
-		printf("To: %s\r\n", sane(to->data));
-	}
-	// put encoded subject
-	if (opts & OPT_s) {
-		printf("Subject: =?%s?B?", charset);
-		uuencode(NULL, subject);
-		puts("?=\r");
-	}
-	// put notification
-	if (opts & OPT_n) {
-		// -n without parameter?
-		if (!notify)
-			notify = from; // notify sender by default
-		printf("Disposition-Notification-To: %s\r\n", sane(notify));
-	}
-	// put common headers and body start
-	//srand(?);
-	boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
-	printf(
-		"X-Mailer: busybox " BB_VER " sendmail\r\n"
-		"Message-ID: <%s>\r\n"
-		"Mime-Version: 1.0\r\n"
-		"%smultipart/mixed; boundary=\"%s\"\r\n"
-		"\r\n"
-		"--%s\r\n"
-		"%stext/plain; charset=%s\r\n"
-		"%s\r\n%s"
-		, boundary
-		, "Content-Type: "
-		, boundary, boundary
-		, "Content-Type: "
-		, charset
-		, "Content-Disposition: inline"
-		, "Content-Transfer-Encoding: base64\r\n"
-	);
-	// put body(ies)
-	for (llist_t *f = bodies; f; f = f->link) {
-		uuencode(f->data, NULL);
-	}
-	// put attachment(s)
-	for (llist_t *f = attachments; f; f = f->link) {
+		// put encoded subject
+		if (opts & OPTS_c)
+			sane(charset);
+		if (opts & OPTS_s) {
+			printf("Subject: =?%s?B?", charset);
+			uuencode(NULL, subject);
+			printf("?=\r\n");
+		}
+		// put notification
+		if (opts & OPTS_n)
+			printf("Disposition-Notification-To: %s\r\n", from);
+		// put common headers and body start
+		boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
 		printf(
-			"\r\n--%s\r\n"
-			"%sapplication/octet-stream\r\n"
-			"%s; filename=\"%s\"\r\n"
-			"%s"
+			USE_FEATURE_SENDMAIL_BLOATY("X-Mailer: busybox " BB_VER " sendmail\r\n")
+			"Message-ID: <%s>\r\n"
+			"Mime-Version: 1.0\r\n"
+			"%smultipart/mixed; boundary=\"%s\"\r\n"
 			, boundary
 			, "Content-Type: "
-			, "Content-Disposition: inline"
-			, bb_get_last_path_component_strip(f->data)
-			, "Content-Transfer-Encoding: base64\r\n"
+			, boundary
 		);
-		uuencode(f->data, NULL);
+		// put body + attachment(s)
+	{
+		const char *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]) {
+			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"
+			;
+			uuencode(*argv, NULL);
+			if (*(++argv))
+				q = bb_get_last_path_component_strip(argv[0]);
+		}
 	}
-	// put terminator
-	printf("\r\n--%s--\r\n\r\n", boundary);
-	if (ENABLE_FEATURE_CLEAN_UP)
-		free(boundary);
+		// put terminator
+		printf("\r\n--%s--\r\n" "\r\n", boundary);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(boundary);
 
-#if ENABLE_FEATURE_SENDMAIL_NETWORK
-	// end message and say goodbye
-	if (!(opts & OPT_d)) {
-		alarm(timeout);
-		puts_and_check(".", 250, "BODY");
-		puts_and_check("quit", 221, "QUIT");
-	}
+		// end message and say goodbye
+		smtp_check(".", 250);
+		smtp_check("QUIT", 221);
+
+	// FETCHMAIL
+	} else {
+		// authenticate
+		char *buf;
+		unsigned nmsg;
+		if (!(opts & OPT_U)) {
+			//opts |= OPT_U;
+			opt_user = getenv("USER");
+		}
+#if ENABLE_FEATURE_FETCHMAIL_APOP
+		pop3_checkr(NULL, NULL, &buf);
+		// server supports APOP?
+		if ('<' == buf[4]) {
+			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;
+			// get md5 sum of <stamp><password>
+			md5_begin(&md5);
+			md5_hash(s, strlen(s), &md5);
+			md5_end(s, &md5);
+			bin2hex(hex, s, 16);
+			// APOP
+			s = xasprintf("%s %s", opt_user, hex);
+			pop3_check("APOP %s", s);
+			if (ENABLE_FEATURE_CLEAN_UP) {
+				free(s);
+				free(buf);
+			}
+		} else {
+#else
+		{
+			pop3_check(NULL, NULL);
 #endif
+			// USER
+			pop3_check("USER %s", opt_user);
+			// PASS
+			pop3_check("PASS %s", opt_pass);
+		}
 
+		// get statistics
+		pop3_checkr("STAT", NULL, &buf);
+
+		// get number of messages
+		nmsg = atoi(buf+4);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(buf);
+
+		// 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 thru external filter argv... if *argv
+
+		// cache fetch command
+	{
+		const char *retr = (opts & OPTF_t) ? "TOP %u 0" : "RETR %u";
+		// loop thru messages
+		for (; nmsg; nmsg--) {
+			int fd;
+			char tmp_name[sizeof("tmp/XXXXXX")];
+			char new_name[sizeof("new/XXXXXX")];
+
+			// 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);
+				if (rename(tmp_name, new_name) < 0) {
+					// something is very wrong
+					bb_perror_msg_and_die("cannot move %s to %s", tmp_name, new_name);
+				}
+			}
+
+			// delete message from server
+			if (opts & OPTF_z)
+				pop3_check("DELE %u", (const char*)nmsg);
+		}
+	}
+
+		// Bye
+		pop3_check("QUIT", NULL);
+
+		// unlock maildir
+		////unlink(".lock");
+	}
+
 	return 0;
 }




More information about the busybox-cvs mailing list