Java tutorial
/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2013 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.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.Const; import org.pentaho.di.core.logging.LogChannelInterface; 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<String, String> env = new Hashtable<String, String>(); 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<String> getMX(String hostName) throws NamingException { // Perform a DNS lookup for MX records in the domain Hashtable<String, String> env = new Hashtable<String, String>(); 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<String> res = new ArrayList<String>(); 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<String> mxList = new ArrayList<String>(); if (Const.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; } }