io.vertx.ext.mail.impl.SMTPSendMail Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2015 The original author or authors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.ext.mail.impl;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.impl.NoStackTraceThrowable;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.mail.MailConfig;
import io.vertx.ext.mail.MailMessage;
import io.vertx.ext.mail.MailResult;
import io.vertx.ext.mail.mailencoder.EmailAddress;
import io.vertx.ext.mail.mailencoder.MailEncoder;
import java.util.ArrayList;
import java.util.List;
class SMTPSendMail {
private static final Logger log = LoggerFactory.getLogger(SMTPSendMail.class);
private final SMTPConnection connection;
private final MailMessage email;
private final MailConfig config;
private final Handler> resultHandler;
private final MailResult mailResult;
private final String hostname;
private String mailMessage;
SMTPSendMail(SMTPConnection connection, MailMessage email, MailConfig config, String hostname, Handler> resultHandler) {
this.connection = connection;
this.email = email;
this.config = config;
this.resultHandler = resultHandler;
mailResult = new MailResult();
this.hostname = hostname;
}
void start() {
try {
if (checkSize()) {
mailFromCmd();
}
} catch (Exception e) {
handleError(e);
}
}
/**
* Check if message size is allowed if size is supported.
*
* returns true if the message is allowed, have to make sure that when returning from the handleError method it
* doesn't continue with the mail from operation
*/
private boolean checkSize() {
final int size = connection.getCapa().getSize();
if (size > 0) {
createMailMessage();
if (mailMessage.length() > size) {
handleError("message exceeds allowed size limit");
return false;
} else {
return true;
}
} else {
return true;
}
}
private void mailFromCmd() {
try {
String fromAddr;
String bounceAddr = email.getBounceAddress();
if (bounceAddr != null && !bounceAddr.isEmpty()) {
fromAddr = bounceAddr;
} else {
fromAddr = email.getFrom();
}
EmailAddress from = new EmailAddress(fromAddr);
String sizeParameter;
if (connection.getCapa().getSize() > 0) {
sizeParameter = " SIZE=" + mailMessage.length();
} else {
sizeParameter = "";
}
connection.write("MAIL FROM:<" + from.getEmail() + ">" + sizeParameter, message -> {
log.debug("MAIL FROM result: " + message);
if (StatusCode.isStatusOk(message)) {
rcptToCmd();
} else {
log.warn("sender address not accepted: " + message);
handleError("sender address not accepted: " + message);
}
});
} catch (IllegalArgumentException e) {
log.error("address exception", e);
handleError(e);
}
}
private void rcptToCmd() {
List recipientAddrs = new ArrayList();
if (email.getTo() != null) {
recipientAddrs.addAll(email.getTo());
}
if (email.getCc() != null) {
recipientAddrs.addAll(email.getCc());
}
if (email.getBcc() != null) {
recipientAddrs.addAll(email.getBcc());
}
rcptToCmd(recipientAddrs, 0);
}
private void rcptToCmd(List recipientAddrs, int i) {
try {
EmailAddress toAddr = new EmailAddress(recipientAddrs.get(i));
connection.write("RCPT TO:<" + toAddr.getEmail() + ">", message -> {
if (StatusCode.isStatusOk(message)) {
log.debug("RCPT TO result: " + message);
mailResult.getRecipients().add(toAddr.getEmail());
nextRcpt(recipientAddrs, i);
} else {
if (config.isAllowRcptErrors()) {
log.warn("recipient address not accepted, continuing: " + message);
nextRcpt(recipientAddrs, i);
} else {
log.warn("recipient address not accepted: " + message);
handleError("recipient address not accepted: " + message);
}
}
});
} catch (IllegalArgumentException e) {
log.error("address exception", e);
handleError(e);
}
}
private void nextRcpt(List recipientAddrs, int i) {
if (i + 1 < recipientAddrs.size()) {
rcptToCmd(recipientAddrs, i + 1);
} else {
if (mailResult.getRecipients().size() > 0) {
dataCmd();
} else {
log.warn("no recipient addresses were accepted, not sending mail");
handleError("no recipient addresses were accepted, not sending mail");
}
}
}
private void handleError(Throwable throwable) {
resultHandler.handle(Future.failedFuture(throwable));
}
private void handleError(String message) {
handleError(new NoStackTraceThrowable(message));
}
private void dataCmd() {
connection.write("DATA", message -> {
log.debug("DATA result: " + message);
if (StatusCode.isStatusOk(message)) {
sendMaildata();
} else {
log.warn("DATA command not accepted: " + message);
handleError("DATA command not accepted: " + message);
}
});
}
private void sendMaildata() {
// create the message here if it hasn't been created
// for the size check above
createMailMessage();
sendLineByLine(0, mailMessage.length());
}
private void sendLineByLine(int index, int length) {
while (index < length) {
int nextIndex = mailMessage.indexOf('\n', index);
String line;
if (nextIndex == -1) {
line = mailMessage.substring(index);
nextIndex = length;
} else {
line = mailMessage.substring(index, nextIndex);
nextIndex++;
}
if (line.startsWith(".")) {
line = "." + line;
}
final int nextIndexFinal = nextIndex;
final boolean mayLog = nextIndex < 1000;
if (connection.writeQueueFull()) {
connection.writeLineWithDrainHandler(line, mayLog, v -> {
sendLineByLine(nextIndexFinal, length);
});
// call to our handler will finish the whole message, we just return here
return;
} else {
connection.writeLine(line, mayLog);
index = nextIndex;
}
}
connection.write(".", message -> {
log.debug("maildata result: " + message);
if (StatusCode.isStatusOk(message)) {
resultHandler.handle(Future.succeededFuture(mailResult));
} else {
log.warn("sending data failed: " + message);
handleError("sending data failed: " + message);
}
});
}
/**
* create message if it hasn't been already
*/
private void createMailMessage() {
if (mailMessage == null) {
MailEncoder encoder = new MailEncoder(email, hostname);
mailMessage = encoder.encode();
mailResult.setMessageID(encoder.getMessageID());
}
}
}