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;
}
Aggregations