DM_DEFAULT_ENCODING: Be explicit that we're using 7-bit ASCII when sending email.
[cfb.git] / prod / net / jaekl / qd / util / SendMail.java
index ce21ea3ba81538694d250b3820b8d98463fa5067..33be358b31edc0c28e7b232861e89419a76357ca 100644 (file)
@@ -3,10 +3,13 @@ package net.jaekl.qd.util;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -17,8 +20,17 @@ public class SendMail {
        private static final String RESP_251 = "251";   // mail address change (we don't support this)
        private static final String RESP_354 = "354";   // OK, proceed with DATA transmission
        
+       // Actually, RFC821 (with which all SMTP should be backwards-compatible)
+       // requires all control transmissions to be done in US-ASCII.
+       // However, there's no point in throwing an exception if the server with which 
+       // we're communicating sends bytes with the high bit set.  So, let's read from 
+       // the socket with the only Charset that Java guarantees will be available that 
+       // can also read arbitrary sequences of 8-bit bytes.
+       private static final String SMTP_CHARSET = "ISO-8859-1";
+       
        String m_hostName;
        String m_boundary;
+       Charset m_charset;
        
        String m_smtpHost;
        int m_smtpPort;
@@ -55,9 +67,9 @@ public class SendMail {
        public void send() throws MailException 
        {
                try (
-                               Socket sock = new Socket(m_smtpHost, m_smtpPort);
-                               PrintWriter pw = new PrintWriter(sock.getOutputStream(), true);
-                               BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
+                               Socket sock = openSocket(m_smtpHost, m_smtpPort);
+                               PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream(), StandardCharsets.US_ASCII), true);
+                               BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream(), getCharset()));
                        ) 
                {
                        send(pw, br);
@@ -67,6 +79,10 @@ public class SendMail {
                }
        }
        
+       Socket openSocket(String host, int port) throws UnknownHostException, IOException {
+               return new Socket(host, port);
+       }
+       
        String getHostName() throws UnknownHostException { 
                if (null == m_hostName) {
                        m_hostName = InetAddress.getLocalHost().getHostName();
@@ -84,6 +100,7 @@ public class SendMail {
                        validateResponse("", RESP_220, line);
                        
                        cmd = "HELO " + getHostName();
+                       line = sendLine(pw, br, cmd);
                        validateResponse(cmd, RESP_250, line);
                        
                        cmd = "MAIL FROM: " + m_from;
@@ -124,14 +141,14 @@ public class SendMail {
                
                sendMimeParts(pw);
                
-               String result = sendLine(pw, br, "\r\n.\r\n");
+               String result = sendLine(pw, br, "\r\n.");
                return result;
        }
        
        void sendMimeHeaders(PrintWriter pw)
        {
                sendLine(pw, "MIME-Version: 1.0");
-               sendLine(pw, "Content-Type: multipart/mixed; boundary=" + getBoundary());
+               sendLine(pw, "Content-Type: multipart/mixed; boundary=\"" + getBoundary() + "\"");
        }
        
        void sendMimeParts(PrintWriter pw) {
@@ -140,6 +157,7 @@ public class SendMail {
                        sendLine(pw, "Content-Type: " + part.getMimeType());    // TODO:  Add support for encodings
                        sendLine(pw, "");
                        sendLine(pw, part.getContent());
+                       sendLine(pw, "");
                }
                sendLine(pw, "--" + getBoundary() + "--");
        }
@@ -153,6 +171,13 @@ public class SendMail {
                return true;
        }
        
+       Charset getCharset() {
+               if (null == m_charset) {
+                       m_charset = Charset.forName(SMTP_CHARSET);
+               }
+               return m_charset;
+       }
+       
        String getBoundary()
        {
                if (null != m_boundary) {
@@ -188,7 +213,7 @@ public class SendMail {
                }
        }
        
-       void validateResponse(String cmd, String actual, String expected) throws MailException 
+       void validateResponse(String cmd, String expected, String actual) throws MailException 
        {
                if (! actual.startsWith(expected)) {
                        throw new MailException(cmd, expected, actual);
@@ -280,6 +305,7 @@ public class SendMail {
        void sendLine(PrintWriter pw, String line)
        {
                pw.write(line + "\r\n");
+               pw.flush();
        }
 
        String sendLine(PrintWriter pw, BufferedReader br, String line) throws IOException