sendmail: update by Vladimir

function                                             old     new   delta
sendmail_main                                        897     939     +42
rcptto                                                17      40     +23

Signed-off-by: Vladimir Dronnikov <dronnikov@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index 014b20d..a216d66 100644
--- a/mailutils/sendmail.c
+++ b/mailutils/sendmail.c
@@ -9,6 +9,10 @@
 #include "libbb.h"
 #include "mail.h"
 
+// limit maximum allowed number of headers to prevent overflows.
+// set to 0 to not limit
+#define MAX_HEADERS 256
+
 static int smtp_checkp(const char *fmt, const char *param, int code)
 {
 	char *answer;
@@ -55,7 +59,9 @@
 
 static void rcptto(const char *s)
 {
-	smtp_checkp("RCPT TO:<%s>", s, 250);
+	// N.B. we don't die if recipient is rejected, for the other recipients may be accepted
+	if (250 != smtp_checkp("RCPT TO:<%s>", s, -1))
+		bb_error_msg("Bad recipient: <%s>", s);
 }
 
 int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -66,6 +72,7 @@
 	char *s;
 	llist_t *list = NULL;
 	char *domain = sane_address(safe_getdomainname());
+	unsigned nheaders = 0;
 	int code;
 
 	enum {
@@ -197,6 +204,7 @@
 	// and then use the rest of stdin as message body
 	code = 0; // set "analyze headers" mode
 	while ((s = xmalloc_fgetline(G.fp0)) != NULL) {
+ dump:
 		// put message lines doubling leading dots
 		if (code) {
 			// escape leading dots
@@ -215,41 +223,62 @@
 		// 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);
+			goto addheader;
 		// 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:
+		// N.B. we allow MAX_HEADERS generic headers at most to prevent attacks
+		} else if (strchr(s, ':')) {
+ addheader:
+			if (MAX_HEADERS && ++nheaders >= MAX_HEADERS)
+				goto bail;
 			llist_add_to_end(&list, s);
-		// the empty line stops analyzing headers
+		// a line without ":" (an empty line too, by definition) doesn't look like a valid header
+		// so stop "analyze headers" mode
 		} else {
-			free(s);
+ reenter:
 			// put recipients specified on cmdline
 			while (*argv) {
-				s = sane_address(*argv);
-				rcptto(s);
-				llist_add_to_end(&list, xasprintf("To: %s", s));
+				char *t = sane_address(*argv);
+				rcptto(t);
+				//if (MAX_HEADERS && ++nheaders >= MAX_HEADERS)
+				//	goto bail;
+				llist_add_to_end(&list, xasprintf("To: %s", t));
 				argv++;
 			}
 			// enter "put message" mode
-			smtp_check("DATA", 354);
+			// N.B. DATA fails iff no recipients were accepted (or even provided)
+			// in this case just bail out gracefully
+			if (354 != smtp_check("DATA", -1))
+				goto bail;
 			// 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++;
+			// N.B. !s means: we read nothing, and nothing to be read in the future.
+			// just dump empty line and break the loop
+			if (!s) {
+				puts("\r");
+				break;
+			}
+			// go dump message body
+			// N.B. "s" already contains the first non-header line, so pretend we read it from input
+			goto dump;
 		}
 	}
+	// odd case: we didn't stop "analyze headers" mode -> message body is empty. Reenter the loop
+	// N.B. after reenter code will be > 0
+	if (!code)
+		goto reenter;
 
 	// finalize the message
 	smtp_check(".", 250);
+ bail:
 	// ... and say goodbye
 	smtp_check("QUIT", 221);
 	// cleanup