Alert about bounced emails using Spring Boot - java

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

Related

java outlook client application getting 535 5.7.3 Authentication unsuccessful exception [duplicate]

I get this error when I try to send a mail using JavaMail API. I am sure that the username and password are 100% correct. The Gmail account which I'm connecting is an older account, because they say it takes time for it to work with new accounts.
DEBUG SMTP RCVD: 535-5.7.1 Username and Password not accepted. Learn more at
535 5.7.1 http://mail.google.com/support/bin/answer.py?answer=14257 x35sm3011668
wfh.6
javax.mail.SendFailedException: Sending failed;
nested exception is:
javax.mail.AuthenticationFailedException
at javax.mail.Transport.send0(Transport.java:218)
at javax.mail.Transport.send(Transport.java:80)
at Main.(Main.java:41)
at Main.main(Main.java:51)
and this is my code:
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
public class Main
{
String d_email = "abc#gmail.com",
d_password = "pass",
d_host = "smtp.gmail.com",
d_port = "465",
m_to = "abc#gmail.com",
m_subject = "Testing",
m_text = "testing email.";
public Main()
{
Properties props = new Properties();
props.put("mail.smtp.user", d_email);
props.put("mail.smtp.host", d_host);
props.put("mail.smtp.port", d_port);
props.put("mail.smtp.starttls.enable","true");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.debug", "true");
props.put("mail.smtp.socketFactory.port", d_port);
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
SecurityManager security = System.getSecurityManager();
try
{
Authenticator auth = new SMTPAuthenticator();
Session session = Session.getInstance(props, auth);
session.setDebug(true);
MimeMessage msg = new MimeMessage(session);
msg.setText(m_text);
msg.setSubject(m_subject);
msg.setFrom(new InternetAddress(d_email));
msg.addRecipient(Message.RecipientType.TO, new InternetAddress(m_to));
Transport.send(msg);
}
catch (Exception mex)
{
mex.printStackTrace();
}
}
public static void main(String[] args)
{
Main blah = new Main();
}
private class SMTPAuthenticator extends javax.mail.Authenticator
{
public PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(d_email, d_password);
}
}
}
I had same issue :
I refer this link, I have followed below steps it worked for me.
By default Gmail account is highly secured. When we use gmail smtp from non gmail tool, email is blocked. To test in our local environment, make your gmail account less secure as
Login to Gmail.
Access the URL as https://www.google.com/settings/security/lesssecureapps
Select "Turn on"
The given code snippet works fine on my Gmail account, so this problem lies somewhere else. Did you follow the link given in the error message? It contains the following hints:
Make sure that you've entered your full email address (e.g. username#gmail.com)
Re-enter your password to ensure that it's correct. Keep in mind that passwords are case-sensitive.
Make sure your mail client isn't set to check for new mail too often. If your mail client checks for new messages more than once every 10 minutes, your client might repeatedly request your username and password.
Especially the last point is important. Google is very strict in this. If you're trying to connect Gmail for example more than 10 times in a minute programmatically, then you may already get blocked. Have a bit of patience, after some time it will get unblocked.
If you'd like more freedom in sending mails, I recommend to look for a dedicated mail host or to setup your own mail server, such as Apache James or Microsoft Exchange. I've already answered this in detail in one of your previous questions.
I encountered the exact same problem, for me the reason is I have turned on 2-step verification on my gmail account.
After generating a new application-specific password and use that in my java application, this "535 5.7.1" issue is gone.
You can generate a new application-specific password following this official google guide.
I faced the same problem although my username and password were correct, but after some research, I was able to send email through applications by enabling the Less secure app access option on my Gmail account. You can find this feature under security option in the left menu.
Related links:
I can't sign in to my email client
Enable access to less secure applications
Now, Google not supported Less Secure Apps so we are getting this issue.
You follow bellow steps to resolve your Authentication issue during send mail using SMTP.
1 - Enable 2-step Verification in your mail
2 - Once you enabled above, you will able to see App Passwords option in same Security tab
3 - Now generate App Password for Other(Custom)
4 - Use this generated password in place of your gmail password in your code.
Enjoy :)
I have the same error message and this is how I have solved the issue,
Create an app password: here is how we can generate an app password,
1. Visit your App passwords page. You may be asked to sign in to your Google Account.
2. At the bottom, click Select app and choose the app you’re using.
Click Select device and choose the device you’re using.
3. Select Generate.
4. Follow the instructions to enter the App password (the 16 character code in the yellow bar) on your device.
5. Select Done.
I worked for a Spring boot app and I get the app password say, sadsadaffferere for the email address, xyz#gmail.com. So, I need to configure the app properties like the following,
# the email settings
# ------------------
spring.mail.host=smtp.gmail.com
spring.mail.username=xyz#gmail.com
spring.mail.password=sadsadaffferere
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.socketFactory.port=465
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.smtp.socketFactory.fallback=false
support.email=xyz#gmail.com
Everything works fine afterwards
You need to turn on the 'less secure apps allowed' in gmail settings.
If the same credential was working earlier and it stopped working then the primary reason for this problem is password mismatch/changed on gmail client and not updated in Jenkins or other CI server. If that is not the case then check for reasons mentioned by #BalusC
i turned off antivirus and enabled less secure app in gmail, but now google has disabled this option, for that to work you need to do two steps verification and genetare 16 digit password and paste in application.properties replace this 16 digit password.

JavaMail: Sending Mail via 1und1 gives 550 Error

A friend of mine has a new website and i build him a tool to organize email-addresses and send newsletters (HTML-Messages). Therefore i use the javax.mail library. While developing i sent all mails from my gmail account and it worked fine. But now, for "production", i want to send the mails from his domain, which is hosted by 1und1.
I am able to connect successfully to the 1und1 smtp-Server, but as soon as i send a mail, i get the following errors:
com.sun.mail.smtp.SMTPSenderFailedException: 550-Requested action not taken: mailbox unavailable
550 invalid DNS MX or A/AAAA resource record
When sending from the online 1und1 webmailer, i can send to any mailadress without errors.
These are the props i set for mailing. Like i said, i can connect to the server with these information, but sending fails.
Properties props = new Properties();
// Zum Senden
props.setProperty("mail.smtp.starttls.enable" , "true");
props.setProperty("mail.smtp.host", configurationService.getMailHost()); // smtp.1und1.de
props.setProperty("mail.smtp.auth", configurationService.getMailAuth().toString()); // true
props.setProperty("mail.smtp.port", configurationService.getMailPort()); // 587
props.setProperty("mail.smtp.user", user);
props.setProperty("mail.smtp.password", pass);
return Session.getInstance(props, new javax.mail.Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(props.getProperty("mail.smtp.user"),
props.getProperty("mail.smtp.password"));
}
});
Thanks for help!
Edit: When Debugging the Session, i get the following line just before the exception occurs:
MAIL FROM: <USER#LAPTOP-Name.fritz.box>
How can i change this to something accepted?

Java email received from standalone class, but not when executed within a servlet

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...)

Appengine not sending email

I am trying to use the Appengine Mail API to send mail from my application. I'm running the code below which executes fine without exceptions but no mail is actually sent. Here's the code:
public static void sendNotificationEmail(String subject, String message, String emailAddress) {
System.err.println("Sending mail: " + emailAddress);
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
try {
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("a#b.com", "Name"));
msg.addRecipient(Message.RecipientType.TO, new InternetAddress(emailAddress));
msg.setSubject(subject);
msg.setText(message);
Transport.send(msg);
System.err.println("done");
}
catch (Exception e) {
System.err.println(e);
}
}
a#b.com is a registered owner of the app (but not the billing administrator). Looking at the logs, "done" is printed but no email is sent.
Any ideas?
UPDATE:
Found out the emails are actually getting through but are in the spam folder. Any way to give my app more (reputation) to not be marked as spam. Our app sends out notification emails to users when something has changed in the system.
Maybe you email marked as spam because it was sent from different host, I mean that maybe it's not GMail-connected domain.
At this case, to prevent it from being marked as spam, it's very helpful to configure SPF for your domain, see google help: Creating an SPF record. It has helped me some time ago

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.
--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).

Categories

Resources