Search in sources :

Example 1 with EncodingException

use of i2p.susi.webmail.encoding.EncodingException 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)

Example 2 with EncodingException

use of i2p.susi.webmail.encoding.EncodingException in project i2p.i2p by i2p.

the class WebMail method sendMail.

/**
 * @param sessionObject
 * @param request
 * @return success
 */
private static boolean sendMail(SessionObject sessionObject, RequestWrapper request) {
    boolean ok = true;
    String from = request.getParameter(NEW_FROM);
    String to = request.getParameter(NEW_TO);
    String cc = request.getParameter(NEW_CC);
    String bcc = request.getParameter(NEW_BCC);
    String subject = request.getParameter(NEW_SUBJECT, _t("no subject"));
    String text = request.getParameter(NEW_TEXT, "");
    boolean fixed = Boolean.parseBoolean(Config.getProperty(CONFIG_SENDER_FIXED, "true"));
    if (fixed) {
        String domain = Config.getProperty(CONFIG_SENDER_DOMAIN, "mail.i2p");
        from = "<" + sessionObject.user + "@" + domain + ">";
    }
    ArrayList<String> toList = new ArrayList<String>();
    ArrayList<String> ccList = new ArrayList<String>();
    ArrayList<String> bccList = new ArrayList<String>();
    ArrayList<String> recipients = new ArrayList<String>();
    String sender = null;
    if (from == null || !Mail.validateAddress(from)) {
        ok = false;
        sessionObject.error += _t("Found no valid sender address.") + '\n';
    } else {
        sender = Mail.getAddress(from);
        if (sender == null || sender.length() == 0) {
            ok = false;
            sessionObject.error += _t("Found no valid address in \\''{0}\\''.", quoteHTML(from)) + '\n';
        }
    }
    ok = Mail.getRecipientsFromList(toList, to, ok);
    ok = Mail.getRecipientsFromList(ccList, cc, ok);
    ok = Mail.getRecipientsFromList(bccList, bcc, ok);
    recipients.addAll(toList);
    recipients.addAll(ccList);
    recipients.addAll(bccList);
    String bccToSelf = request.getParameter(NEW_BCC_TO_SELF);
    boolean toSelf = "1".equals(bccToSelf);
    // save preference in session
    sessionObject.bccToSelf = toSelf;
    if (toSelf)
        recipients.add(sender);
    if (toList.isEmpty()) {
        ok = false;
        sessionObject.error += _t("No recipients found.") + '\n';
    }
    Encoding qp = EncodingFactory.getEncoding("quoted-printable");
    Encoding hl = EncodingFactory.getEncoding("HEADERLINE");
    if (qp == null) {
        ok = false;
        // can't happen, don't translate
        sessionObject.error += "Internal error: Quoted printable encoder not available.";
    }
    if (hl == null) {
        ok = false;
        // can't happen, don't translate
        sessionObject.error += "Internal error: Header line encoder not available.";
    }
    long total = text.length();
    boolean multipart = sessionObject.attachments != null && !sessionObject.attachments.isEmpty();
    if (multipart) {
        for (Attachment a : sessionObject.attachments) {
            total += a.getSize();
        }
    }
    if (total > SMTPClient.BINARY_MAX_SIZE) {
        ok = false;
        sessionObject.error += _t("Email is too large, max is {0}", DataHelper.formatSize2(SMTPClient.BINARY_MAX_SIZE, false) + 'B') + '\n';
    }
    if (ok) {
        StringBuilder body = new StringBuilder(1024);
        I2PAppContext ctx = I2PAppContext.getGlobalContext();
        body.append("Date: " + RFC822Date.to822Date(ctx.clock().now()) + "\r\n");
        // todo include real names, and headerline encode them
        body.append("From: " + from + "\r\n");
        Mail.appendRecipients(body, toList, "To: ");
        Mail.appendRecipients(body, ccList, "Cc: ");
        try {
            body.append(hl.encode("Subject: " + subject.trim()));
        } catch (EncodingException e) {
            ok = false;
            sessionObject.error += e.getMessage();
        }
        String boundary = "_=" + ctx.random().nextLong();
        if (multipart) {
            body.append("MIME-Version: 1.0\r\nContent-type: multipart/mixed; boundary=\"" + boundary + "\"\r\n\r\n");
        } else {
            body.append("MIME-Version: 1.0\r\nContent-type: text/plain; charset=\"utf-8\"\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n");
        }
        try {
            // TODO pass the text separately to SMTP and let it pick the encoding
            if (multipart)
                body.append("--" + boundary + "\r\nContent-type: text/plain; charset=\"utf-8\"\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n");
            body.append(qp.encode(text));
        } catch (EncodingException e) {
            ok = false;
            sessionObject.error += e.getMessage();
        }
        // set to the StringBuilder so SMTP can replace() in place
        sessionObject.sentMail = body;
        if (ok) {
            SMTPClient relay = new SMTPClient();
            if (relay.sendMail(sessionObject.host, sessionObject.smtpPort, sessionObject.user, sessionObject.pass, sender, recipients.toArray(new String[recipients.size()]), sessionObject.sentMail, sessionObject.attachments, boundary)) {
                sessionObject.info += _t("Mail sent.");
                sessionObject.sentMail = null;
                sessionObject.clearAttachments();
            } else {
                ok = false;
                sessionObject.error += relay.error;
            }
        }
    }
    return ok;
}
Also used : SMTPClient(i2p.susi.webmail.smtp.SMTPClient) I2PAppContext(net.i2p.I2PAppContext) UnsupportedEncodingException(java.io.UnsupportedEncodingException) EncodingException(i2p.susi.webmail.encoding.EncodingException) ArrayList(java.util.ArrayList) Encoding(i2p.susi.webmail.encoding.Encoding)

Aggregations

Encoding (i2p.susi.webmail.encoding.Encoding)2 EncodingException (i2p.susi.webmail.encoding.EncodingException)2 ArrayList (java.util.ArrayList)2 Attachment (i2p.susi.webmail.Attachment)1 SMTPClient (i2p.susi.webmail.smtp.SMTPClient)1 BufferedWriter (java.io.BufferedWriter)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 OutputStreamWriter (java.io.OutputStreamWriter)1 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 Writer (java.io.Writer)1 I2PAppContext (net.i2p.I2PAppContext)1