Ssmtp puts stdin into non-blocking mode. This means that fgets might return without a newline if the process writing to the pipe pauses. The code assumes that the only reason fgets would return without a newline is if the line is too long for the buffer, so it writes the entire buffer. Since fgets is guaranteed to place a nul after the data, we can use strlen to figure out how much to write.

I am attaching a small Perl script that demonstrates the problem and a patch.

Thanks

- Dave
Common subdirectories: ssmtp-original/debian and ssmtp-2.62/debian
Common subdirectories: ssmtp-original/md5auth and ssmtp-2.62/md5auth
Common subdirectories: ssmtp-original/.pc and ssmtp-2.62/.pc
diff -u ssmtp-original/ssmtp.c ssmtp-2.62/ssmtp.c
--- ssmtp-original/ssmtp.c	2009-11-29 13:47:29.000000000 -0500
+++ ssmtp-2.62/ssmtp.c	2009-11-29 13:51:18.000000000 -0500
@@ -1649,12 +1649,12 @@
 			outbytes += smtp_write(sock, "%s", leadingdot ? b : buf);
 		} else {
 			if (log_level > 0) {
-				log_event(LOG_INFO, "Sent a very long line in chunks");
+				log_event(LOG_INFO, "Sending a partial line");
 			}
 			if (leadingdot) {
-				outbytes += fd_puts(sock, b, sizeof(b));
+				outbytes += fd_puts(sock, b, strlen(b));
 			} else {
-				outbytes += fd_puts(sock, buf, bufsize);
+				outbytes += fd_puts(sock, buf, strlen(buf));
 			}
 		}
 		(void)alarm((unsigned) MEDWAIT);
#!/usr/bin/perl -w

use strict;

# Change this Address to use your own server!
my $to_addr = "dmearns-d...@dmearns.com";
my $from_addr = "postmast...@localhost";
my $bin = "/usr/sbin/ssmtp";

my $dd=`date --rfc-2822`;
chomp( $dd );

open( P, "| $bin $to_addr" );
select((select(P), $| = 1)[0]);

print P qq(From: $from_addr
Date: $dd
To: $to_addr
Subject: test

line1
line2
);

for (my $i=0; $i<3; $i++) {
  print P "line3...";
  sleep 5;
}

print P "\nline 4\n";
close P;

Reply via email to