in plugins/misc/mail/src/main/java/org/apache/hop/mail/workflow/actions/mail/ActionMail.java [209:763]
public Result execute(Result result, int nr) {
File masterZipfile = null;
session = null;
String protocol = "smtp";
if (!StringUtils.isEmpty(connectionName)) {
try {
connection =
getMetadataProvider().getSerializer(MailServerConnection.class).load(connectionName);
session = connection.getSession(getVariables() /*, getLogChannel()*/);
} catch (HopException e) {
throw new RuntimeException(
"Mail server connection '" + connectionName + "' could not be found", e);
}
} else {
// Send an e-mail...
// create some properties and get the default Session
Properties props = new Properties();
if (Utils.isEmpty(server)) {
logError(BaseMessages.getString(PKG, "ActionMail.Error.HostNotSpecified"));
result.setNrErrors(1L);
result.setResult(false);
return result;
}
if (usingSecureAuthentication) {
if (usexoauth2) {
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
}
if (secureConnectionType.equals("TLS")) {
// Allow TLS authentication
props.put("mail.smtp.starttls.enable", "true");
} else if (secureConnectionType.equals("TLS 1.2")) {
// Allow TLS 1.2 authentication
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.ssl.protocols", "TLSv1.2");
} else {
protocol = "smtps";
// required to get rid of a SSL exception :
// nested exception is:
// javax.net.ssl.SSLException: Unsupported record version Unknown
props.put("mail.smtps.quitwait", "false");
}
props.put("mail.smtp.ssl.checkServerIdentity", isCheckServerIdentity());
if (!Utils.isEmpty(trustedHosts)) {
props.put("mail.smtp.ssl.trust", resolve(trustedHosts));
}
}
props.put(CONST_MAIL + protocol + ".host", resolve(server));
if (!Utils.isEmpty(port)) {
props.put(CONST_MAIL + protocol + ".port", resolve(port));
}
if (isDebug()) {
props.put("mail.debug", "true");
}
if (usingAuthentication) {
props.put(CONST_MAIL + protocol + ".auth", "true");
}
session = Session.getInstance(props);
}
session.setDebug(isDebug());
try {
// create a message
Message msg = new MimeMessage(session);
// set message priority
if (usePriority) {
String priorityInt = "1";
if (priority.equals("low")) {
priorityInt = "3";
}
if (priority.equals("normal")) {
priorityInt = "2";
}
msg.setHeader("X-Priority", priorityInt); // (String)int between 1= high and 3 = low.
msg.setHeader("Importance", importance);
// seems to be needed for MS Outlook.
// where it returns a string of high /normal /low.
msg.setHeader("Sensitivity", sensitivity);
// Possible values are normal, personal, private, company-confidential
}
// Set Mail sender (From)
String senderAddress = resolve(replyAddress);
if (!Utils.isEmpty(senderAddress)) {
String senderName = resolve(replyName);
if (!Utils.isEmpty(senderName)) {
senderAddress = senderName + '<' + senderAddress + '>';
}
msg.setFrom(new InternetAddress(senderAddress));
} else {
throw new MessagingException(
BaseMessages.getString(PKG, "ActionMail.Error.ReplyEmailNotFilled"));
}
// set Reply to addresses
String replyToAddress = resolve(replyToAddresses);
if (!Utils.isEmpty(replyToAddress)) {
// Split the mail-address: variables separated
String[] replyAddressList = resolve(replyToAddress).split(" ");
InternetAddress[] address = new InternetAddress[replyAddressList.length];
for (int i = 0; i < replyAddressList.length; i++) {
address[i] = new InternetAddress(replyAddressList[i]);
}
msg.setReplyTo(address);
}
// Split the mail-address: variables separated
String[] destinations = resolve(destination).split(" ");
InternetAddress[] address = new InternetAddress[destinations.length];
for (int i = 0; i < destinations.length; i++) {
address[i] = new InternetAddress(destinations[i]);
}
msg.setRecipients(Message.RecipientType.TO, address);
String realCC = resolve(getDestinationCc());
if (!Utils.isEmpty(realCC)) {
// Split the mail-address Cc: variables separated
String[] destinationsCc = realCC.split(" ");
InternetAddress[] addressCc = new InternetAddress[destinationsCc.length];
for (int i = 0; i < destinationsCc.length; i++) {
addressCc[i] = new InternetAddress(destinationsCc[i]);
}
msg.setRecipients(Message.RecipientType.CC, addressCc);
}
String realBCc = resolve(getDestinationBCc());
if (!Utils.isEmpty(realBCc)) {
// Split the mail-address BCc: variables separated
String[] destinationsBCc = realBCc.split(" ");
InternetAddress[] addressBCc = new InternetAddress[destinationsBCc.length];
for (int i = 0; i < destinationsBCc.length; i++) {
addressBCc[i] = new InternetAddress(destinationsBCc[i]);
}
msg.setRecipients(Message.RecipientType.BCC, addressBCc);
}
String realSubject = resolve(subject);
if (!Utils.isEmpty(realSubject)) {
msg.setSubject(realSubject);
}
msg.setSentDate(new Date());
StringBuilder messageText = new StringBuilder();
String endRow = isUseHTML() ? "<br>" : Const.CR;
String realComment = resolve(comment);
if (!Utils.isEmpty(realComment)) {
messageText.append(realComment).append(Const.CR).append(Const.CR);
}
if (!onlySendComment) {
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.Workflow"))
.append(endRow);
messageText.append("-----").append(endRow);
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.JobName") + " : ")
.append(parentWorkflow.getWorkflowMeta().getName())
.append(endRow);
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.Action") + " : ")
.append(getName())
.append(endRow);
messageText.append(Const.CR);
}
if (includeDate) {
messageText
.append(endRow)
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.MsgDate") + ": ")
.append(XmlHandler.date2string(new Date()))
.append(endRow)
.append(endRow);
}
if (!onlySendComment && result != null) {
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.PreviousResult") + ":")
.append(endRow);
messageText.append("-----------------").append(endRow);
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.ActionNr") + " : ")
.append(result.getEntryNr())
.append(endRow);
messageText
.append(
BaseMessages.getString(PKG, "ActionMail.Log.Comment.Errors") + " : ")
.append(result.getNrErrors())
.append(endRow);
messageText
.append(
BaseMessages.getString(PKG, "ActionMail.Log.Comment.LinesRead") + " : ")
.append(result.getNrLinesRead())
.append(endRow);
messageText
.append(
BaseMessages.getString(PKG, "ActionMail.Log.Comment.LinesWritten") + " : ")
.append(result.getNrLinesWritten())
.append(endRow);
messageText
.append(
BaseMessages.getString(PKG, "ActionMail.Log.Comment.LinesInput") + " : ")
.append(result.getNrLinesInput())
.append(endRow);
messageText
.append(
BaseMessages.getString(PKG, "ActionMail.Log.Comment.LinesOutput") + " : ")
.append(result.getNrLinesOutput())
.append(endRow);
messageText
.append(
BaseMessages.getString(PKG, "ActionMail.Log.Comment.LinesUpdated") + " : ")
.append(result.getNrLinesUpdated())
.append(endRow);
messageText
.append(
BaseMessages.getString(PKG, "ActionMail.Log.Comment.LinesRejected") + " : ")
.append(result.getNrLinesRejected())
.append(endRow);
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.Status") + " : ")
.append(result.getExitStatus())
.append(endRow);
messageText
.append(
BaseMessages.getString(PKG, "ActionMail.Log.Comment.Result") + " : ")
.append(result.getResult())
.append(endRow);
messageText.append(endRow);
}
if (!onlySendComment
&& (!Utils.isEmpty(resolve(contactPerson)) || !Utils.isEmpty(resolve(contactPhone)))) {
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.ContactInfo") + " :")
.append(endRow);
messageText.append("---------------------").append(endRow);
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.PersonToContact") + " : ")
.append(resolve(contactPerson))
.append(endRow);
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.Tel") + " : ")
.append(resolve(contactPhone))
.append(endRow);
messageText.append(endRow);
}
// Include the path to this action...
if (!onlySendComment) {
WorkflowTracker workflowTracker = parentWorkflow.getWorkflowTracker();
if (workflowTracker != null) {
messageText
.append(BaseMessages.getString(PKG, "ActionMail.Log.Comment.PathToJobentry") + ":")
.append(endRow);
messageText.append("------------------------").append(endRow);
addBacktracking(workflowTracker, messageText);
if (isUseHTML()) {
messageText.replace(
0, messageText.length(), messageText.toString().replace(Const.CR, endRow));
}
}
}
MimeMultipart parts = new MimeMultipart();
MimeBodyPart part1 = new MimeBodyPart(); // put the text in the
// Attached files counter
int nrattachedFiles = 0;
// 1st part
if (useHTML) {
if (!Utils.isEmpty(getEncoding())) {
part1.setContent(messageText.toString(), "text/html; " + "charset=" + getEncoding());
} else {
part1.setContent(messageText.toString(), "text/html; " + "charset=ISO-8859-1");
}
} else {
part1.setText(messageText.toString());
}
parts.addBodyPart(part1);
if (includingFiles && result != null) {
List<ResultFile> resultFiles = result.getResultFilesList();
if (resultFiles != null && !resultFiles.isEmpty()) {
if (!zipFiles) {
// Add all files to the message...
//
for (ResultFile resultFile : resultFiles) {
FileObject file = resultFile.getFile();
if (file != null && file.exists()) {
boolean found = false;
for (ActionMailFileTypeField fileTypeField : fileTypes) {
if (fileTypeField.getFileType().equals(resultFile.getTypeDesc())) {
found = true;
}
}
if (found) {
// create a data source
MimeBodyPart files = new MimeBodyPart();
URLDataSource fds = new URLDataSource(file.getURL());
// get a data IHandler to manipulate this file type
files.setDataHandler(new DataHandler(fds));
// include the file in the data source
files.setFileName(file.getName().getBaseName());
// insist on base64 to preserve line endings
files.addHeader("Content-Transfer-Encoding", "base64");
// add the part with the file in the BodyPart()
parts.addBodyPart(files);
nrattachedFiles++;
logBasic("Added file '" + fds.getName() + "' to the mail message.");
}
}
}
} else {
// create a single ZIP archive of all files
masterZipfile =
new File(
System.getProperty("java.io.tmpdir")
+ Const.FILE_SEPARATOR
+ resolve(zipFilename));
ZipOutputStream zipOutputStream = null;
try {
zipOutputStream = new ZipOutputStream(new FileOutputStream(masterZipfile));
for (ResultFile resultFile : resultFiles) {
boolean found = false;
for (int i = 0; i < fileTypes.size(); i++) {
if (fileTypes.get(i).getFileType().equals(resultFile.getTypeDesc())) {
found = true;
}
}
if (found) {
FileObject file = resultFile.getFile();
ZipEntry zipEntry = new ZipEntry(file.getName().getBaseName());
zipOutputStream.putNextEntry(zipEntry);
// Now put the content of this file into this archive...
BufferedInputStream inputStream =
new BufferedInputStream(HopVfs.getInputStream(file));
try {
int c;
while ((c = inputStream.read()) >= 0) {
zipOutputStream.write(c);
}
} finally {
inputStream.close();
}
zipOutputStream.closeEntry();
nrattachedFiles++;
logBasic(
"Added file '"
+ file.getName().getURI()
+ "' to the mail message in a zip archive.");
}
}
} catch (Exception e) {
logError(
"Error zipping attachement files into file ["
+ masterZipfile.getPath()
+ "] : "
+ e.toString());
logError(Const.getStackTracker(e));
result.setNrErrors(1);
} finally {
if (zipOutputStream != null) {
try {
zipOutputStream.finish();
zipOutputStream.close();
} catch (IOException e) {
logError("Unable to close attachement zip file archive : " + e.toString());
logError(Const.getStackTracker(e));
result.setNrErrors(1);
}
}
}
// Now attach the master zip file to the message.
if (result.getNrErrors() == 0) {
// create a data source
MimeBodyPart files = new MimeBodyPart();
FileDataSource fds = new FileDataSource(masterZipfile);
// get a data IHandler to manipulate this file type
files.setDataHandler(new DataHandler(fds));
// include the file in the data source
files.setFileName(fds.getName());
// add the part with the file in the BodyPart()
parts.addBodyPart(files);
}
}
}
}
int nrEmbeddedImages = 0;
if (embeddedimages != null && embeddedimages.size() > 0) {
FileObject imageFile = null;
for (int i = 0; i < embeddedimages.size(); i++) {
String realImageFile = resolve(embeddedimages.get(i).getEmbeddedimage());
String realcontenID = resolve(embeddedimages.get(i).getContentId());
if (messageText.indexOf("cid:" + realcontenID) < 0) {
if (isDebug()) {
logDebug("Image [" + realImageFile + "] is not used in message body!");
}
} else {
try {
boolean found = false;
imageFile = HopVfs.getFileObject(realImageFile);
if (imageFile.exists() && imageFile.getType() == FileType.FILE) {
found = true;
} else {
logError("We can not find [" + realImageFile + "] or it is not a file");
}
if (found) {
// Create part for the image
MimeBodyPart messageBodyPart = new MimeBodyPart();
// Load the image
URLDataSource fds = new URLDataSource(imageFile.getURL());
messageBodyPart.setDataHandler(new DataHandler(fds));
// Setting the header
messageBodyPart.setHeader("Content-ID", "<" + realcontenID + ">");
// Add part to multi-part
parts.addBodyPart(messageBodyPart);
nrEmbeddedImages++;
logBasic("Image '" + fds.getName() + "' was embedded in message.");
}
} catch (Exception e) {
logError(
"Error embedding image [" + realImageFile + "] in message : " + e.toString());
logError(Const.getStackTracker(e));
result.setNrErrors(1);
} finally {
if (imageFile != null) {
try {
imageFile.close();
} catch (Exception e) {
/* Ignore */
}
}
}
}
}
}
if (nrEmbeddedImages > 0 && nrattachedFiles == 0) {
// If we need to embedd images...
// We need to create a "multipart/related" message.
// otherwise image will appear as attached file
parts.setSubType("related");
}
// put all parts together
msg.setContent(parts);
Transport transport = null;
try {
if (!StringUtils.isEmpty(connectionName)) {
transport = connection.getTransport();
} else {
transport = session.getTransport(protocol);
String authPass = getPassword(authenticationPassword);
if (usingAuthentication) {
if (!Utils.isEmpty(port)) {
transport.connect(
resolve(Const.NVL(server, "")),
Integer.parseInt(resolve(Const.NVL(port, ""))),
resolve(Const.NVL(authenticationUser, "")),
authPass);
} else {
transport.connect(
resolve(Const.NVL(server, "")),
resolve(Const.NVL(authenticationUser, "")),
authPass);
}
} else {
transport.connect();
}
}
transport.sendMessage(msg, msg.getAllRecipients());
} finally {
if (transport != null) {
transport.close();
}
}
} catch (IOException e) {
logError("Problem while sending message: " + e.toString());
result.setNrErrors(1);
} catch (MessagingException mex) {
logError("Problem while sending message: " + mex.toString());
result.setNrErrors(1);
Exception ex = mex;
do {
if (ex instanceof SendFailedException sfex) {
Address[] invalid = sfex.getInvalidAddresses();
if (invalid != null) {
logError(" ** Invalid Addresses");
for (int i = 0; i < invalid.length; i++) {
logError(CONST_SPACES_LONG + invalid[i]);
result.setNrErrors(1);
}
}
Address[] validUnsent = sfex.getValidUnsentAddresses();
if (validUnsent != null) {
logError(" ** ValidUnsent Addresses");
for (int i = 0; i < validUnsent.length; i++) {
logError(CONST_SPACES_LONG + validUnsent[i]);
result.setNrErrors(1);
}
}
Address[] validSent = sfex.getValidSentAddresses();
if (validSent != null) {
for (int i = 0; i < validSent.length; i++) {
logError(CONST_SPACES_LONG + validSent[i]);
result.setNrErrors(1);
}
}
}
if (ex instanceof MessagingException messagingException) {
ex = messagingException.getNextException();
} else {
ex = null;
}
} while (ex != null);
} finally {
if (masterZipfile != null && masterZipfile.exists()) {
masterZipfile.delete();
}
}
if (result.getNrErrors() > 0) {
result.setResult(false);
} else {
result.setResult(true);
}
return result;
}