What is the use of javax.mail.Session? - java

I was fixing a class responsible for sending emails. It looked like this (simplified):
/* ... */
Properties props = System.getProperties();
props.put("mail.smtp.host", A_VALID_IP_OF_MAIL_SERVER);
Session session = Session.getDefaultInstance(props, null);
try {
Message msg = new MimeMessage(session);
/* msg.setFrom(); msg.addRecipient(); etc. */
Transport.send(msg);
System.out.println("Sent!");
}
catch (Exception e) { /* ... */ }
/* ... */
During my work I set session to null and to my utter surprise the class still worked fine. It doesn't matter if I pass null to MimeMessage constructor. It doesn't throw an exception or anything. Moreover, the Transport.send() method includes the following lines:
240 Session s = (msg.session != null) ? msg.session :
241 Session.getDefaultInstance(System.getProperties(), null);
So if the session is null it just creates a new one using system properties. What is then the purpose of creating a Session object at all? Why doesn't MimeMessage have a default constructor if it doesn't matter what you pass in there?
I viewed a number of examples of using javax.mail, such as: example from Google and
example from tutorialspoint and they both create a Session object which seems pretty useless. Why would anyone do that?

What is then the purpose of creating a Session object at all?
The session is the context of how you are going to interact with the mail host. This includes but not limited to debugging output from the mail host, timeouts, and authentication mechanisms. If you want to interact with the same mail host in different ways then the session is the object that holds this information.
If a single JVM needs to connect to multiple mail servers you need two different sessions. This is explained in detail in the JavaMail FAQ:
If some other code in the same JVM (e.g., in the same app server) has already created the default Session with their properties, you may end up using their Session and your properties will be ignored. This often explains why your property settings seem to be ignored. Always use Session.getInstance to avoid this problem.
Most JavaMail examples fail the common mistakes test. Try to reference the JavaMail API sample programs Session.getDefaultInstance is rarely the right choice for any code. Most code should use Session.getInstance. Including a default constructor for MimeMessage would just encourage wrong behavior.

Related

Using Meek Transport

I am trying to integrate Meek in an Android application. There is a sample here that shows how to instantiate the transport:
https://github.com/guardianproject/AndroidPluggableTransports/blob/master/sample/src/main/java/info/pluggeabletransports/sample/SampleClientActivity.java
The question is what do I do from there. Assuming I have an application that uses OkHttp3. Is there a way to reconcile both and use OkHttp3 as underlying transport mechanism while the app only interacts with Okhttp3?
I am also quite conflicted on how to instantiate the transport and what each option means. In the link provided above, the transport is instantiate as follows:
private void initMeekTransport() {
new MeekTransport().register();
Properties options = new Properties();
String remoteAddress = "185.152.65.180:9001";// a public Tor guard to test
options.put(MeekTransport.OPTION_URL,"https://meek.azureedge.net/"); //the public Tor Meek endpoint
options.put(MeekTransport.OPTION_FRONT, "ajax.aspnetcdn.com"); //the domain fronting address to use
options.put(MeekTransport.OPTION_KEY, "97700DFE9F483596DDA6264C4D7DF7641E1E39CE"); //the key that is needed for this endpoint
init(DispatchConstants.PT_TRANSPORTS_MEEK, remoteAddress, options);
}
However, in the README (https://github.com/guardianproject/AndroidPluggableTransports), the suggested approach is:
Properties options = new Properties();
String bridgeAddress = "https://meek.actualdomain.com";
options.put(MeekTransport.OPTION_FRONT,"www.somefrontabledomain.com");
options.put(MeekTransport.OPTION_KEY,"18800CFE9F483596DDA6264C4D7DF7331E1E39CE");
init("meek", bridgeAddress, options);
Transport transport = Dispatcher.get().getTransport(this, PT_TRANSPORTS_MEEK, options);
if (transport != null)
{
Connection conn = transport.connect(bridgeAddress);
//now use the connection, either as a proxy, or to read and write bytes directly
if (conn.getLocalAddress() != null && conn.getLocalPort() != -1)
setSocksProxy (conn.getLocalAddress(), conn.getLocalPort());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write("GET https://somewebsite.org/TheProject.html HTTP/1.0".getBytes());
conn.write(baos.toByteArray());
byte[] buffer = new byte[1024*64];
int read = conn.read(buffer,0,buffer.length);
String response = new String(buffer);
}
In the latter approach, the bridge address is passed to the init() method. In the first approach it is the remote address that is passed to the init() method while the bridge address is passed as option. Which one of these approaches is the right one?
Furthermore, I would appreciate some comments on OPTION_URL, OPTION_FRONT, and OPTION_KEY. Where does each of these pieces of information come from? If I do not want to use these default information, how do i go about setting up a "Meek endpoint" for instance to not use the default Tor one? Anything particular needs to be done on the CDN? How would I use Amazon or Google instead of aspnetcdn?
As you can see, I am fairly confused.
Thanks for your interest in this library. Unfortunately, we haven't made a formal release yet, and it is still quite early in its development.
However, we think that we can help still.
First, it should be made clear that the major cloud providers (Google, Amazon, Azure) that support domain fronting, are now very much against it. Thus, you might find some limitation in using any of those platforms, especially with a fronted domain that you do not control.
You can read about Tor and Signal's own struggle with this here: https://signal.org/blog/looking-back-on-the-front/ and https://blog.torproject.org/domain-fronting-critical-open-web
Now, that said, if you still want to proceed with domain fronting, and all you are using is HTTP/S, then you can do so, without using Meek at all. You simple need to set the "Host" header on your request to the actual domain you want to access, while the request itself using the fronted domain. It is actually quite simple.
You can read more here: https://www.bamsoftware.com/papers/fronting/#sec:introduction

Java Mail: How to Recognize New Mails

When I receive mails, open the inbox, and get the messages; how can I compare them to a previous array of messages, so that I know which of them I have already read?
Properties properties = new Properties();
properties.put("mail.imaps.host", host);
properties.put("mail.imaps.port", "993");
Session emailSession = Session.getDefaultInstance(properties);
//2) create the POP3 store object and connect with the pop server
Store emailStore = emailSession.getStore("imaps");
emailStore.connect(user,pw);
//3) create the folder object and open it
Folder emailFolder = emailStore.getFolder("INBOX");
emailFolder.open(Folder.READ_ONLY);
Message[] messages = emailFolder.getMessages();
How to handle this depends on exactly what your requirements are. For example, will other applications and/or users be accessing the mailbox at the same time as your application? Or does your application have dedicated access to the mailbox?
You can use the SEEN flag to find messages that you haven't already read.
You can also save the IMAP UID for the last message you processed (and the UIDVALIDITY for the folder) to later find messages with larger UIDs. See the UIDFolder interface for details.
(The comments in your code talk about pop, but your code is using imap; if you ever have to use the pop3 protocol the answers are quite different.)

Remote access to Message Queue over SSL in GlassFish

I'm trying to access a Message Queue configured in GlassFish but have been facing a lot of problems. The first one is regarding the Trust Store password on the client side. I can only make it work if I use the default password (changeit), I'm always getting
Keystore was tampered with, or password was incorrect
if I try to use a different password. And yes, I'm pretty sure I'm using the correct password.
In fact, if I set the Trust Store with the default password and use the following property with a wrong value, it still works:
-Djavax.net.ssl.trustStorePassword=<wrong value>
Which leads me to conclude that it completely ignores this setting.
This is what I've so far:
System.setProperty("com.sun.CSIV2.ssl.standalone.client.required", "true");
System.setProperty("javax.net.ssl.trustStore", "<my path>/truststore.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "<my password>");
Properties properties = new Properties();
System.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");
System.setProperty("org.omg.CORBA.ORBInitialPort", "3820");
InitialContext ctx = new InitialContext(properties);
com.sun.messaging.ConnectionFactory connectionFactory = new com.sun.messaging.ConnectionFactory();
connectionFactory.setProperty(ConnectionConfiguration.imqAddressList, "127.0.0.1:7676");
connectionFactory.setProperty(ConnectionConfiguration.imqDefaultUsername, "<app user>");
connectionFactory.setProperty(ConnectionConfiguration.imqDefaultPassword, "<app user password>");
QueueConnection queueConnection = connectionFactory.createQueueConnection();
queueConnection.start();
So, my first question is: How can I use the non-default password on my Trust Store? Do I have to use some kind of encoding/hashing on the value?
My second question is regarding authentication. The standard way of getting the Connection Factory (via JNDI) and authenticate (via createQueueConnection(user, pass)) does not seem to be working, it always tries to use the guest account. Is there a way to use the standard approach on GlassFish or am I limited to use proprietary code?
EDIT #1
After digging a little deeper, I've detected a behavior that seems a bug.
Looking at the stacktrace, one of the first methods being invoked is initJKS() from com.sun.enterprise.security.ssl.impl.SecuritySupportImpl. With the help of javassist, I realized that the following section is being invoked:
if (masterPasswordHelper == null && Globals.getDefaultHabitat() != null) {
masterPasswordHelper = Globals.getDefaultHabitat().getByType(MasterPasswordImpl.class);
}
if (masterPasswordHelper instanceof MasterPasswordImpl) {
keyStorePass = masterPasswordHelper.getMasterPassword();
trustStorePass = keyStorePass;
}
Causing the bypass of the next set of instructions:
if (keyStorePass == null) {
keyStorePass = System.getProperty(KEYSTORE_PASS_PROP, DEFAULT_KEYSTORE_PASS).toCharArray();
trustStorePass = System.getProperty(TRUSTSTORE_PASS_PROP, DEFAULT_TRUSTSTORE_PASS).toCharArray();}
And, as a consequence, to ignore the property javax.net.ssl.trustStorePassword. Why is Globals.getDefaultHabitat() returning a non null value?

SMTP/POP3 through proxy System.getProperties() vs new Properties()?

Im trying to get mail from a POP3 server through a proxy. Most "tutorials" suggest doing something like
Properties p = System.getProperties();
p.setProperty("proxySet", "true");//does this line even do anything?
p.setProperty("socksProxyHost", proxyHost);
p.setPorperty("socksProxyPort", proxyPort);
p.setProperty("socksProxyVersion", "5");//or 4 if you want to use 4
p.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
p.setProperty("mail.pop3.socketFactory.fallback", "false");//also not sure what it does
p.setProperty("mail.pop3.port", portOnHostYouWantToTalkTo);
p.setProperty("mail.pop3.socketFactory.port", portOnHostYouWantToTalkTo);
Session session = Session.getDefaultInstance(p, null);
//or session = Session.getInstance(p, null);
URLName urlName = new URLName(protocol, hostYouwantToTalkTo, portOnHostYouWantToTalkTo, null, mailbox, mailboxPassword);
Store store = session.getStore(urlName);
Now, if I do something like this I get an exception:
java.net.SocketException: Can't connect to SOCKS proxy:Connection timed out: connect.
My POP3 server does not log any connections, suggesting there is a proxy issue or an error in my code. I am using 73.29.157.190:29099 for now.
2) If, however, I do
Properties p = new Properties();
//all the same logic and stuff
Session = Session.getInstance(p, null);
My POP3 server logs a connection from localhost, and works properly, suggesting that I am NOT using a proxy to connect to it and everything else is fine.
My question is, why do "tutorials" use System.getProperties() and pass it to getInstance()? Every Session instance will keep a reference to System.properties. So, effectively every Session instance will be affected every time you try to create a new one or alter System.getProperties() in any way so you might as well reuse the same one.
Does javamail need something set in System.properties specifically and not the ones passed to Session?
Also, what parameters do you need to set in order to get javamail to use a proxy? What does System.properties have that makes it work unlike my new Properties? A link to a good tutorial or documentation that explains it would be greatly appreciated.
Thanks!
First, get rid of all the socket factory stuff, you don't need it.
Next, make sure you really have a SOCKS proxy and not just a web proxy. If you do, see this JavaMail FAQ entry.
Setting the System properties for a SOCKS proxy will cause all network connections from your program to go through the proxy server, which may not be what you want.

Gmail SMTP Suitable For Production Messaging

I have some automated emailing tasks set up in my application. That is every day I send application specific email to customers to remind them of appointments etc. Is using Gmail's smtp suitable for production tasks beyond just a simple message here any there? Is there any benefit to implementing my own smtp server such as Apache James?
I agree with #Richthofen - Using gmail to send emails in a production environment is a bad (and unethical) idea; Amazon SES or Sendgrid are the best solutions here. If you want to run your own SMTP server then please keep in mind that it will share resources with your application and will probably slow it down.
However I use gmail to test development/testing environments using javamail API. Here's the code:
public class EmailSender{
public void send(){
//javamail code
Session mailSession = createSmtpSession();
//javamail code
}
private Session createSmtpSession() {
final Properties props = new Properties();
props.setProperty ("mail.host", "smtp.gmail.com");
props.setProperty("mail.smtp.auth", "true");
props.setProperty("mail.smtp.port", "" + 587);
props.setProperty("mail.smtp.starttls.enable", "true");
props.setProperty ("mail.transport.protocol", "smtp");
// props.setProperty("mail.debug", "true");
return Session.getDefaultInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
"<gmail ID in user#domain format goes here>",
"<password goes here>");
}
});
}
}
Gmail TOS prohibits unsolicited commercial emails. In general I wouldn't deploy any service that relies on Gmail to the public. I think Gmail caps an email recipient list to 100 anyways so it would probably fail if you tried to send the same message to more than 100 people.
Usually you want your own IP for outgoing mail for reputation reasons. For most of my clients when I do freelance work I recommend affordable partner services like http://sendgrid.com/ ... Having your own IP means that you can manage your reputation as a bulk email sender legitimately. And you won't have to worry about Gmail shutting you down for breaking the TOS. Gmail also won't give you metrics about deliverability so you won't have any idea if you're being successful in sending these.
Having worked for a major email marketer, I can tell you that just sending a message to an SMTP server is not enough these days. All major mail service providers do things like require sender identification keys for bulk mail. They also meter messages and flag senders who end up submitting too many messages in a specific amount of time. If you want your mail delivered and not in the SPAM folder you need to do either a lot of work and spin up a dedicated server w/ a dedicated IP, or you should use a vendor who can do some of that work for you.

Categories