Verify mail server connection programmatically in ColdFusion
Asked Answered
O

3

6

I'm using custom SMTP servers and would like to verify the connection when user enters his own server credentials.

Exactly the same type of check as Adobe CF and Railo allow to do when adding mail server.

Sure, this does not guarantee that delivery will be working, but at least to check that entered server/username/pass are valid.

I can see one tricky way: try to send the email with cfmail and check the mail log. But I believe that it can be done with more elegant.

Is there any Java library available with standard ACF/Railo distro to help me? How would I use them? Examples are highly appreciated.

Thanks in advance.

EDIT:

Please don't be confused with Java tag present. Solution needed in CFML. Though it can use some Java libraries, if applicable.

Opus answered 22/4, 2010 at 8:15 Comment(0)
M
7

I think sfussenegger has the right idea. But instead of using a custom authenticator, what about authenticating via connect(..)? Only tested with gmail. But it seems to work.

EDIT: I tested this with CF9 & OBD successfully. Unfortunately, I had no luck with Railo ... bummer.

EDIT: Updated to add the missing "mail.smtp.auth" property. It should now work correctly with Railo as well.

    //Java Version
    int port = 587;
    String host = "smtp.gmail.com";
    String user = "[email protected]";
    String pwd = "email password";

    try {
        Properties props = new Properties();
        // required for gmail 
        props.put("mail.smtp.starttls.enable","true");
        props.put("mail.smtp.auth", "true");
        // or use getDefaultInstance instance if desired...
        Session session = Session.getInstance(props, null);
        Transport transport = session.getTransport("smtp");
        transport.connect(host, port, user, pwd);
        transport.close();
        System.out.println("success");
     } 
     catch(AuthenticationFailedException e) {
           System.out.println("AuthenticationFailedException - for authentication failures");
           e.printStackTrace();
     }
     catch(MessagingException e) {
           System.out.println("for other failures");
           e.printStackTrace();
     }



<cfscript>
    //CF Version
    port = 587;
    host = "smtp.gmail.com";
    user = "[email protected]";
    pwd = "email password";

    try {
        props = createObject("java", "java.util.Properties").init();
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.auth", "true");
        // or use getDefaultInstance instance if desired...
        mailSession = createObject("java", "javax.mail.Session").getInstance(props, javacast("null", ""));
        transport = mailSession.getTransport("smtp");
        transport.connect(host, port, user, pwd);
        transport.close();
        WriteOutput("success");
     } 
     //for authentication failures
     catch(javax.mail.AuthenticationFailedException e) {
           WriteOutput("Error: "& e.type &" ** "& e.message);
     }
     // for other failures
     catch(javax.mail.MessagingException e) {
           WriteOutput("Error: "& e.type &" ** "& e.message);
     }
</cfscript>
Mozza answered 23/4, 2010 at 1:42 Comment(7)
Thanks! Exactly what I've needed.Opus
Welcome :) (extra characters here because SO hates brevity)Mozza
Also, testing this now, I only receive success messages, even if settings are wrong. Any idea why? (Using hosted SMTP with 1and1, rather than gmail)Plumy
@Plumy - (Edit) I ran a quick test against "smtp.1and1.com" with bad credentials and it throws an error as expected: Error: javax.mail.AuthenticationFailedException ** 535 Authentication credentials invalid Are you using the exact code above and no errors thrown or in the logs? Which settings - host or credentials? It should throw an exception, so I am wondering if maybe it is being cached/saved. Does it happen after connecting successfully at least once?Mozza
@Leigh, Thank you so much for testing this! I have a fear that, yes the information has been cached because I tested with correct credentials the first time. I was using pretty much the exact code above (just changed variable names in transport.connect() which are all correct). I will check into releasing cached information. If you have an idea of where to start, I'd love to hear!Plumy
@Leigh, I added a cache clear on destroy of the app, it's working now - Thank you for the pointer in the right direction!Plumy
@Plumy - So the transport instance was being cached? That would explain the results, if the transport instance caches connection data. Thanks for the follow-up!Mozza
H
1

Using Apache Commons Net, you can do something like this:

try {
     int reply;
     client.connect("mail.foobar.com");
     System.out.print(client.getReplyString());
     // After connection attempt, you should check the reply code to verify
     // success.
     reply = client.getReplyCode();
     if(!SMTPReply.isPositiveCompletion(reply)) {
       client.disconnect();
       System.err.println("SMTP server refused connection.");
       System.exit(1);
     }
     // Do useful stuff here.
     ...
   } catch(IOException e) {
     if(client.isConnected()) {
       try {
         client.disconnect();
       } catch(IOException f) {
         // do nothing
       }
     }
     System.err.println("Could not connect to server.");
     e.printStackTrace();
     System.exit(1);
   }

Where client is an instance of org.apache.commons.net.smtp.SMTPClient class. Code above was taken from the SMTPClient API Docs.

Halakah answered 22/4, 2010 at 8:32 Comment(1)
Thanks, at least this class is present in Railo's package and I can test connection now. Though I can't find the method to test the user/pass.Opus
A
0

That's not pretty, but could do: simply use try to send an email to an illegal address and see what error message you get. If the error message complains about failed authentication, you know what to do.

EDIT some working code:

here's some working code to validate Gmail credentials. Gmail doesn't complain about the illegal@localhost though. Now you could either try to search for a recipient that causes an exception or really send mail to an address and discard it immediately.. Edit: It's not even necessary to send something. Simply connect and handle a possible AuthenticationFailedException.

import java.util.Properties;
import javax.mail.AuthenticationFailedException;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;

public class Main {

    private static Session createSmtpSession(final String user, final String password) {
        final Properties props = new Properties();
        props.setProperty("mail.transport.protocol", "smtp");
        props.setProperty("mail.smtp.host", "smtp.gmail.com");
        props.setProperty("mail.smtp.auth", "true");
        props.setProperty("mail.smtp.port", "587");
        props.setProperty("mail.smtp.starttls.enable", "true");

        return Session.getDefaultInstance(props, new javax.mail.Authenticator() {

            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(user, password);
            }
        });
    }

    private static boolean validateCredentials(String user, String password) {
        try {
            Transport transport = createSmtpSession(user, password).getTransport();
            transport.connect();
            transport.close();
        } catch (AuthenticationFailedException e) {
            return false;
        } catch (MessagingException e) {
            throw new RuntimeException("validate failed", e);
        }
        return true;
    }

    public static void main(String[] args) {
        System.out.println(validateCredentials(args[0], args[1]));
    }

}
Annotate answered 22/4, 2010 at 8:23 Comment(5)
Nope, there wont be any errors if email attributes have correct syntax: message will be pushed into the pool. That's what I've mentioned in my quesion when told about mail.log parsing.Opus
@Sergii here's some working code. It's not about parsing logs, it's only exception handling.Annotate
if there is no such exception, the SMTP server is most likely configured to silently fail which would be quite odd. I suspect that cfmail is simply swallowing the exception.Annotate
Sorry, are we talking about COLDFUSION? I can't use pure Java solution, sorry.Opus
Obviously I forgot about the coldfusion tag since I first answered the question. Well, maybe somebody else finds this useful after all.Annotate

© 2022 - 2024 — McMap. All rights reserved.