org.pentaho.di.trans.steps.mailvalidator.MailValidation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kettle-engine Show documentation
Show all versions of kettle-engine Show documentation
Container pom for Pentaho Data Integration modules
The newest version!
/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 by Hitachi Vantara : 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.trans.steps.mailvalidator;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Hashtable;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import org.apache.commons.validator.GenericValidator;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.i18n.BaseMessages;
public class MailValidation {
private static Class> PKG = MailValidatorMeta.class; // for i18n purposes, needed by Translator2!!
public static boolean isRegExValid( String emailAdress ) {
return GenericValidator.isEmail( emailAdress );
}
/**
* verify if there is a mail server registered to the domain name. and return the email servers count
*/
public static int mailServersCount( String hostName ) throws NamingException {
Hashtable env = new Hashtable();
env.put( "java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory" );
DirContext ictx = new InitialDirContext( env );
Attributes attrs = ictx.getAttributes( hostName, new String[] { "MX" } );
Attribute attr = attrs.get( "MX" );
if ( attr == null ) {
return ( 0 );
}
return ( attr.size() );
}
private static String className() {
return BaseMessages.getString( PKG, "MailValidator.ClassName" );
}
private static int hear( BufferedReader in ) throws IOException {
String line = null;
int res = 0;
while ( ( line = in.readLine() ) != null ) {
String pfx = line.substring( 0, 3 );
try {
res = Integer.parseInt( pfx );
} catch ( Exception ex ) {
res = -1;
}
if ( line.charAt( 3 ) != '-' ) {
break;
}
}
return res;
}
private static void say( BufferedWriter wr, String text ) throws IOException {
wr.write( text + "\r\n" );
wr.flush();
return;
}
private static ArrayList getMX( String hostName ) throws NamingException {
// Perform a DNS lookup for MX records in the domain
Hashtable env = new Hashtable();
env.put( "java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory" );
DirContext ictx = new InitialDirContext( env );
Attributes attrs = ictx.getAttributes( hostName, new String[] { "MX" } );
Attribute attr = attrs.get( "MX" );
// if we don't have an MX record, try the machine itself
if ( ( attr == null ) || ( attr.size() == 0 ) ) {
attrs = ictx.getAttributes( hostName, new String[] { "A" } );
attr = attrs.get( "A" );
if ( attr == null ) {
throw new NamingException( BaseMessages.getString( PKG, "MailValidator.NoMatchName", hostName ) );
}
}
// Huzzah! we have machines to try. Return them as an array list
// NOTE: We SHOULD take the preference into account to be absolutely
// correct. This is left as an exercise for anyone who cares.
ArrayList res = new ArrayList();
NamingEnumeration> en = attr.getAll();
while ( en.hasMore() ) {
String x = (String) en.next();
String[] f = x.split( " " );
if ( f[1].endsWith( "." ) ) {
f[1] = f[1].substring( 0, ( f[1].length() - 1 ) );
}
res.add( f[1] );
}
return res;
}
/**
* Validate an email address This code is from : http://www.rgagnon.com/javadetails/java-0452.html
*
* @param email
* address
* @param sender
* email address
* @param default SMTP Server
* @param timeout
* for socket connection
* @param deepCheck
* (if we want to perform a SMTP check
* @return true or false
*/
public static MailValidationResult isAddressValid( LogChannelInterface log, String address,
String senderAddress, String defaultSMTPServer, int timeout, boolean deepCheck ) {
MailValidationResult result = new MailValidationResult();
if ( !isRegExValid( address ) ) {
result.setErrorMessage( BaseMessages.getString( PKG, "MailValidator.MalformedAddress", address ) );
return result;
}
// Find the separator for the domain name
int pos = address.indexOf( '@' );
// If the address does not contain an '@', it's not valid
if ( pos == -1 ) {
return result;
}
if ( !deepCheck ) {
result.setValide( true );
return result;
}
// Isolate the domain/machine name and get a list of mail exchangers
String domain = address.substring( ++pos );
// Maybe user want to switch to a default SMTP server?
// In that case, we will ignore the domain
// extracted from email address
ArrayList mxList = new ArrayList();
if ( Utils.isEmpty( defaultSMTPServer ) ) {
try {
mxList = getMX( domain );
// Just because we can send mail to the domain, doesn't mean that the
// address is valid, but if we can't, it's a sure sign that it isn't
if ( mxList == null || mxList.size() == 0 ) {
result.setErrorMessage( BaseMessages.getString( PKG, "MailValidator.NoMachinesInDomain", domain ) );
return result;
}
} catch ( Exception ex ) {
result.setErrorMessage( BaseMessages.getString( PKG, "MailValidator.ErrorGettingMachinesInDomain", ex
.getMessage() ) );
return result;
}
} else {
mxList.add( defaultSMTPServer );
}
if ( log.isDebug() ) {
log.logDebug( BaseMessages.getString( PKG, "MailValidator.ExchangersFound", "" + mxList.size() ) );
}
// Now, do the SMTP validation, try each mail exchanger until we get
// a positive acceptance. It *MAY* be possible for one MX to allow
// a message [store and forwarder for example] and another [like
// the actual mail server] to reject it. This is why we REALLY ought
// to take the preference into account.
for ( int mx = 0; mx < mxList.size(); mx++ ) {
boolean valid = false;
BufferedReader rdr = null;
BufferedWriter wtr = null;
Socket skt = null;
try {
String exhanger = mxList.get( mx );
if ( log.isDebug() ) {
log.logDebug( className(), BaseMessages.getString( PKG, "MailValidator.TryingExchanger", exhanger ) );
}
int res;
skt = new Socket( exhanger, 25 );
// set timeout (milliseconds)
if ( timeout > 0 ) {
skt.setSoTimeout( timeout );
}
if ( log.isDebug() ) {
log.logDebug( className(), BaseMessages.getString(
PKG, "MailValidator.ConnectingTo", exhanger, "25", skt.isConnected() + "" ) );
}
rdr = new BufferedReader( new InputStreamReader( skt.getInputStream() ) );
wtr = new BufferedWriter( new OutputStreamWriter( skt.getOutputStream() ) );
res = hear( rdr );
if ( res != 220 ) {
throw new Exception( BaseMessages.getString( PKG, "MailValidator.InvalidHeader" ) );
}
// say HELLO it's me
if ( log.isDebug() ) {
log.logDebug( className(), BaseMessages.getString( PKG, "MailValidator.SayHello", domain ) );
}
say( wtr, "EHLO " + domain );
res = hear( rdr );
if ( res != 250 ) {
throw new Exception( "Not ESMTP" );
}
if ( log.isDebug() ) {
log.logDebug( className(), BaseMessages.getString( PKG, "MailValidator.ServerReplied", "" + res ) );
}
// validate the sender address
if ( log.isDebug() ) {
log.logDebug( className(), BaseMessages.getString( PKG, "MailValidator.CheckSender", senderAddress ) );
}
say( wtr, "MAIL FROM: <" + senderAddress + ">" );
res = hear( rdr );
if ( res != 250 ) {
throw new Exception( BaseMessages.getString( PKG, "MailValidator.SenderRejected" ) );
}
if ( log.isDebug() ) {
log.logDebug( className(), BaseMessages.getString( PKG, "MailValidator.SenderAccepted", "" + res ) );
}
// Validate receiver
if ( log.isDebug() ) {
log.logDebug( className(), BaseMessages.getString( PKG, "MailValidator.CheckReceiver", address ) );
}
say( wtr, "RCPT TO: <" + address + ">" );
res = hear( rdr );
// be polite
say( wtr, "RSET" );
hear( rdr );
say( wtr, "QUIT" );
hear( rdr );
if ( res != 250 ) {
throw new Exception( BaseMessages.getString( PKG, "MailValidator.AddressNotValid", address ) );
}
if ( log.isDebug() ) {
log.logDebug( className(), BaseMessages.getString( PKG, "MailValidator.ReceiverAccepted", address, ""
+ res ) );
}
valid = true;
} catch ( Exception ex ) {
// Do nothing but try next host
result.setValide( false );
result.setErrorMessage( ex.getMessage() );
} finally {
if ( rdr != null ) {
try {
rdr.close();
} catch ( Exception e ) {
// ignore this
}
}
if ( wtr != null ) {
try {
wtr.close();
} catch ( Exception e ) {
// ignore this
}
}
if ( skt != null ) {
try {
skt.close();
} catch ( Exception e ) {
// ignore this
}
}
if ( valid ) {
result.setValide( true );
result.setErrorMessage( null );
if ( log.isDebug() ) {
log.logDebug( className(), "=============================================" );
}
return result;
}
}
}
if ( log.isDebug() ) {
log.logDebug( className(), "=============================================" );
}
return result;
}
}