Search in sources :

Example 1 with Attachment

use of i2p.susi.webmail.Attachment in project i2p.i2p by i2p.

the class SMTPClient method sendMail.

/**
 *  @param body without the attachments
 *  @param attachments may be null
 *  @param boundary non-null if attachments is non-null
 *  @return success
 */
public boolean sendMail(String host, int port, String user, String pass, String sender, String[] recipients, StringBuilder body, List<Attachment> attachments, String boundary) {
    boolean mailSent = false;
    boolean ok = true;
    Writer out = null;
    try {
        socket = InternalSocket.getSocket(host, port);
    } catch (IOException e) {
        error += _t("Cannot connect") + " (" + host + ':' + port + ") : " + e.getMessage() + '\n';
        ok = false;
    }
    try {
        // AUTH ref: RFC 4954
        if (ok) {
            socket.setSoTimeout(120 * 1000);
            int result = sendCmd(null);
            if (result != 220) {
                if (result != 0)
                    error += _t("Server refused connection") + " (" + result + ")\n";
                else
                    error += _t("Cannot connect") + " (" + host + ':' + port + ")\n";
                ok = false;
            }
        }
        if (ok) {
            sendCmdNoWait("EHLO localhost");
            socket.getOutputStream().flush();
            socket.setSoTimeout(60 * 1000);
            Result r = getFullResult();
            if (r.result == 250) {
                String[] caps = DataHelper.split(r.recv, "\r");
                for (String c : caps) {
                    if (c.equals("PIPELINING")) {
                        supportsPipelining = true;
                        Debug.debug(Debug.DEBUG, "Server supports pipelining");
                    } else if (c.startsWith("SIZE ")) {
                        try {
                            maxSize = Long.parseLong(c.substring(5));
                            Debug.debug(Debug.DEBUG, "Server max size: " + maxSize);
                        } catch (NumberFormatException nfe) {
                        }
                    } else if (c.equals("8BITMIME")) {
                        // unused, see encoding/EightBit.java
                        eightBitMime = true;
                        Debug.debug(Debug.DEBUG, "Server supports 8bitmime");
                    }
                }
            } else {
                error += _t("Server refused connection") + " (" + r + ")\n";
                ok = false;
            }
        }
        if (ok && maxSize < DEFAULT_MAX_SIZE) {
            Debug.debug(Debug.DEBUG, "Rechecking with new max size");
            // recalculate whether we'll fit
            // copied from WebMail
            long total = body.length();
            if (attachments != null && !attachments.isEmpty()) {
                for (Attachment a : attachments) {
                    total += a.getSize();
                }
            }
            long binaryMax = (long) ((maxSize * 57.0d / 78) - 32 * 1024);
            if (total > binaryMax) {
                ok = false;
                error += _t("Email is too large, max is {0}", DataHelper.formatSize2(binaryMax, false) + 'B') + '\n';
            }
        }
        if (ok) {
            // RFC 4954 says AUTH must be the last but let's assume
            // that includes the user/pass on following lines
            List<SendExpect> cmds = new ArrayList<SendExpect>();
            cmds.add(new SendExpect("AUTH LOGIN", 334));
            cmds.add(new SendExpect(base64.encode(user), 334));
            cmds.add(new SendExpect(base64.encode(pass), 235));
            if (sendCmds(cmds) != 3) {
                error += _t("Login failed") + '\n';
                ok = false;
            }
        }
        if (ok) {
            List<SendExpect> cmds = new ArrayList<SendExpect>();
            cmds.add(new SendExpect("MAIL FROM: " + sender, 250));
            for (int i = 0; i < recipients.length; i++) {
                cmds.add(new SendExpect("RCPT TO: " + recipients[i], 250));
            }
            cmds.add(new SendExpect("DATA", 354));
            if (sendCmds(cmds) != cmds.size()) {
                // TODO which recipient?
                error += _t("Mail rejected") + '\n';
                ok = false;
            }
        }
        if (ok) {
            // in-memory replace, no copies
            DataHelper.replace(body, "\r\n.\r\n", "\r\n..\r\n");
            // socket.getOutputStream().write(DataHelper.getUTF8(body));
            // socket.getOutputStream().write(DataHelper.getASCII("\r\n.\r\n"));
            // Do it this way so we don't double the memory
            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "ISO-8859-1"));
            out.write(body.toString());
            // and check the max total size
            if (attachments != null && !attachments.isEmpty()) {
                for (Attachment attachment : attachments) {
                    String encodeTo = attachment.getTransferEncoding();
                    Encoding encoding = EncodingFactory.getEncoding(encodeTo);
                    if (encoding == null)
                        throw new EncodingException(_t("No Encoding found for {0}", encodeTo));
                    // ref: https://blog.nodemailer.com/2017/01/27/the-mess-that-is-attachment-filenames/
                    // ref: RFC 2231
                    // split Content-Disposition into 3 lines to maximize room
                    // TODO filename*0* for long names...
                    String name = attachment.getFileName();
                    String name2 = FilenameUtil.sanitizeFilename(name);
                    String name3 = FilenameUtil.encodeFilenameRFC5987(name);
                    out.write("\r\n--" + boundary + "\r\nContent-type: " + attachment.getContentType() + "\r\nContent-Disposition: attachment;\r\n\tfilename=\"" + name2 + "\";\r\n\tfilename*=" + name3 + "\r\nContent-Transfer-Encoding: " + attachment.getTransferEncoding() + "\r\n\r\n");
                    InputStream in = null;
                    try {
                        in = attachment.getData();
                        encoding.encode(in, out);
                    } finally {
                        if (in != null)
                            try {
                                in.close();
                            } catch (IOException ioe) {
                            }
                    }
                }
                out.write("\r\n--" + boundary + "--\r\n");
            }
            out.write("\r\n.\r\n");
            out.flush();
            socket.setSoTimeout(0);
            int result = sendCmd(null);
            if (result == 250)
                mailSent = true;
            else
                error += _t("Error sending mail") + " (" + result + ")\n";
        }
    } catch (IOException e) {
        error += _t("Error sending mail") + ": " + e.getMessage() + '\n';
    }
    if (!mailSent && lastResponse.length() > 0) {
        String[] lines = DataHelper.split(lastResponse, "\r");
        for (int i = 0; i < lines.length; i++) error += lines[i] + '\n';
    }
    sendCmd("QUIT", false);
    if (socket != null) {
        try {
            socket.close();
        } catch (IOException e1) {
        }
        if (out != null)
            try {
                out.close();
            } catch (IOException ioe) {
            }
    }
    return mailSent;
}
Also used : EncodingException(i2p.susi.webmail.encoding.EncodingException) InputStream(java.io.InputStream) ArrayList(java.util.ArrayList) Attachment(i2p.susi.webmail.Attachment) Encoding(i2p.susi.webmail.encoding.Encoding) IOException(java.io.IOException) BufferedWriter(java.io.BufferedWriter) OutputStreamWriter(java.io.OutputStreamWriter) BufferedWriter(java.io.BufferedWriter) Writer(java.io.Writer) OutputStreamWriter(java.io.OutputStreamWriter)

Aggregations

Attachment (i2p.susi.webmail.Attachment)1 Encoding (i2p.susi.webmail.encoding.Encoding)1 EncodingException (i2p.susi.webmail.encoding.EncodingException)1 BufferedWriter (java.io.BufferedWriter)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 OutputStreamWriter (java.io.OutputStreamWriter)1 Writer (java.io.Writer)1 ArrayList (java.util.ArrayList)1