Getting mail from GMail into Java application using IMAP - java

I want to access messages in Gmail from a Java application using JavaMail and IMAP. Why am I getting a SocketTimeoutException ?
Here is my code:
Properties props = System.getProperties();
props.setProperty("mail.imap.host", "imap.gmail.com");
props.setProperty("mail.imap.port", "993");
props.setProperty("mail.imap.connectiontimeout", "5000");
props.setProperty("mail.imap.timeout", "5000");
try {
Session session = Session.getDefaultInstance(props, new MyAuthenticator());
URLName urlName = new URLName("imap://MYUSERNAME#gmail.com:MYPASSWORD#imap.gmail.com");
Store store = session.getStore(urlName);
if (!store.isConnected()) {
store.connect();
}
} catch (NoSuchProviderException e) {
e.printStackTrace();
System.exit(1);
} catch (MessagingException e) {
e.printStackTrace();
System.exit(2);
}
I have set the timeout values so that it wouldn't take "forever" to timeout. Also, MyAuthenticator also has the username and password, which seems redundant with the URL. Is there another way to specify the protocol? (I didn't see it in the JavaDoc for IMAP.)

Using imaps was a great suggestion. Neither of the answers provided just worked for me, so I googled some more and found something that worked. Here's how my code looks now.
Properties props = System.getProperties();
props.setProperty("mail.store.protocol", "imaps");
try {
Session session = Session.getDefaultInstance(props, null);
Store store = session.getStore("imaps");
store.connect("imap.gmail.com", "<username>#gmail.com", "<password>");
...
} catch (NoSuchProviderException e) {
e.printStackTrace();
System.exit(1);
} catch (MessagingException e) {
e.printStackTrace();
System.exit(2);
}
This is nice because it takes the redundant Authenticator out of the picture. I'm glad this worked because the SSLNOTES.txt make my head spin.

You need to use the following properties for imaps:
props.setProperty("mail.imaps.host", "imap.gmail.com");
props.setProperty("mail.imaps.port", "993");
props.setProperty("mail.imaps.connectiontimeout", "5000");
props.setProperty("mail.imaps.timeout", "5000");
Notices it's "imaps", not "imap", since the protocol you're using is imaps (IMAP + SSL)

In JavaMail, you can use imaps as the URL scheme to use IMAP over SSL. (See SSLNOTES.txt in your JavaMail distribution for more details.) For example, imaps://username%40gmail.com#imap.gmail.com/INBOX.
Similarly, use smtps to send emails via Gmail. e.g., smtps://username%40gmail.com#smtp.gmail.com/. Again, read SSLNOTES.txt for more details. Hope it helps!

You have to connect to GMail using SSL only. Setting the following properties will force that for you.
props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.setProperty("mail.imap.socketFactory.fallback", "false");

Here is what worked for my team and I, given a classic account nickname#gmail.com and a business account employee#business.com :
final Properties properties = new Properties();
properties.put("mail.imap.ssl.enable", "true");
imapSession = Session.getInstance(properties, null);
imapSession.setDebug(false);
imapStore = imapSession.getStore("imap");
imapStore.connect("imap.gmail.com", USERNAME, "password");
with USERNAME = "nickname" in the classic case, and USERNAME = "employee#business.com" in the business account case.
In the classic case, don't forget to lower the account security here : https://www.google.com/settings/security/lesssecureapps
In both cases check in GMail Settings => Forwarding POP / IMAP if IMAP is enabled for the account.
Hope it helps!
To go further :
http://www.oracle.com/technetwork/java/javamail/faq/index.html#gmail
https://support.google.com/mail/accounts/answer/78754

If you'd like more sample code on using JavaMail with Gmail (e.g. converting Gmail labels to IMAP folder names, or using IMAP IDLE), do check out my program GmailAssistant on SourceForge.

Check http://g4j.sourceforge.net/. There is a minimal gmail client built using this API.

I used following properties to get the store and It works well.
"mail.imaps.host" : "imap.gmail.com"
"mail.store.protocol" : "imaps"
"mail.imaps.port" : "993"

You need to have JSSE installed to use SSL with Java

URLName server = new URLName("imaps://<gmail-user-name>:<gmail-pass>#imap.gmail.com/INBOX");

Related

How to share the same email session between all instances of the application?

Maybe this question is already answered, but I couldn't find the proper answer.
I have a web application based in JSF, and I want to share the same email session between all the instances of the application, yet I haven't found how to do that.
My questions are:
a) What I am thinking is stupid? Should I just create a new session every time that I want to send a new mail?
b) If a is false, is there a proper way to do that?
Additional info: I'm working with PrimeFaces 4.0, Apache Tomcat 7.0.41, and JDK 7.
EDIT: I'm establishing an email connection like this (using sun's java mail)
Properties datos = new Properties();
datos.put("mail.smtp.host", "smtp.gmail.com");
datos.setProperty("mail.smtp.starttls.enable", "true");
datos.setProperty("mail.smtp.port", "587");
datos.setProperty("mail.smtp.user", usuarioAutenticacion);
datos.put("mail.smtp.timeout", 5000);
System.out.println(usuarioAutenticacion + " - " + contrasenaAutenticacion);
sesionCorreo = Session.getDefaultInstance(datos, null);
sesionCorreo.setDebug(true);
try {
conexionCorreo = sesionCorreo.getTransport("smtp");
} catch (NoSuchProviderException ex) {
Logger.getLogger(NotificacionesManager.class.getName()).log(Level.SEVERE, null, ex);
}
try {
conexionCorreo.connect(usuarioAutenticacion, contrasenaAutenticacion);
Then I proceed to send the messages in the Queue, but I'm looking for a way for just set that connection once then start sending the mails in the queue when necessary.
The way that the Java EE designers intended you to do this is that you configure your javax.mail.Session object in your server. This is described in the Tomcat 7 JavaMail Sessions documentation.
Your managed beans should then be able to access the session via #Resource:
class MyManagedBean {
#Resource(name="mail/Session") // this name is defined by your configuration
private Session mailSession;
public void someBusinessMethod() {
...
Message message = new MimeMessage(mailSession);
// compose message
...
Transport.send(message);
}
}
If you need to do this from a non-managed bean then you grab your Session instance using JNDI. This is described in the documentation linked above.

Pass hashed password to Java Mail API

Good morning everybody,
I'm developping an ERP for my company with the GWT Framework and I would get
the number of unread emails using the Java Mail API.
I can do this but, the problem is I stores the SHA-512 hashed password on the
database and I would not pass the clear password to the Java Mail API, but just the hashed password to avoiding to transmit the clear password on the network.
I use this code to get the number of unread mail:
private static int getNumberOfUnreadMails() {
int numberOfUnreadMails = 0;
Properties properties = new Properties();
properties.put("mail.imap.host", "myserver.com");
properties.put("mail.imap.user", "developper#myserver.com");
properties.put("mail.imap.socketFactory", 143);
properties.put("mail.imap.socketFactory.class", "java.net.ssl.SSLSocketFactory");
properties.put("mail.imap.port", 143);
Session session = Session.getDefaultInstance(properties, new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("developper#myserver.com", "mypassword");
}
});
Store store;
try {
store = session.getStore("imap");
store.connect();
Folder folder = store.getFolder("Inbox");
numberOfUnreadMails = folder.getUnreadMessageCount();
} catch (Exception e) {
e.printStackTrace();
}
return numberOfUnreadMails;
}
I can also use another hashing algorithm.
If you know a solution for my problem, thaks you in advance.
P.S.: Sorry for my poor English, I’m French.
Your IMAP-server will need the unhashed password to be able to authenticate. You probably are already using SSL (as you set mail.imap.socketFactory.class), so your password is never sent in the clear.
BTW: the correct way to use IMAP with SSL with javamail is to use the imaps protocol (and use the mail.imaps.*, not using the imap protocol and specifying an SSL socket factory as the socket factory. Also usually the IMAP with SSL port is 993, not 143 .

Trouble sending via gmail's SMTP server with Java

One of my customers is using Gmail for business (part of Google Apps) and I had to reconfigure the website I've developed so it would match the new credentials. After a bit of struggle due to TLS errors, I've managed to make the code work on localhost and on my test server (both Apache Tomcat 5.5). Everything was going smooth until I had to move it on his server (another Tomcat 5.5 as the hosting company told me). On the client's server I get the following error:
javax.mail.SendFailedException: Sending failed;
nested exception is:
class javax.mail.MessagingException: Could not connect to SMTP host: smtp.gmail.com, port: 465;
nested exception is:
java.io.IOException: Couldn't connect using "javax.net.ssl.SSLSocketFactory" socket factory to host, port: smtp.gmail.com, 465; Exception: java.lang.reflect.InvocationTargetException
The strange thing is that on localhost and the test server the port 465 works fine, and the guys from hosting said that port is opened on their server.
The code that connects to the mailserver is:
private void initConfigParams() throws CMSGeneralException {
try {
props = System.getProperties();
String smtpHost = Messages.getDBConfString("mail.smtp.host");
String mailPort = Messages.getDBConfString("mail.smtp.port");
String socketFallback = Messages.getDBConfString("mail.smtp.socketFactory.fallback");
String enableTls = Messages.getDBConfString("mail.smtp.starttls.enable");
String authSmtp = Messages.getDBConfString("mail.smtp.auth");
String tlsRequired = Messages.getDBConfString("mail.smtp.stattls.required");
String sktFactory = Messages.getDBConfString("mail.smtp.socketFactory.class");
props.put("mail.smtp.starttls.enable", enableTls);
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.auth", authSmtp);
props.put("mail.smtp.starttls.required", tlsRequired);
props.setProperty("mail.smtp.socketFactory.class", sktFactory);
props.setProperty("mail.smtp.socketFactory.fallback", socketFallback);
props.setProperty("mail.smtp.port", mailPort);
props.setProperty("mail.smtp.socketFactory.port", mailPort);
props.put("mail.transport.protocol", Messages.getDBConfString("mail.transport.protocol"));
Authenticator auth = null;
userName = Messages.getDBConfString("mail.username");
userPassword = Messages.getDBConfString("mail.userpassword");
if (!CMSUtils.isEmptyString(userName) && !CMSUtils.isEmptyString(userPassword)){
/* props.put("mail.smtp.auth", "true"); */
auth = new SMTPAuthenticator(userName, userPassword);
}
session = Session.getDefaultInstance(props, auth);
session.setDebug(false);
address = new InternetAddress[1];
address[0] = new InternetAddress(recipients);
mbText = new MimeBodyPart();
mbText.setContent(text, "text/html");
mp = new MimeMultipart();
mp.addBodyPart(mbText);
} catch (MessagingException e) {
e.printStackTrace();
throw new CMSGeneralException();
}
}
With the following .properties file
mail.smtp.starttls.enable=true
mail.smtp.host=smtp.gmail.com
mail.smtp.auth=true
mail.smtp.starttls.required=true
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
mail.smtp.socketFactory.fallback=false
mail.smtp.port=465
mail.transport.protocol=smtps
mail.username=website#example.com
mail.userpassword=password
mail.from=website#example.com
mail.to=mail#example.com
I tried the other ports for GMail, 587, but it doesn't work on any of the servers. Not even port 25 won't do the trick.
So, what am I doing wrong, and what should I do to make the mailing work?
Get rid of all the socket factory properties; if you're using a reasonably recent version of JavaMail you don't need them. See the JavaMail FAQ for how to configure JavaMail to access Gmail. You'll also find debugging tips there if it still doesn't work.
Also, change Session.getDefaultInstance to Session.getInstance.
And finally, if you're setting "mail.transport.protocol" to "smtps", you need to set the other properties as "mail.smtps." properties, not "mail.smtp." properties.
It seems to you have problems with java.lang.reflect.InvocationTargetException. So, I mean it's not a network problem. May be you've specified some parameters wrong and JavaMail API routes couldn't invoke a method. May be you should also specify property mail.smtp.ssl.socketFactory.
Some documentation here http://javamail.kenai.com/nonav/javadocs/com/sun/mail/smtp/package-summary.html .

Must issue a STARTTLS command first when trying to send a SimpleMailMessage in Grails

I try to send a message in a grails application. I use Java code with this problem tho, here is my code
SimpleMailMessage message_ref = new SimpleMailMessage();
JavaMailSenderImpl sender_ref = new JavaMailSenderImpl();
sender_ref.setHost("smtp.gmail.com")
sender_ref.setUsername("testAccount#googlemail.com")
sender_ref.setPassword("topsecret")
message_ref.setTo("testRecipient#gmx.de")
message_ref.setSubject("Hello there")
message_ref.setText("How are you")
sender_ref.send(message_ref)
I'm getting the following exception:
SMTPSendFailedException: 530 5.7.0 Must issue a STARTTLS command first
I found a similar problem here on stackoverflow here
Must issue a STARTTLS command first. Sending email with Java and Google Apps
but it didn't help me cause he used an different approach.
Can somebody tell me what's wrong? I'm expecting the error is not in the code but in some configuration file and this is where my knowledge edges.
Quoting from the Grails mail plugin docs:
grails {
mail {
host = "smtp.gmail.com"
port = 465
username = "youracount#gmail.com"
password = "yourpassword"
props = ["mail.smtp.auth":"true",
"mail.smtp.socketFactory.port":"465",
"mail.smtp.socketFactory.class":"javax.net.ssl.SSLSocketFactory",
"mail.smtp.socketFactory.fallback":"false"]
} }
I can't help you much, but your problem is basically that you need to use SSL to communicate with the server. Google does not allow plain-text communication for a lot of good reasons. I don't know much about grails, but I assume it has some sort of ssl-support. If it does not, you're probably better off doing it in javax.mail.
StartTLS is just a text-command you send to the smtp-server to explicitly start secure communications.
Properties properties = new Properties();
properties.put("mail.smtp.host", smtpHost);
properties.put("mail.smtp.port", smtpPort);
properties.put("mail.smtp.starttls.enable", "true");
properties.put("mail.user", userName);
properties.put("mail.password", password);

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