Currently I am using Commons Email to send email messages, but I have not been able to find a way to share smtp connections between emails sent. I have code like the following:
Email email = new SimpleEmail();
email.setFrom("example#example.com");
email.addTo("example#example.com");
email.setSubject("Hello Example");
email.setMsg("Hello Example");
email.setSmtpPort(25);
email.setHostName("localhost");
email.send();
Which is very readable, but is slow when I do a large amount of messages, which I believe is the overhead of reconnecting for each message. So I profiled it with the following code and have found that using the reusing the Transport makes things about three times faster.
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "smtp");
Session mailSession = Session.getDefaultInstance(props, null);
Transport transport = mailSession.getTransport("smtp");
transport.connect("localhost", 25, null, null);
MimeMessage message = new MimeMessage(mailSession);
message.setFrom(new InternetAddress("example#example.com"));
message.addRecipient(Message.RecipientType.TO, new InternetAddress("example#example.com"));
message.setSubject("Hello Example");
message.setContent("Hello Example", "text/html; charset=ISO-8859-1");
transport.sendMessage(message, message.getAllRecipients());
So I was wondering if there was a way to make Commons Email reuse an SMTP connection for multiple email sendings? I like the Commons Email API better, but the performance is kind of painful.
Thanks,
Ransom
I came up with the following solution after digging into the commons source itself. This should work, but there may be better solutions I do not know of
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "smtp");
Session mailSession = Session.getDefaultInstance(props, null);
Transport transport = mailSession.getTransport("smtp");
transport.connect("localhost", 25, null, null);
Email email = new SimpleEmail();
email.setFrom("example#example.com");
email.addTo("example#example.com");
email.setSubject("Hello Example");
email.setMsg("Hello Example");
email.setHostName("localhost"); // buildMimeMessage call below freaks out without this
// dug into the internals of commons email
// basically send() is buildMimeMessage() + Transport.send(message)
// so rather than using Transport, reuse the one that I already have
email.buildMimeMessage();
Message m = email.getMimeMessage();
transport.sendMessage(m, m.getAllRecipients());
Could we not achieve this easier by getting the mail session from the first email using getMailSession() and putting it to all subsequent messages using setMailSession() ?
Not 100% sure what the
Please note that passing a username and password (in the case of mail authentication) will create a new mail session with a DefaultAuthenticator. This is a convience but might come unexpected. If mail authentication is used but NO username and password is supplied the implementation assumes that you have set a authenticator and will use the existing mail session (as expected).
from the javadoc means, though :-/
http://commons.apache.org/email/api-release/org/apache/commons/mail/Email.html#setMailSession%28javax.mail.Session%29
see also:
https://issues.apache.org/jira/browse/EMAIL-96
not sure how to continue here...
Related
I wrote a simple Java program that uses Java Mail API to send an email.
public static void main(String[] args) {
System.out.println("SimpleEmail Start");
String smtpHostServer = "smtp.gmail.com";
String emailID = "xxxxxx#hotmail.com";
Properties props = System.getProperties();
props.put("mail.smtp.host", smtpHostServer);
props.put("mail.smtp.starttls.enable", "true");
Session session = Session.getInstance(props, null);
EmailUtil.sendEmail(session, emailID,"SimpleEmail Testing Subject", "SimpleEmail Testing Body");
}
}
EmailUtil class:
public class EmailUtil {
/**
* Utility method to send simple HTML email
* #param session
* #param toEmail
* #param subject
* #param body
*/
public static void sendEmail(Session session, String toEmail, String subject, String body){
try
{
MimeMessage msg = new MimeMessage(session);
//set message headers
msg.addHeader("Content-type", "text/HTML; charset=UTF-8");
msg.addHeader("format", "flowed");
msg.addHeader("Content-Transfer-Encoding", "8bit");
msg.setFrom(new InternetAddress("no_reply#example.com", "NoReply-JD"));
msg.setReplyTo(InternetAddress.parse("no_reply#example.com", false));
msg.setSubject(subject, "UTF-8");
msg.setText(body, "UTF-8");
msg.setSentDate(new Date());
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail, false));
System.out.println("Message is ready");
Transport.send(msg);
System.out.println("EMail Sent Successfully!!");
}
catch (Exception e) {
e.printStackTrace();
}
}
}
With this implementation, it is said I do not need any password at all so i gave this a tried and it tells me:
com.sun.mail.smtp.SMTPSendFailedException: 530-5.5.1 Authentication Required
I got this code from an online resources and from what I read,it should be able to send without authentication.
So my question is, do I always need to set a username and password to send mail using JAVA mail?
If no, what am I doing wrong?
It depends on who's running the server.
See https://support.google.com/a/answer/176600?hl=en&vid=0-974788924023-1554173451081; specifically the column labeled 'Gmail SMTP server' (which you're using). Google explicitly says that you have to authenticate to utilize that server. Not doing so gives you this error message.
Once upon a time you could use any mail server to send emails to any email address you wanted.
And then came SPAM. Spammers also could use any mail server to send emails to any email address - and as a mail server operator you do not want spammers to use your server for sending emails (because operating the server costs you money, because your mail server can get blacklisted for spamming).
So today most mail servers require that you
either provide authentication (for sending emails to any email address you want)
or are only allowed to send emails to email addresses hosted by the mail server operator
Google even has two distinct mail servers:
one that requires authentication, that can be used for sending emails to any email address you want (that is the server at smtp.gmail.com)
one that doesn't require authentication, that can only be used for sending emails to Gmail or G Suite users (that is the server aspmx.l.google.com)
It could be that your source dates back to a time when no authentication was required or that the mail server in your source was only used for sending mails to addresses that are hosted at the mail server.
Either way - if you want to use the server smtp.gmail.com for sending mails to any address you must authenticate (or convince google that they should allow you - and only you - to sending emails without authentication, but then: how will google know that it is exactly you who is trying to send mails?)
i'm developping a web application in java. During develop of email sender i have to specify credentials (email and password) of my host.
i was doing something like this:
public void sendEmail(String toAddress,
String subject, String message) throws AddressException,
MessagingException {
// sets SMTP server properties
Properties properties = new Properties();
properties.put("mail.smtp.host", smtp.gmail.com);
properties.put("mail.smtp.port", 587);
properties.put("mail.smtp.auth", "true");
properties.put("mail.smtp.starttls.enable", "true");
// Sender's email ID needs to be mentioned
final String username = "user_mail";
final String password = "password_mail";
// creates a new session with an authenticator
Authenticator auth = new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
};
Session session = Session.getInstance(properties, auth);
// creates a new e-mail message
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(username));
InternetAddress[] toAddresses = { new InternetAddress(toAddress) };
msg.setRecipients(Message.RecipientType.TO, toAddresses);
msg.setSubject(subject);
msg.setSentDate(new Date());
// set plain text message
msg.setText(message);
// sends the e-mail
Transport.send(msg);
}
i think that the better practice to use credentials is to save them in an xml file, and calling them back using some method, maybe Context's method.
Can someone explain me (or give me some link where i can see) how can i do that or some better?
any help will be appreciated.
One interesting way to do it is to use Vault. It's a system for managing "secrets" like credentials, API keys, etc.
There are other things you can do like write some code to automatically encrypt and decrypt the password.
As a rule of thumb, it's also important to make sure that you don't install the JDK on a production machine. If you do, a hacker would get access to javap which is the Java disassembler. The output wouldn't be that good, but probably enough to get a ballpark idea of the method you are using to secure the password.
It depends how much effort you want to place in the hiding the secrets. At the end - you have to store the credentials somewhere. You can encrypt them, but you have to store the encryption key somewhere anyway.
Many (even mature) systems simply store the username / password as plains in a configuration file and rely on the hosting to make the system secure. Some systems encrypt the passwords, but the encryption key is available anyway somewhere in a confguration and keystore. It makes retrieving your email password a little bit more complex, but not much.
The most secure way I've seen was creating a key based on a configured secret (password) and some environment property (IP, MAC, ...).
So - lets be practical. Storing your SYSTEM credentials as plains in most of the cases may be ok. You have to secure access to your system so there should be no way to get the configuration file.
I am using the scopes: mail.send, mail.readwrite, mail.read, offline_access, openid, email, and profile (although I am fairly confident I do not need all of these -> goal is to read inbox and send emails, while also getting email and name if they exist).
I am then connecting to SMTP server with the following code:
OAuth2Authenticator.connectToSmtp("smtp-mail.outlook.com",
587,
user.getOutlookUid(),
accessToken,
true);
The code that actually connects to the server is here:
public static SMTPTransport connectToSmtp(String host, int port, String userEmail, String oauthToken, boolean debug)
throws Exception {
Properties props = new Properties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.sasl.enable", "true");
props.put("mail.smtp.sasl.mechanisms", "XOAUTH2");
props.put("mail.smtp.sasl.mechanisms.oauth2.oauthToken", oauthToken);
Session session = Session.getInstance(props);
session.setDebug(debug);
URLName unusedUrlName = null;
SMTPTransport transport = new SMTPTransport(session, unusedUrlName);
// If the password is non-null, SMTP tries to do AUTH LOGIN.
String password = "";
transport.connect(host, port, userEmail, password);
return transport;
}
Okay, now I can get to the most frustrating part... I have used the "connectToSMTP" method to connect to Gmail and it worked perfectly.
OAuth2Authenticator.connectToSmtp("smtp.gmail.com",
587,
user.getGoogleUid(),
accessToken,
true);
So ultimately my question is "what am I doing wrong?" or "what can I update to be able to send emails through Outlook"? I have seen that Outlook has a REST API, but that is plan B. Is there something different about Outlook vs Gmail?
Some things I have considered:
Scope did not request enough access (so I probably am asking for too much now)
access_token was stored incorrectly or encoded in some way (tried decoding it from base_64 which provided nothing). I am able to use my refresh_token to update the access_token so that tells me I am probably storing them correctly.
I tried passing null for the password. Also passed in the actual password and that WORKED, but I have the access_token and refresh_token so I shouldn't need to ask for their explicit password. Also this would be dangerous and sketchy to ask of users.
I tried manually connecting to the smtp server using "openssl s_client -crlf -starttls smtp -connect smtp-mail.outlook.com:587", but it seemed to think my access_token was wrong "535 5.0.0 OAuth failed: OAuth authentication failed due to Invalid token. Code -2147184118" That number when taken two's complement and converted to hex is 0x8004920a. Helped in searches but was to no avail.
I have done a lot of searching for this and will continue now to post this everywhere. A lot of resources for it working with Gmail, but as previously stated I already have it working for Gmail. Something seems different for Outlook. Also I have encountered lots of posts regarding email forwarding on an email client... I am semi-creating an email client so going through outlook.com settings doesn't help me.
Another concern that a buddy of mine had was that my access token was really long, contributing to what the manual smtp server claimed. It is 1188 characters long. It's something like 'EwB4Aul3BAAUo4xeBIbHjhBxWOFekj4Xy2...x9stHxi2K/VFggE=' (obviously I hid most of the characters).
Preemptive THANK YOU for anyone who offers advice or finds my issue. Especially why I can pass in the email password and that fails, but using the oauth access_token fails.
Try using "pop3://user:password#host:port/INBOX". to retrieve email from the inbox ,
more information can be found out https://javamail.java.net/docs/api/com/sun/mail/pop3/package-summary.html
hope this helps : https://technet.microsoft.com/en-ca/dn44016. Properties props = new Properties();
props.put("mail.imap.ssl.enable", "true"); // required for Gmail
props.put("mail.imap.auth.mechanisms", "XOAUTH2");
Session session = Session.getInstance(props);
Store store = session.getStore("imap");
store.connect("imap.gmail.com", username, oauth2_access_token);
I've a button on my application that has to check if inputted data is correct, adds it to a database and sends an email, it's represented by the following code:
<%# page import = "java.text.*" %>
<%# page import="java.io.*,java.util.*,javax.mail.*, javax.mail.Service"%>
<%# page import="javax.mail.internet.*,javax.activation.*"%>
<%# page import="javax.servlet.http.*,javax.servlet.*" %>
[...]
<a href="javascript:void();" onclick="
javascript:if(confirmaEnviar())$('#usuario').submit();
<% String result;
// Recipient's email ID needs to be mentioned.
String host = "smtp.gmail.com";
String to = "existingdirection#gmail.com";
String from = "existingdirection2#gmail.com";
String subject = "test";
String messageText = "body test";
Properties props = System.getProperties();
props.put("mail.host", host);
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.port", "25");
// If using authentication, otherwise comment out
props.put("mail.smtp.auth", "true");
// Gmail requires TLS, your server may not
props.put("mail.smtp.starttls.enable", "true");
Session mailSession = Session.getDefaultInstance(props, null);
Message msg = new MimeMessage(mailSession);
msg.setFrom(new InternetAddress(from));
InternetAddress[] address = {new InternetAddress(to)};
msg.setRecipients(Message.RecipientType.TO, address);
msg.setSubject(subject);
msg.setSentDate(new Date());
msg.setText(messageText);
Transport transport = mailSession.getTransport("smtp");
//connect with authentication
transport.connect(host,"existingdirection2#gmail.com" , "password");
transport.sendMessage(msg, address);
transport.close();
%>
return false;" class="button button-alt">ENVIAR</a></div>
But if I upload it and try to execute it I get an error in the browser: Error: Server Error
The server encountered an error and could not complete your request.
I had thought that it might be that jsp code couldn't be called that way (in the middle of an onclick) but if I put it into another place of the code (in the middle of html tags) the error still reproduces.
I've added the library for java mail to the java build path and I've also copied it to the lib directory.
Of course it has been tested that what fails is the email related code, if I delete it from the application the rest goes nicely.
Email directions of passwords have also been checked to be correct.
I'm using GAE to host the application.
Any idea of what might be happening?
I'm grateful for your help.
Edit: That's the stack trace, guess it's not really a code related question after all..
com.google.apphosting.api.ApiProxy$FeatureNotEnabledException: The Socket API will be enabled for this application once billing has been enabled in the admin console. at java.lang.Thread.getStackTrace(Thread.java:1567) at com.google.apphosting.runtime.ApiProxyImpl.doSyncCall(ApiProxyImpl.java:259) at com.google.apphosting.runtime.ApiProxyImpl.access$000(ApiProxyImpl.java:68) at com.google.apphosting.runtime.ApiProxyImpl$1.run(ApiProxyImpl.java:202) at com.google.apphosting.runtime.ApiProxyImpl$1.run(ApiProxyImpl.java:199) at java.security.AccessController.doPrivileged(Native Method) at com.google.apphosting.runtime.ApiProxyImpl.makeSyncCall(ApiProxyImpl.java:199) at com.google.apphosting.runtime.ApiProxyImpl.makeSyncCall(ApiProxyImpl.java:68) at com.google.apphosting.api.ApiProxy.makeSyncCall(ApiProxy.java:107) at com.google.apphosting.api.ApiProxy.makeSyncCall(ApiProxy.java:56) at com.google.appengine.api.socket.SocketApiHelper.apiProxyMakeSyncCall(SocketApiHelper.java:90) at com.google.appengine.api.socket.SocketApiHelper.makeSyncCall(SocketApiHelper.java:58) at com.google.appengine.api.socket.NameServiceImpl.lookupAllHostAddr(NameServiceImpl.java:61)
Strnage thing is that there's no API called Socket API in Google Api's console, guess I'll also try to contact a google consultor also.
GAE comes with it's own SMTP server, so you do not need to set your own SMTP parameters: https://developers.google.com/appengine/docs/java/mail/usingjavamail
However, if you do set your own SMTP parameters then GAE will use sockets which have this limitations. These apply in your case:
Sockets are available only for paid apps - this is the one producing Error.
Port 25 (SMTP) is blocked
Google IP ranges are blocked, except Gmail SMTPS: smtp.gmail.com port 465 and 587
I am trying to send email in HTML format. It is working for text/plain. But when i set content type to text/html mail is not being transported(no exception is being thrown but i don't get email as well).
Following is my code.
public void postMail() throws Exception {
boolean debug = false;
Properties props = new Properties();
props.put("mail.smtp.host", smtpServer);
props.put("mail.smtp.port", smtpServerPort);
props.put("mail.smtp.auth", "false");
props.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback","false");
lgr.debug(lgr.isDebugEnabled()?"SMTP Server --->" + smtpServer : null);
Session session = Session.getInstance(props, null);
session.setDebug(debug);
Message msg = new MimeMessage(session);
InternetAddress addressFrom = new InternetAddress(from);
msg.setFrom(addressFrom);
// Here is some logic to add TO and CC and BCC
msg.setSubject(subject);
// writer.println("Subject : " + subject);
lgr.debug(lgr.isDebugEnabled()?"Subject : " + subject : null);
msg.setContent(message, "text/html");
Transport.send(msg);
lgr.info(lgr.isInfoEnabled() ?"Mail sent": null);
}
Any help in this regard will be highly appreciated.
Get rid of the socket factory stuff, you don't need it.
When you set debug to true, do you see any errors in the debug output? Or is your mail server accepting the message without complaint? If no errors, then most likely the problem is that the recipient's mail server thinks your message is spam, although that seems unlikely with your simple test message.
Have you tried sending to different recipients, especially different recipients using different mail servers? It would be useful to figure out if the problem is with the recipient's mail server or with the mail server you're using for sending.
I faced a similar issue some times back. I found that the issue was with the html content containing single quotes.
As a test try to send a (simple word)/(simple html without quotes at all) as message using text/html format. If that works you can probably modify your html message to contain only double quotes.
Hope that helps!