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.
--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.
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)
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.
Try this:
String to="stackoverflow#so.com";
String cc="one#mail.com,two#mail.com"; //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.
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).
Related
I need to send email from java program. I am first trying to understand basics. I found a snippet at:
https://www.javatpoint.com/example-of-sending-email-using-java-mail-api
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
public class SendEmail
{
public static void main(String [] args){
String to = "sonoojaiswal1988#gmail.com";//change accordingly
String from = "sonoojaiswal1987#gmail.com";change accordingly
String host = "localhost";//or IP address
//Get the session object
Properties properties = System.getProperties();
properties.setProperty("mail.smtp.host", host);
Session session = Session.getDefaultInstance(properties);
//compose the message
try{
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,new InternetAddress(to));
message.setSubject("Ping");
message.setText("Hello, this is example of sending email ");
// Send message
Transport.send(message);
System.out.println("message sent successfully....");
}catch (MessagingException mex) {mex.printStackTrace();}
}
}
My question is that as per the code, it looks like anyone can use any sender email address string and send infinite emails to any receiver email address. I am missing something in my understanding, which will prevent such scenario to happen. Please help.
I understand that this is not a programming question, but guess, it will not take too much time to answer this basic question and don't know any other equally active forum.
This example works for servers which don't need authentication. And this is usually not applicable to the smtp servers used in production. Such servers are used mostly for testing purposes where they are not exposed over the internet. Hence, although its possible to send infinite number of mails as mentioned by you, no one would be interested in doing the same.
For the servers where authentication is necessary, credentials need to be provided. And this is explained in detail in the blog mentioned by you.
I have a Spring Boot application that sends emails. For any action that requires notification, a Mail instance is created with status PENDING in the database and a job is run to send the pending emails every minute. The status of the Mail instances are set as PENDING, SENT or FAILED.
try {
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.smtp.from", myEmailAddress);
props.put("mail.smtp.timeout", 2000);
Session session = Session.getInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
MimeMessage message = new MimeMessage(session);
MimeMultipart content = getContent(mail, message, username);
message.setContent(content);
Transport.send(message);
mail.setStatus(MailStatus.SENT);
mailService.save(mail);
} catch (MailConnectException mce) {
mail.setStatus(MailStatus.FAILED);
mailService.save(mail);
} catch (Exception e) {
// other actions
}
Now, this works fine if a valid email id is provided. But when the receiving email address is a non-existent one like somerandomname#gmail.com, there are no exceptions thrown. From what I read from similar questions in SO and elsewhere, I understand that mail sending is an asynchronous process and hence there is no way to determine that a given email is existing or not (or other issues like Inbox full). That is why after the Transport.send(message); statement, the mail.setStatus(MailStatus.SENT); statement will always be executed irrespective of the email address being present. Later the mail will actually be attempted to be sent and I get an email in myEmailAddress with content like the following:
The response from the remote server was: 550 5.1.1
somerandomname#gmail.com User unknown
Okay, accepted until this point. But now I need a way to alert the user that the email couldn't be sent because they entered an invalid email so that they can update their email. More specifically, I need to set the status of the Mail instance to FAILED. What is the best way to achieve this?
If you use a service like AWS SES to send your mail, you can receive notifications about bounces (and complaints) either simply as notifications for a human to read, or as programmatic triggers that could be used to call an alert endpoint on your spring boot server, for example through AWS Lambda. There are other services that may be able to do the same, such as SendGrid.
Your Spring Boot application would have to retrieve the notifications (an email itself) from your inbox corresponding to the sender address of the initial email. There are various options how to achieve this:
Use a service with provides callbacks (triggers) for incoming messages as mentioned by Benjamin
Poll your inbox using a standard protocol like POP or IMAP
Hint for the second option: https://docs.spring.io/spring-integration/reference/html/mail.html#mail-inbound
I'd like to send mail from my GAE project. I've followed the documentation example...
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
try {
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("xxx#xxxx.appspotmail.com", "Example.com Admin"));
msg.addRecipient(Message.RecipientType.TO,
new InternetAddress("xxxxx#gmail.com", "Mr. User"));
msg.setSubject("Your Example.com account has been activated");
msg.setText("This is a test");
Transport.send(msg);
} catch (AddressException e) {
e.printStackTrace();
} catch (MessagingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
After deployment, I get this exception message
javax.mail.MessagingException: Could not connect to SMTP host: localhost, port: 25;
But the documentation says that:
When you create a JavaMail Session, if you do not provide any SMTP server configuration, App Engine uses the Mail service for sending messages
But it seems to try connecting to a SMTP server... and obviously there is no SMTP server on localhost...
I've never used this service... my quotas are full available.
Please, help me !
had the same issue today. just got it working. app engine sdk already includes the classes you will need to send email:
https://cloud.google.com/appengine/docs/standard/java/javadoc/com/google/appengine/api/mail/MailService.Message
that and the related classes are the way to invoke the mail service. replace your message classes with those, remove all references to javax.mail. one other thing in case you're referencing this (as I was):
https://cloud.google.com/appengine/docs/standard/java/mail/sending-mail-with-mail-api
I couldn't get it to work, doesn't looks like it would without an smtp host at least. Nice of google to provide nonsensical documentation for a non-working example in their example code base
also, if you follow the "who can send mail" link it tells you that any address of the form anything#[APP_NAME].appspotmail.com or anything#[APP_ALIAS].appspotmail.com should work. using my apps name resulted in "unauthorized sender", but using the app id from the dashboard worked. what should have been a ten minute solution turned into hours of drudgery, but I have a working emailer. thanks, google.
The Mail service API supports the JavaMail (javax.mail) interface which is included with the App Engine SDK. Using any other jars may create the issue. You may follow the code sample in Java 7 and Java 8 which demonstrate how to send mail.
I should note that outbound connections on ports 25, 465, and 587 are not allowed due to spam concerns, so the sender address of a message must be one of the optioned in this link.
You can take your application ID/name (which is the same as the project ID/name) through the dashboard.
Kindly note that Issue Tracker is reserved for reporting bugs and feature requests. If you encounter any issue related to APP_NAME or APP_ALIAS, it is recommended to report the issue there so that we would be able to dig into the problem.
I've been working on a java application to use my Gmail connection to send an email but am hitting a wall.
I've found out that I need to use OAuth which the current implementation of JavaMail uses, but I haven't really found a clear cut guide to how to do this. I'm an amateur programmer and well, kind of a dummy when it comes to comprehension (I'm working really hard for some C's in school, lol) but I'm trying to learn here.
Most of what I've put together has been culled from other things I've found, trying to get something working, which it kind of is. But the problem that I'm hitting is one of two things.
Either I can't get authorized, then I get an email from Google saying an app was trying to access my email without complying with modern security standards, or that I need to log in via a web browser.
From what I've found, a few years back Google changed their security which is why we need to use OAuth now, which, luckily JavaMail supports, but I can't get it working. And a lot of the help I've seen here on Stack Overflow or other places is nearly 5 years old.
Right now, I'm trying to make a java program in eclipse to just send an email, and I'm using github.com/google/gmail-oauth2-tools
To try and make the OAuth token, but there's an error
Error Line
The method in OAuth2SaslClientFactory
public SaslClient createSaslClient(String[] mechanisms,
String authorizationId,
String protocol,
String serverName,
Map<String, ?> props,
CallbackHandler callbackHandler) {
boolean matchedMechanism = false;
for (int i = 0; i < mechanisms.length; ++i) {
if ("XOAUTH2".equalsIgnoreCase(mechanisms[i])) {
matchedMechanism = true;
break;
}
}
if (!matchedMechanism) {
logger.info("Failed to match any mechanisms");
return null;
}
return new OAuth2SaslClient((String)props.get(OAUTH_TOKEN_PROP), callbackHandler);
}
The code from OAuth2SASLClient
public OAuth2SaslClient(String oauthToken, CallbackHandler callbackHandler) {
this.oauthToken = oauthToken;
this.callbackHandler = callbackHandler; }
That return line is saying the constructor can't handle that, but the constructor in OAuth2SaSLClient.java is actually set up to have a (String, Callback) parameter.
If somebody has something quick and easy that works, I'd love to see it,even though it appears that this needs to use a specific way of creating an OAuth token.
I can post my code if it helps, but I need to clean it up first, it's sort of a first draft / messy collage of things I've tried to get working.
Or a javamail / Oauth tutorial for dummies.
The end goal here is to tie this to a program that can send out a quick notification when something happens. Which I can't seem to do.
Mixing OAuth and javamail won't work, you need to choose your API: either the Gmail v4 API (with OAuth) or the javamail API. You said you are just trying to send emails from your own account, so I'd suggest you use the javamail IMAP API. When going through the javamail API you don't need Oauth at all.
For example:
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
Session session = Session.getInstance(props, new javax.mail.Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(GOOGLE_USERNAME, password);
}
});
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(FROM_EMAIL, FROM_NAME));
// rest of the email settings
If you needed to send emails on behalf of other people, or need to do more advanced mail operations such as create a Gmail draft, then the Gmail API would be the proper approach.
Note: if you are going to use the Gmail API, be sure to use the newer Gmail API: https://developers.google.com/gmail/api/quickstart/java
The old Google Data API is deprecated
Using Java, I can send email from a toy standalone class (with hard-coded data) perfectly fine, and I receive the email within seconds.
When the exact same code is called in the context of a servlet, however, the code executes without failure, but I don't actually receive the email.
How could that be? Is there something special about the servlet environment?
I'm running Tomcat 6, JRE 1.5.
Here is the code (I have edited my data):
public void sendEmail(String aSubject, String aBody){
Properties props = new Properties();
props.put("mail.host", "smtp.blah.ca");
props.put("mail.from", "from-blah#blah.ca");
Session session = Session.getDefaultInstance(props, null /*no authenticator*/);
System.out.println("JRE" + System.getProperty("java.version"));
System.out.println(session.getProperties());
MimeMessage message = new MimeMessage(session);
try {
message.addRecipient(
Message.RecipientType.TO, new InternetAddress("to-blah#blah.com")
);
message.setSubject(aSubject);
message.setText(aBody);
Transport.send(message);
}
catch (MessagingException ex){
System.err.println("Cannot send email. " + ex);
}
}
Edit: the email is recieved when the content (email body) is small. When the content is large, it's not recieved.
This is not java-related at all.
I tested a large email with a normal email client. The same behavior was seen: the email is sent fine, but I'm not receiving it in a timely manner, for some reason. (I haven't seen this kind of behavior before, but there it is...)