Steven M. Cherry
Steven M. Cherry

Reputation: 1375

Getting Invalid Address with javax.mail when the addresses are fine

I'm using the javax.mail system, and having problems with "Invalid Address" exceptions. Here's the basics of the code:

    // Get system properties
    Properties props = System.getProperties();

    // Setup mail server
    props.put("mail.smtp.host", m_sending_host);

    // Get session
    Session session = Session.getDefaultInstance(props, new Authenticator(){
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(m_sending_user, m_sending_pass);
        }
    });


    // Define message
    MimeMessage message = new MimeMessage(session);
    message.setFrom(new InternetAddress(m_sending_from));
    message.addRecipient(Message.RecipientType.TO, 
        new InternetAddress(vcea.get(i).emailaddr));
    message.setSubject( replaceEnvVars(subject) );
    message.setText(replaceEnvVars(body));

    // Send message
    try {
        Transport.send(message);
    } catch (Exception e){
        Log.Error("Error sending e-mail to addr (%s): %s",
                vcea.get(i).emailaddr, e.getLocalizedMessage() );

    }

The issue is that the above code does work, sometimes. But for some e-mail addresses that I know to be valid (because I can send to them via a standard e-mail client), the above code will throw an "Invalid Address" exception when trying to send.

Any clues or hints would be greatly appreciated.

--Update: problem with authentication.

Ok, here's what I've discovered was going on. When receiving e-mail, the code above correctly sets up authentication and the Authenticator.getPasswordAuthentication() callback is actually invoked.

Not so when sending e-mail. You have to do a bit more. Add this:

    // Setup mail server
    props.put("mail.smtp.host", m_sending_host);
    props.put("mail.smtp.auth", "true");

which will force the javax.mail API to do the login authentication. And then use an actual Transport instance instead of the static .send() method:

    Transport t = session.getTransport(m_sending_protocol);
    t.connect(m_sending_user, m_sending_pass);

...

        // Send message
        try {
            t.sendMessage(message, message.getAllRecipients());
        } catch (Exception e){

Without forcing the authentication, the mail server saw me as an unauthorized relay, and just shut me down. The difference between the addresses that "worked" and the addresses that didn't was that the ones that "worked" were all local to the mail server. Therefore, it simply accepted them. But for any non-local "relay" addresses, it would reject the message because my authentication information hadn't been presented by the javax.mail API when I thought it would have.

Thanks for the clues to prompt me to look at the mail server side of things as well.

Upvotes: 7

Views: 35539

Answers (5)

Agusti-N
Agusti-N

Reputation: 3973

Try this:

String to="[email protected]";
String cc="[email protected],[email protected]"; //The separator ',' works good

message.setRecipients(Message.RecipientType.TO,new InternetAddress[] { 
new InternetAddress(to) }); // This is only one mail

InternetAddress[] addr = parseAddressList(cc); //Here add all the rest of the mails
message.setRecipients(Message.RecipientType.CC,addr);

Sorry for my english. It's not good.

Upvotes: 0

Alex Cheptsov
Alex Cheptsov

Reputation: 9

A good hint for those using the ssl encryption in the smtp configuration, you should enable it by specifying the property mail.smtp.ssl.enable, as shown below:

props.put("mail.smtp.ssl.enable", "true");

Otherwise it can lead to similar problems as described above.

Upvotes: 0

celdrake
celdrake

Reputation:

This seems to me like a problem that happened at my work. If the code you are showing is being concurrent, then using directly System.getProperties could be a problem, because the host value you put on them can be overwritten by next request, while still keeping the user and password from the overwritten host.

In our case, we solved that using a clone of the System.getProperties() hashtable.

Hope that helps (this problem was really hard to track).

Upvotes: -1

Steven M. Cherry
Steven M. Cherry

Reputation: 1375

--Update: problem with authentication.

Ok, here's what I've discovered was going on. When receiving e-mail, the code above correctly sets up authentication and the Authenticator.getPasswordAuthentication() callback is actually invoked.

Not so when sending e-mail. You have to do a bit more. Add this:

// Setup mail server
props.put("mail.smtp.host", m_sending_host);
props.put("mail.smtp.auth", "true");

which will force the javax.mail API to do the login authentication. And then use an actual Transport instance instead of the static .send() method:

Transport t = session.getTransport(m_sending_protocol);
t.connect(m_sending_user, m_sending_pass);

...

    // Send message
    try {
        t.sendMessage(message, message.getAllRecipients());
    } catch (Exception e){

Without forcing the authentication, the mail server saw me as an unauthorized relay, and just shut me down. The difference between the addresses that "worked" and the addresses that didn't was that the ones that "worked" were all local to the mail server. Therefore, it simply accepted them. But for any non-local "relay" addresses, it would reject the message because my authentication information hadn't been presented by the javax.mail API when I thought it would have.

Thanks for the clues to prompt me to look at the mail server side of things as well.

Upvotes: 7

Alex B
Alex B

Reputation: 24926

I would change the call to InternetAddress to use the "strict" interpretation and see if you get further errors on the addresses you are having trouble with.

message.addRecipient(Message.RecipientType.TO, 
        new InternetAddress(vcea.get(i).emailaddr, true ));
//                                                 ^^^^ turns on strict interpretation

Javadoc for InternetAddress constructor

If this fails, it will throw an AddressException which has a method called getPos() which returns the position of the failure (Javadoc)

Upvotes: 2

Related Questions