/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.di.job.entries.mail;
import org.pentaho.di.job.entry.validator.AndValidator;
import org.pentaho.di.job.entry.validator.JobEntryValidatorUtils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.activation.URLDataSource;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.SendFailedException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileType;
import org.pentaho.di.cluster.SlaveServer;
import org.pentaho.di.core.CheckResultInterface;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.Result;
import org.pentaho.di.core.ResultFile;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.encryption.Encr;
import org.pentaho.di.core.exception.KettleDatabaseException;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleXMLException;
import org.pentaho.di.core.gui.JobTracker;
import org.pentaho.di.core.variables.VariableSpace;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.core.xml.XMLHandler;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.job.JobEntryResult;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.job.entry.JobEntryBase;
import org.pentaho.di.job.entry.JobEntryInterface;
import org.pentaho.di.repository.ObjectId;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.resource.ResourceEntry;
import org.pentaho.di.resource.ResourceEntry.ResourceType;
import org.pentaho.di.resource.ResourceReference;
import org.pentaho.metastore.api.IMetaStore;
import org.w3c.dom.Node;
/**
* Describes a Mail Job Entry.
*
* @author Matt Created on 17-06-2003
*
*/
public class JobEntryMail extends JobEntryBase implements Cloneable, JobEntryInterface {
private static Class<?> PKG = JobEntryMail.class; // for i18n purposes, needed by Translator2!!
private String server;
private String destination;
private String destinationCc;
private String destinationBCc;
/** Caution : It's sender address and NOT reply address **/
private String replyAddress;
/** Caution : It's sender name name and NOT reply name **/
private String replyName;
private String subject;
private boolean includeDate;
private String contactPerson;
private String contactPhone;
private String comment;
private boolean includingFiles;
private int[] fileType;
private boolean zipFiles;
private String zipFilename;
private boolean usingAuthentication;
private String authenticationUser;
private String authenticationPassword;
private boolean onlySendComment;
private boolean useHTML;
private boolean usingSecureAuthentication;
private boolean usePriority;
private String port;
private String priority;
private String importance;
private String sensitivity;
private String secureConnectionType;
/** The encoding to use for reading: null or empty string means system default encoding */
private String encoding;
/** The reply to addresses */
private String replyToAddresses;
public String[] embeddedimages;
public String[] contentids;
public JobEntryMail( String n ) {
super( n, "" );
allocate( 0 );
}
public JobEntryMail() {
this( "" );
allocate( 0 );
}
public void allocate( int nrFileTypes ) {
fileType = new int[nrFileTypes];
}
public void allocateImages( int nrImages ) {
embeddedimages = new String[nrImages];
contentids = new String[nrImages];
}
public Object clone() {
JobEntryMail je = (JobEntryMail) super.clone();
if ( fileType != null ) {
int nrFileTypes = fileType.length;
je.allocate( nrFileTypes );
System.arraycopy( fileType, 0, je.fileType, 0, nrFileTypes );
}
if ( embeddedimages != null ) {
int nrImages = embeddedimages.length;
je.allocateImages( nrImages );
System.arraycopy( embeddedimages, 0, je.embeddedimages, 0, nrImages );
System.arraycopy( contentids, 0, je.contentids, 0, nrImages );
}
return je;
}
public String getXML() {
StringBuilder retval = new StringBuilder( 600 );
retval.append( super.getXML() );
retval.append( " " ).append( XMLHandler.addTagValue( "server", server ) );
retval.append( " " ).append( XMLHandler.addTagValue( "port", port ) );
retval.append( " " ).append( XMLHandler.addTagValue( "destination", destination ) );
retval.append( " " ).append( XMLHandler.addTagValue( "destinationCc", destinationCc ) );
retval.append( " " ).append( XMLHandler.addTagValue( "destinationBCc", destinationBCc ) );
retval.append( " " ).append( XMLHandler.addTagValue( "replyto", replyAddress ) );
retval.append( " " ).append( XMLHandler.addTagValue( "replytoname", replyName ) );
retval.append( " " ).append( XMLHandler.addTagValue( "subject", subject ) );
retval.append( " " ).append( XMLHandler.addTagValue( "include_date", includeDate ) );
retval.append( " " ).append( XMLHandler.addTagValue( "contact_person", contactPerson ) );
retval.append( " " ).append( XMLHandler.addTagValue( "contact_phone", contactPhone ) );
retval.append( " " ).append( XMLHandler.addTagValue( "comment", comment ) );
retval.append( " " ).append( XMLHandler.addTagValue( "include_files", includingFiles ) );
retval.append( " " ).append( XMLHandler.addTagValue( "zip_files", zipFiles ) );
retval.append( " " ).append( XMLHandler.addTagValue( "zip_name", zipFilename ) );
retval.append( " " ).append( XMLHandler.addTagValue( "use_auth", usingAuthentication ) );
retval.append( " " ).append( XMLHandler.addTagValue( "use_secure_auth", usingSecureAuthentication ) );
retval.append( " " ).append( XMLHandler.addTagValue( "auth_user", authenticationUser ) );
retval.append( " " ).append(
XMLHandler
.addTagValue( "auth_password", Encr.encryptPasswordIfNotUsingVariables( authenticationPassword ) ) );
retval.append( " " ).append( XMLHandler.addTagValue( "only_comment", onlySendComment ) );
retval.append( " " ).append( XMLHandler.addTagValue( "use_HTML", useHTML ) );
retval.append( " " ).append( XMLHandler.addTagValue( "use_Priority", usePriority ) );
retval.append( " " ).append( XMLHandler.addTagValue( "encoding", encoding ) );
retval.append( " " ).append( XMLHandler.addTagValue( "priority", priority ) );
retval.append( " " ).append( XMLHandler.addTagValue( "importance", importance ) );
retval.append( " " ).append( XMLHandler.addTagValue( "sensitivity", sensitivity ) );
retval.append( " " ).append( XMLHandler.addTagValue( "secureconnectiontype", secureConnectionType ) );
retval.append( " " ).append( XMLHandler.addTagValue( "replyToAddresses", replyToAddresses ) );
retval.append( " <filetypes>" );
if ( fileType != null ) {
for ( int i = 0; i < fileType.length; i++ ) {
retval.append( " " ).append(
XMLHandler.addTagValue( "filetype", ResultFile.getTypeCode( fileType[i] ) ) );
}
}
retval.append( " </filetypes>" );
retval.append( " <embeddedimages>" ).append( Const.CR );
if ( embeddedimages != null ) {
for ( int i = 0; i < embeddedimages.length; i++ ) {
retval.append( " <embeddedimage>" ).append( Const.CR );
retval.append( " " ).append( XMLHandler.addTagValue( "image_name", embeddedimages[i] ) );
retval.append( " " ).append( XMLHandler.addTagValue( "content_id", contentids[i] ) );
retval.append( " </embeddedimage>" ).append( Const.CR );
}
}
retval.append( " </embeddedimages>" ).append( Const.CR );
return retval.toString();
}
public void loadXML( Node entrynode, List<DatabaseMeta> databases, List<SlaveServer> slaveServers,
Repository rep, IMetaStore metaStore ) throws KettleXMLException {
try {
super.loadXML( entrynode, databases, slaveServers );
setServer( XMLHandler.getTagValue( entrynode, "server" ) );
setPort( XMLHandler.getTagValue( entrynode, "port" ) );
setDestination( XMLHandler.getTagValue( entrynode, "destination" ) );
setDestinationCc( XMLHandler.getTagValue( entrynode, "destinationCc" ) );
setDestinationBCc( XMLHandler.getTagValue( entrynode, "destinationBCc" ) );
setReplyAddress( XMLHandler.getTagValue( entrynode, "replyto" ) );
setReplyName( XMLHandler.getTagValue( entrynode, "replytoname" ) );
setSubject( XMLHandler.getTagValue( entrynode, "subject" ) );
setIncludeDate( "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "include_date" ) ) );
setContactPerson( XMLHandler.getTagValue( entrynode, "contact_person" ) );
setContactPhone( XMLHandler.getTagValue( entrynode, "contact_phone" ) );
setComment( XMLHandler.getTagValue( entrynode, "comment" ) );
setIncludingFiles( "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "include_files" ) ) );
setUsingAuthentication( "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "use_auth" ) ) );
setUsingSecureAuthentication( "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "use_secure_auth" ) ) );
setAuthenticationUser( XMLHandler.getTagValue( entrynode, "auth_user" ) );
setAuthenticationPassword( Encr.decryptPasswordOptionallyEncrypted( XMLHandler.getTagValue(
entrynode, "auth_password" ) ) );
setOnlySendComment( "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "only_comment" ) ) );
setUseHTML( "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "use_HTML" ) ) );
setUsePriority( "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "use_Priority" ) ) );
setEncoding( XMLHandler.getTagValue( entrynode, "encoding" ) );
setPriority( XMLHandler.getTagValue( entrynode, "priority" ) );
setImportance( XMLHandler.getTagValue( entrynode, "importance" ) );
setSensitivity( XMLHandler.getTagValue( entrynode, "sensitivity" ) );
setSecureConnectionType( XMLHandler.getTagValue( entrynode, "secureconnectiontype" ) );
Node ftsnode = XMLHandler.getSubNode( entrynode, "filetypes" );
int nrTypes = XMLHandler.countNodes( ftsnode, "filetype" );
allocate( nrTypes );
for ( int i = 0; i < nrTypes; i++ ) {
Node ftnode = XMLHandler.getSubNodeByNr( ftsnode, "filetype", i );
fileType[i] = ResultFile.getType( XMLHandler.getNodeValue( ftnode ) );
}
setZipFiles( "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "zip_files" ) ) );
setZipFilename( XMLHandler.getTagValue( entrynode, "zip_name" ) );
setReplyToAddresses( XMLHandler.getTagValue( entrynode, "replyToAddresses" ) );
Node images = XMLHandler.getSubNode( entrynode, "embeddedimages" );
// How many field embedded images ?
int nrImages = XMLHandler.countNodes( images, "embeddedimage" );
allocateImages( nrImages );
// Read them all...
for ( int i = 0; i < nrImages; i++ ) {
Node fnode = XMLHandler.getSubNodeByNr( images, "embeddedimage", i );
embeddedimages[i] = XMLHandler.getTagValue( fnode, "image_name" );
contentids[i] = XMLHandler.getTagValue( fnode, "content_id" );
}
} catch ( KettleException xe ) {
throw new KettleXMLException( "Unable to load job entry of type 'mail' from XML node", xe );
}
}
public void loadRep( Repository rep, IMetaStore metaStore, ObjectId id_jobentry, List<DatabaseMeta> databases,
List<SlaveServer> slaveServers ) throws KettleException {
try {
// First load the common parts like name & description, then the attributes...
//
server = rep.getJobEntryAttributeString( id_jobentry, "server" );
port = rep.getJobEntryAttributeString( id_jobentry, "port" );
destination = rep.getJobEntryAttributeString( id_jobentry, "destination" );
destinationCc = rep.getJobEntryAttributeString( id_jobentry, "destinationCc" );
destinationBCc = rep.getJobEntryAttributeString( id_jobentry, "destinationBCc" );
replyAddress = rep.getJobEntryAttributeString( id_jobentry, "replyto" );
replyName = rep.getJobEntryAttributeString( id_jobentry, "replytoname" );
subject = rep.getJobEntryAttributeString( id_jobentry, "subject" );
includeDate = rep.getJobEntryAttributeBoolean( id_jobentry, "include_date" );
contactPerson = rep.getJobEntryAttributeString( id_jobentry, "contact_person" );
contactPhone = rep.getJobEntryAttributeString( id_jobentry, "contact_phone" );
comment = rep.getJobEntryAttributeString( id_jobentry, "comment" );
encoding = rep.getJobEntryAttributeString( id_jobentry, "encoding" );
priority = rep.getJobEntryAttributeString( id_jobentry, "priority" );
importance = rep.getJobEntryAttributeString( id_jobentry, "importance" );
sensitivity = rep.getJobEntryAttributeString( id_jobentry, "sensitivity" );
includingFiles = rep.getJobEntryAttributeBoolean( id_jobentry, "include_files" );
usingAuthentication = rep.getJobEntryAttributeBoolean( id_jobentry, "use_auth" );
usingSecureAuthentication = rep.getJobEntryAttributeBoolean( id_jobentry, "use_secure_auth" );
authenticationUser = rep.getJobEntryAttributeString( id_jobentry, "auth_user" );
authenticationPassword =
Encr.decryptPasswordOptionallyEncrypted( rep.getJobEntryAttributeString( id_jobentry, "auth_password" ) );
onlySendComment = rep.getJobEntryAttributeBoolean( id_jobentry, "only_comment" );
useHTML = rep.getJobEntryAttributeBoolean( id_jobentry, "use_HTML" );
usePriority = rep.getJobEntryAttributeBoolean( id_jobentry, "use_Priority" );
secureConnectionType = rep.getJobEntryAttributeString( id_jobentry, "secureconnectiontype" );
int nrTypes = rep.countNrJobEntryAttributes( id_jobentry, "file_type" );
allocate( nrTypes );
for ( int i = 0; i < nrTypes; i++ ) {
String typeCode = rep.getJobEntryAttributeString( id_jobentry, i, "file_type" );
fileType[i] = ResultFile.getType( typeCode );
}
zipFiles = rep.getJobEntryAttributeBoolean( id_jobentry, "zip_files" );
zipFilename = rep.getJobEntryAttributeString( id_jobentry, "zip_name" );
replyToAddresses = rep.getJobEntryAttributeString( id_jobentry, "replyToAddresses" );
// How many arguments?
int imagesnr = rep.countNrJobEntryAttributes( id_jobentry, "embeddedimage" );
allocateImages( imagesnr );
// Read them all...
for ( int a = 0; a < imagesnr; a++ ) {
embeddedimages[a] = rep.getJobEntryAttributeString( id_jobentry, a, "embeddedimage" );
contentids[a] = rep.getJobEntryAttributeString( id_jobentry, a, "contentid" );
}
} catch ( KettleDatabaseException dbe ) {
throw new KettleException( "Unable to load job entry of type 'mail' from the repository with id_jobentry="
+ id_jobentry, dbe );
}
}
public void saveRep( Repository rep, IMetaStore metaStore, ObjectId id_job ) throws KettleException {
try {
rep.saveJobEntryAttribute( id_job, getObjectId(), "server", server );
rep.saveJobEntryAttribute( id_job, getObjectId(), "port", port );
rep.saveJobEntryAttribute( id_job, getObjectId(), "destination", destination );
rep.saveJobEntryAttribute( id_job, getObjectId(), "destinationCc", destinationCc );
rep.saveJobEntryAttribute( id_job, getObjectId(), "destinationBCc", destinationBCc );
rep.saveJobEntryAttribute( id_job, getObjectId(), "replyto", replyAddress );
rep.saveJobEntryAttribute( id_job, getObjectId(), "replytoname", replyName );
rep.saveJobEntryAttribute( id_job, getObjectId(), "subject", subject );
rep.saveJobEntryAttribute( id_job, getObjectId(), "include_date", includeDate );
rep.saveJobEntryAttribute( id_job, getObjectId(), "contact_person", contactPerson );
rep.saveJobEntryAttribute( id_job, getObjectId(), "contact_phone", contactPhone );
rep.saveJobEntryAttribute( id_job, getObjectId(), "comment", comment );
rep.saveJobEntryAttribute( id_job, getObjectId(), "encoding", encoding );
rep.saveJobEntryAttribute( id_job, getObjectId(), "priority", priority );
rep.saveJobEntryAttribute( id_job, getObjectId(), "importance", importance );
rep.saveJobEntryAttribute( id_job, getObjectId(), "sensitivity", sensitivity );
rep.saveJobEntryAttribute( id_job, getObjectId(), "include_files", includingFiles );
rep.saveJobEntryAttribute( id_job, getObjectId(), "use_auth", usingAuthentication );
rep.saveJobEntryAttribute( id_job, getObjectId(), "use_secure_auth", usingSecureAuthentication );
rep.saveJobEntryAttribute( id_job, getObjectId(), "auth_user", authenticationUser );
rep.saveJobEntryAttribute( id_job, getObjectId(), "auth_password", Encr
.encryptPasswordIfNotUsingVariables( authenticationPassword ) );
rep.saveJobEntryAttribute( id_job, getObjectId(), "only_comment", onlySendComment );
rep.saveJobEntryAttribute( id_job, getObjectId(), "use_HTML", useHTML );
rep.saveJobEntryAttribute( id_job, getObjectId(), "use_Priority", usePriority );
rep.saveJobEntryAttribute( id_job, getObjectId(), "secureconnectiontype", secureConnectionType );
if ( fileType != null ) {
for ( int i = 0; i < fileType.length; i++ ) {
rep.saveJobEntryAttribute( id_job, getObjectId(), i, "file_type", ResultFile.getTypeCode( fileType[i] ) );
}
}
rep.saveJobEntryAttribute( id_job, getObjectId(), "zip_files", zipFiles );
rep.saveJobEntryAttribute( id_job, getObjectId(), "zip_name", zipFilename );
rep.saveJobEntryAttribute( id_job, getObjectId(), "replyToAddresses", replyToAddresses );
// save the arguments...
if ( embeddedimages != null ) {
for ( int i = 0; i < embeddedimages.length; i++ ) {
rep.saveJobEntryAttribute( id_job, getObjectId(), i, "embeddedimage", embeddedimages[i] );
rep.saveJobEntryAttribute( id_job, getObjectId(), i, "contentid", contentids[i] );
}
}
} catch ( KettleDatabaseException dbe ) {
throw new KettleException(
"Unable to save job entry of type 'mail' to the repository for id_job=" + id_job, dbe );
}
}
public void setServer( String s ) {
server = s;
}
public String getServer() {
return server;
}
public void setDestination( String dest ) {
destination = dest;
}
public void setDestinationCc( String destCc ) {
destinationCc = destCc;
}
public void setDestinationBCc( String destBCc ) {
destinationBCc = destBCc;
}
public String getDestination() {
return destination;
}
public String getDestinationCc() {
return destinationCc;
}
public String getDestinationBCc() {
return destinationBCc;
}
public void setReplyAddress( String reply ) {
replyAddress = reply;
}
public String getReplyAddress() {
return replyAddress;
}
public void setReplyName( String replyname ) {
this.replyName = replyname;
}
public String getReplyName() {
return replyName;
}
public void setSubject( String subj ) {
subject = subj;
}
public String getSubject() {
return subject;
}
public void setIncludeDate( boolean incl ) {
includeDate = incl;
}
public boolean getIncludeDate() {
return includeDate;
}
public void setContactPerson( String person ) {
contactPerson = person;
}
public String getContactPerson() {
return contactPerson;
}
public void setContactPhone( String phone ) {
contactPhone = phone;
}
public String getContactPhone() {
return contactPhone;
}
public void setComment( String comm ) {
comment = comm;
}
public String getComment() {
return comment;
}
/**
* @return the result file types to select for attachment </b>
* @see ResultFile
*/
public int[] getFileType() {
return fileType;
}
/**
* @param fileType
* the result file types to select for attachment
* @see ResultFile
*/
public void setFileType( int[] fileType ) {
this.fileType = fileType;
}
public boolean isIncludingFiles() {
return includingFiles;
}
public void setIncludingFiles( boolean includeFiles ) {
this.includingFiles = includeFiles;
}
/**
* @return Returns the zipFilename.
*/
public String getZipFilename() {
return zipFilename;
}
/**
* @param zipFilename
* The zipFilename to set.
*/
public void setZipFilename( String zipFilename ) {
this.zipFilename = zipFilename;
}
/**
* @return Returns the zipFiles.
*/
public boolean isZipFiles() {
return zipFiles;
}
/**
* @param zipFiles
* The zipFiles to set.
*/
public void setZipFiles( boolean zipFiles ) {
this.zipFiles = zipFiles;
}
/**
* @return Returns the authenticationPassword.
*/
public String getAuthenticationPassword() {
return authenticationPassword;
}
/**
* @param authenticationPassword
* The authenticationPassword to set.
*/
public void setAuthenticationPassword( String authenticationPassword ) {
this.authenticationPassword = authenticationPassword;
}
/**
* @return Returns the authenticationUser.
*/
public String getAuthenticationUser() {
return authenticationUser;
}
/**
* @param authenticationUser
* The authenticationUser to set.
*/
public void setAuthenticationUser( String authenticationUser ) {
this.authenticationUser = authenticationUser;
}
/**
* @return Returns the usingAuthentication.
*/
public boolean isUsingAuthentication() {
return usingAuthentication;
}
/**
* @param usingAuthentication
* The usingAuthentication to set.
*/
public void setUsingAuthentication( boolean usingAuthentication ) {
this.usingAuthentication = usingAuthentication;
}
/**
* @return the onlySendComment flag
*/
public boolean isOnlySendComment() {
return onlySendComment;
}
/**
* @param onlySendComment
* the onlySendComment flag to set
*/
public void setOnlySendComment( boolean onlySendComment ) {
this.onlySendComment = onlySendComment;
}
/**
* @return the useHTML flag
*/
public boolean isUseHTML() {
return useHTML;
}
/**
* @param useHTML
* the useHTML to set
*/
public void setUseHTML( boolean useHTML ) {
this.useHTML = useHTML;
}
/**
* @return the encoding
*/
public String getEncoding() {
return encoding;
}
/**
* @return the secure connection type
*/
public String getSecureConnectionType() {
return secureConnectionType;
}
/**
* @param secureConnectionType
* the secure connection type to set
*/
public void setSecureConnectionType( String secureConnectionType ) {
this.secureConnectionType = secureConnectionType;
}
/**
* @param encoding
* the encoding to set
*/
public void setEncoding( String encoding ) {
this.encoding = encoding;
}
/**
* @param secureconnectiontype
* the replayToAddresses to set
*/
public void setReplyToAddresses( String replyToAddresses ) {
this.replyToAddresses = replyToAddresses;
}
/**
* @return replayToAddresses
*/
public String getReplyToAddresses() {
return this.replyToAddresses;
}
/**
* @param usePriority
* the usePriority to set
*/
public void setUsePriority( boolean usePriority ) {
this.usePriority = usePriority;
}
/**
* @return the usePriority flag
*/
public boolean isUsePriority() {
return usePriority;
}
/**
* @return the priority
*/
public String getPriority() {
return priority;
}
/**
* @param importance
* the importance to set
*/
public void setImportance( String importance ) {
this.importance = importance;
}
/**
* @return the importance
*/
public String getImportance() {
return importance;
}
public String getSensitivity() {
return sensitivity;
}
public void setSensitivity( String sensitivity ) {
this.sensitivity = sensitivity;
}
/**
* @param priority
* the priority to set
*/
public void setPriority( String priority ) {
this.priority = priority;
}
public Result execute( Result result, int nr ) {
File masterZipfile = null;
// Send an e-mail...
// create some properties and get the default Session
Properties props = new Properties();
if ( Utils.isEmpty( server ) ) {
logError( BaseMessages.getString( PKG, "JobMail.Error.HostNotSpecified" ) );
result.setNrErrors( 1L );
result.setResult( false );
return result;
}
String protocol = "smtp";
if ( usingSecureAuthentication ) {
if ( secureConnectionType.equals( "TLS" ) ) {
// Allow TLS authentication
props.put( "mail.smtp.starttls.enable", "true" );
} 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." + protocol + ".host", environmentSubstitute( server ) );
if ( !Utils.isEmpty( port ) ) {
props.put( "mail." + protocol + ".port", environmentSubstitute( port ) );
}
if ( log.isDebug() ) {
props.put( "mail.debug", "true" );
}
if ( usingAuthentication ) {
props.put( "mail." + protocol + ".auth", "true" );
/*
* authenticator = new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new
* PasswordAuthentication( StringUtil.environmentSubstitute(Const.NVL(authenticationUser, "")),
* StringUtil.environmentSubstitute(Const.NVL(authenticationPassword, "")) ); } };
*/
}
Session session = Session.getInstance( props );
session.setDebug( log.isDebug() );
try {
// create a message
Message msg = new MimeMessage( session );
// set message priority
if ( usePriority ) {
String priority_int = "1";
if ( priority.equals( "low" ) ) {
priority_int = "3";
}
if ( priority.equals( "normal" ) ) {
priority_int = "2";
}
msg.setHeader( "X-Priority", priority_int ); // (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 sender_address = environmentSubstitute( replyAddress );
if ( !Utils.isEmpty( sender_address ) ) {
String sender_name = environmentSubstitute( replyName );
if ( !Utils.isEmpty( sender_name ) ) {
sender_address = sender_name + '<' + sender_address + '>';
}
msg.setFrom( new InternetAddress( sender_address ) );
} else {
throw new MessagingException( BaseMessages.getString( PKG, "JobMail.Error.ReplyEmailNotFilled" ) );
}
// set Reply to addresses
String reply_to_address = environmentSubstitute( replyToAddresses );
if ( !Utils.isEmpty( reply_to_address ) ) {
// Split the mail-address: space separated
String[] reply_Address_List = environmentSubstitute( reply_to_address ).split( " " );
InternetAddress[] address = new InternetAddress[reply_Address_List.length];
for ( int i = 0; i < reply_Address_List.length; i++ ) {
address[i] = new InternetAddress( reply_Address_List[i] );
}
msg.setReplyTo( address );
}
// Split the mail-address: space separated
String[] destinations = environmentSubstitute( 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 = environmentSubstitute( getDestinationCc() );
if ( !Utils.isEmpty( realCC ) ) {
// Split the mail-address Cc: space 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 = environmentSubstitute( getDestinationBCc() );
if ( !Utils.isEmpty( realBCc ) ) {
// Split the mail-address BCc: space 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 = environmentSubstitute( subject );
if ( !Utils.isEmpty( realSubject ) ) {
msg.setSubject( realSubject );
}
msg.setSentDate( new Date() );
StringBuilder messageText = new StringBuilder();
String endRow = isUseHTML() ? "<br>" : Const.CR;
String realComment = environmentSubstitute( comment );
if ( !Utils.isEmpty( realComment ) ) {
messageText.append( realComment ).append( Const.CR ).append( Const.CR );
}
if ( !onlySendComment ) {
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.Job" ) ).append( endRow );
messageText.append( "-----" ).append( endRow );
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.JobName" ) + " : " ).append(
parentJob.getJobMeta().getName() ).append( endRow );
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.JobDirectory" ) + " : " ).append(
parentJob.getJobMeta().getRepositoryDirectory() ).append( endRow );
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.JobEntry" ) + " : " ).append(
getName() ).append( endRow );
messageText.append( Const.CR );
}
if ( includeDate ) {
messageText
.append( endRow ).append( BaseMessages.getString( PKG, "JobMail.Log.Comment.MsgDate" ) + ": " )
.append( XMLHandler.date2string( new Date() ) ).append( endRow ).append( endRow );
}
if ( !onlySendComment && result != null ) {
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.PreviousResult" ) + ":" ).append(
endRow );
messageText.append( "-----------------" ).append( endRow );
messageText
.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.JobEntryNr" ) + " : " ).append(
result.getEntryNr() ).append( endRow );
messageText
.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.Errors" ) + " : " ).append(
result.getNrErrors() ).append( endRow );
messageText
.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.LinesRead" ) + " : " ).append(
result.getNrLinesRead() ).append( endRow );
messageText
.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.LinesWritten" ) + " : " ).append(
result.getNrLinesWritten() ).append( endRow );
messageText
.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.LinesInput" ) + " : " ).append(
result.getNrLinesInput() ).append( endRow );
messageText
.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.LinesOutput" ) + " : " ).append(
result.getNrLinesOutput() ).append( endRow );
messageText
.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.LinesUpdated" ) + " : " ).append(
result.getNrLinesUpdated() ).append( endRow );
messageText
.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.LinesRejected" ) + " : " ).append(
result.getNrLinesRejected() ).append( endRow );
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.Status" ) + " : " ).append(
result.getExitStatus() ).append( endRow );
messageText
.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.Result" ) + " : " ).append(
result.getResult() ).append( endRow );
messageText.append( endRow );
}
if ( !onlySendComment
&& ( !Utils.isEmpty( environmentSubstitute( contactPerson ) ) || !Utils
.isEmpty( environmentSubstitute( contactPhone ) ) ) ) {
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.ContactInfo" ) + " :" ).append(
endRow );
messageText.append( "---------------------" ).append( endRow );
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.PersonToContact" ) + " : " ).append(
environmentSubstitute( contactPerson ) ).append( endRow );
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.Tel" ) + " : " ).append(
environmentSubstitute( contactPhone ) ).append( endRow );
messageText.append( endRow );
}
// Include the path to this job entry...
if ( !onlySendComment ) {
JobTracker jobTracker = parentJob.getJobTracker();
if ( jobTracker != null ) {
messageText.append( BaseMessages.getString( PKG, "JobMail.Log.Comment.PathToJobentry" ) + ":" ).append(
endRow );
messageText.append( "------------------------" ).append( endRow );
addBacktracking( jobTracker, 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 ( int i = 0; i < fileType.length; i++ ) {
if ( fileType[i] == resultFile.getType() ) {
found = true;
}
}
if ( found ) {
// create a data source
MimeBodyPart files = new MimeBodyPart();
URLDataSource fds = new URLDataSource( file.getURL() );
// get a data Handler 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 + environmentSubstitute( zipFilename ) );
ZipOutputStream zipOutputStream = null;
try {
zipOutputStream = new ZipOutputStream( new FileOutputStream( masterZipfile ) );
for ( ResultFile resultFile : resultFiles ) {
boolean found = false;
for ( int i = 0; i < fileType.length; i++ ) {
if ( fileType[i] == resultFile.getType() ) {
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( KettleVFS.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 Handler 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.length > 0 ) {
FileObject imageFile = null;
for ( int i = 0; i < embeddedimages.length; i++ ) {
String realImageFile = environmentSubstitute( embeddedimages[i] );
String realcontenID = environmentSubstitute( contentids[i] );
if ( messageText.indexOf( "cid:" + realcontenID ) < 0 ) {
if ( log.isDebug() ) {
log.logDebug( "Image [" + realImageFile + "] is not used in message body!" );
}
} else {
try {
boolean found = false;
imageFile = KettleVFS.getFileObject( realImageFile, this );
if ( imageFile.exists() && imageFile.getType() == FileType.FILE ) {
found = true;
} else {
log.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++;
log.logBasic( "Image '" + fds.getName() + "' was embedded in message." );
}
} catch ( Exception e ) {
log.logError( "Error embedding image [" + realImageFile + "] in message : " + e.toString() );
log.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 {
transport = session.getTransport( protocol );
String authPass = getPassword( authenticationPassword );
if ( usingAuthentication ) {
if ( !Utils.isEmpty( port ) ) {
transport.connect(
environmentSubstitute( Const.NVL( server, "" ) ),
Integer.parseInt( environmentSubstitute( Const.NVL( port, "" ) ) ),
environmentSubstitute( Const.NVL( authenticationUser, "" ) ),
authPass );
} else {
transport.connect(
environmentSubstitute( Const.NVL( server, "" ) ),
environmentSubstitute( 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 ) {
SendFailedException sfex = (SendFailedException) ex;
Address[] invalid = sfex.getInvalidAddresses();
if ( invalid != null ) {
logError( " ** Invalid Addresses" );
for ( int i = 0; i < invalid.length; i++ ) {
logError( " " + invalid[i] );
result.setNrErrors( 1 );
}
}
Address[] validUnsent = sfex.getValidUnsentAddresses();
if ( validUnsent != null ) {
logError( " ** ValidUnsent Addresses" );
for ( int i = 0; i < validUnsent.length; i++ ) {
logError( " " + validUnsent[i] );
result.setNrErrors( 1 );
}
}
Address[] validSent = sfex.getValidSentAddresses();
if ( validSent != null ) {
// System.out.println(" ** ValidSent Addresses");
for ( int i = 0; i < validSent.length; i++ ) {
logError( " " + validSent[i] );
result.setNrErrors( 1 );
}
}
}
if ( ex instanceof MessagingException ) {
ex = ( (MessagingException) ex ).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;
}
private void addBacktracking( JobTracker jobTracker, StringBuilder messageText ) {
addBacktracking( jobTracker, messageText, 0 );
}
private void addBacktracking( JobTracker jobTracker, StringBuilder messageText, int level ) {
int nr = jobTracker.nrJobTrackers();
messageText.append( Const.rightPad( " ", level * 2 ) );
messageText.append( Const.NVL( jobTracker.getJobName(), "-" ) );
JobEntryResult jer = jobTracker.getJobEntryResult();
if ( jer != null ) {
messageText.append( " : " );
if ( jer.getJobEntryName() != null ) {
messageText.append( " : " );
messageText.append( jer.getJobEntryName() );
}
if ( jer.getResult() != null ) {
messageText.append( " : " );
messageText.append( "[" + jer.getResult().toString() + "]" );
}
if ( jer.getReason() != null ) {
messageText.append( " : " );
messageText.append( jer.getReason() );
}
if ( jer.getComment() != null ) {
messageText.append( " : " );
messageText.append( jer.getComment() );
}
if ( jer.getLogDate() != null ) {
messageText.append( " (" );
messageText.append( XMLHandler.date2string( jer.getLogDate() ) );
messageText.append( ')' );
}
}
messageText.append( Const.CR );
for ( int i = 0; i < nr; i++ ) {
JobTracker jt = jobTracker.getJobTracker( i );
addBacktracking( jt, messageText, level + 1 );
}
}
public boolean evaluates() {
return true;
}
public boolean isUnconditional() {
return true;
}
/**
* @return the usingSecureAuthentication
*/
public boolean isUsingSecureAuthentication() {
return usingSecureAuthentication;
}
/**
* @param usingSecureAuthentication
* the usingSecureAuthentication to set
*/
public void setUsingSecureAuthentication( boolean usingSecureAuthentication ) {
this.usingSecureAuthentication = usingSecureAuthentication;
}
/**
* @return the port
*/
public String getPort() {
return port;
}
/**
* @param port
* the port to set
*/
public void setPort( String port ) {
this.port = port;
}
public List<ResourceReference> getResourceDependencies( JobMeta jobMeta ) {
List<ResourceReference> references = super.getResourceDependencies( jobMeta );
String realServername = jobMeta.environmentSubstitute( server );
ResourceReference reference = new ResourceReference( this );
reference.getEntries().add( new ResourceEntry( realServername, ResourceType.SERVER ) );
references.add( reference );
return references;
}
@Override
public void check( List<CheckResultInterface> remarks, JobMeta jobMeta, VariableSpace space,
Repository repository, IMetaStore metaStore ) {
JobEntryValidatorUtils.andValidator().validate( this, "server", remarks,
AndValidator.putValidators( JobEntryValidatorUtils.notBlankValidator() ) );
JobEntryValidatorUtils.andValidator()
.validate( this, "replyAddress", remarks, AndValidator.putValidators(
JobEntryValidatorUtils.notBlankValidator(), JobEntryValidatorUtils.emailValidator() ) );
JobEntryValidatorUtils.andValidator().validate( this, "destination", remarks,
AndValidator.putValidators( JobEntryValidatorUtils.notBlankValidator() ) );
if ( usingAuthentication ) {
JobEntryValidatorUtils.andValidator().validate( this, "authenticationUser", remarks,
AndValidator.putValidators( JobEntryValidatorUtils.notBlankValidator() ) );
JobEntryValidatorUtils.andValidator().validate( this, "authenticationPassword", remarks,
AndValidator.putValidators( JobEntryValidatorUtils.notNullValidator() ) );
}
JobEntryValidatorUtils.andValidator().validate( this, "port", remarks,
AndValidator.putValidators( JobEntryValidatorUtils.integerValidator() ) );
}
public String getPassword( String authPassword ) {
return Encr.decryptPasswordOptionallyEncrypted(
environmentSubstitute( Const.NVL( authPassword, "" ) ) );
}
}