Java tutorial
/* * Copyright (c) 2008-2012 Tomas Varaneckas * http://www.varaneckas.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.googlecode.gmail4j.javamail; import com.googlecode.gmail4j.GmailConnection; import com.googlecode.gmail4j.GmailException; import com.googlecode.gmail4j.auth.Credentials; import com.googlecode.gmail4j.http.HttpProxyAwareSslSocketFactory; import com.googlecode.gmail4j.http.ProxyAware; import com.googlecode.gmail4j.util.Constants; import com.sun.mail.imap.IMAPSSLStore; import com.sun.mail.imap.IMAPStore; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.mail.*; import javax.net.SocketFactory; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Proxy.Type; import java.security.Security; import java.util.Properties; /** * JavaMail based IMAP {@link GmailConnection} implementation. * <p> * ImapGmailConnection is {@link ProxyAware}, allowing to use JavaMail IMAP * via HTTP proxy, which was not possible by JavaMail design. * <p> * Example: * <p><blockquote><pre> * GmailConnection conn = new ImapGmailConnection(); * conn.setLoginCredentials(new Credentials("user", "pass".toCharArray())); * //if proxy is required * ((ProxyAware) conn).setProxy("proxy.example.com", 8080); * //if proxy auth is required * ((ProxyAware) conn).setProxyCredentials("proxyuser", * "proxypass".toCharArray()); * GmailClient client = new ImapGmailClient(); * client.setConnection(conn); * </pre></blockquote></p> * * @author Tomas Varaneckas <tomas.varaneckas@gmail.com> * @since 0.3 */ public class ImapGmailConnection extends GmailConnection implements ProxyAware { private static final Log log = LogFactory.getLog(ImapGmailConnection.class); /** * Gmail IMAP host. Should not be set unless Gmail server has moved. * * @see #getGmailImapHost() * @see #setGmailImapHost(String) */ private String gmailImapHost = "imap.gmail.com"; /** * Gmail IMAP port for mail receiving. Should not be set unless Gmail server * has moved. * * @see #getGmailImapPort() * @see #setGmailImapPort(int) */ private int gmailImapPort = 993; /** * Gmail SMTP host for mail sending. Should not be set unless Gmail server * has moved. * * @see #getGmailSmtpHost() * @see #setGmailSmtpHost(String) */ private String gmailSmtpHost = "smtp.gmail.com"; /** * Gmail SMTP port for mail sending. Should not be set unless Gmail server * has moved. * * @see #getGmailSmtpPort() */ private int gmailSmtpPort = 465; /** * Proxy Credentials */ private Credentials proxyCredentials; /** * JavaMail configuration {@link Properties}. Derrived from {@link System} * properties. * * @see System#getProperties() * @see System#setProperty(String, String) */ private Properties properties; /** * Proxy in use */ private Proxy proxy; /** * JavaMail {@link Session} * * @see #getSession() */ private Session mailSession; /** * JavaMail {@link Store} */ private Store store; /** * Contain the state of current connection {@link Store}. * * @see #isConnected() * @see #setConnected(boolean) */ private boolean connected = false; /** * Debug level for IMAP connection. */ private boolean debug; /** * Argless constructor. */ public ImapGmailConnection() { Security.addProvider(new OAuth2Provider()); } /** * Constructor with Gmail username and password * * @param username Gmail username (can be full email) * @param password Gmail password */ public ImapGmailConnection(final String username, final char[] password) { this(new Credentials(username, password)); } /** * Constructor with Gmail {@link Credentials} * * @param loginCredentials Gmail login credentials */ public ImapGmailConnection(final Credentials loginCredentials) { this(); this.loginCredentials = loginCredentials; } public static IMAPStore connectToImap(String host, int port, String userEmail, String oauthToken, boolean debug) throws Exception { Properties props = new Properties(); props.put("mail.imaps.sasl.enable", "true"); props.put("mail.imaps.sasl.mechanisms", "XOAUTH2"); props.put(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oauthToken); Session session = Session.getInstance(props); session.setDebug(debug); final URLName unusedUrlName = null; IMAPSSLStore store = new IMAPSSLStore(session, unusedUrlName); final String emptyPassword = ""; store.connect(host, port, userEmail, emptyPassword); return store; } /** * Gets {@link #gmailImapHost} * * @return Gmail IMAP host */ public String getGmailImapHost() { return gmailImapHost; } /** * Sets {@link #gmailImapHost}. * <p/> * You don't have to set Gmail IMAP host, unless it differs from the * predefined value (imap.gmail.com). * * @param gmailImapHost Gmail IMAP host */ public void setGmailImapHost(final String gmailImapHost) { this.gmailImapHost = gmailImapHost; } /** * Gets {@link #gmailSmtpHost} * * @return Gmail SMTP host */ public String getGmailSmtpHost() { return gmailSmtpHost; } /** * Sets {@link #gmailSmtpHost}. * <p/> * You don't have to set Gmail SMTP host, unless it differs from the * predefined value (smtp.gmail.com). * * @param gmailSmtpHost Gmail SMTP host */ public void setGmailSmtpHost(final String gmailSmtpHost) { this.gmailSmtpHost = gmailSmtpHost; } /** * Gets {@link #gmailSmtpPort} * * @return Gmail SMTP port */ public int getGmailSmtpPort() { return gmailSmtpPort; } /** * Sets {@link #gmailSmtpPort}. * <p/> * You don't have to set Gmail SMTP port, unless it differs from the * predefined value (465). * * @param gmailSmtpPort Gmail SMTP port */ public void setGmailSmtpPort(final int gmailSmtpPort) { this.gmailSmtpPort = gmailSmtpPort; } /** * Gets {@link #gmailImapPort} * * @return Gmail IMAP port */ public int getGmailImapPort() { return gmailImapPort; } /** * Sets {@link #gmailImapPort}. * <p/> * You don't have to set Gmail IMAP port, unless it differs from the * predefined value (993). * * @param gmailImapPort Gmail IMAP port */ public void setGmailImapPort(final int gmailImapPort) { this.gmailImapPort = gmailImapPort; } /** * Opens Gmail {@link Store} * * @return singleton instance of Gmail {@link Store} */ public Store openGmailStore() { try { // if stort object instance is not null and the service connection state // is connected then close this service and terminate its connection. // Note : due to security concerns gmail imap only allow max 10 // connections because of this reason any existing connection that // is open should terminate. if (isConnected()) { disconnect(); } store = connectToImap(gmailImapHost, gmailImapPort, loginCredentials.getUsername(), loginCredentials.getOauthToken(), debug); setConnected(store.isConnected()); } catch (final Exception e) { throw new GmailException("Failed opening Gmail IMAP store", e); } return store; } /** * Gets Gmail {@link Transport} * * @return Configured and ready for use Transport */ public Transport getTransport() { try { final Transport transport = getSession().getTransport(); transport.addConnectionListener(new ImapConnectionHandler( new ConnectionInfo(loginCredentials.getUsername(), gmailSmtpHost, gmailSmtpPort))); transport.connect(loginCredentials.getUsername(), new String(loginCredentials.getPasword())); return transport; } catch (final Exception e) { throw new GmailException("ImapGmailClient requires ImapGmailConnection!"); } } /** * Gets Gmail {@link Session} * * @return Configured and ready for use Gmail {@link Session} */ public Session getSession() { synchronized (this) { if (mailSession == null) { properties = System.getProperties(); properties.put("mail.transport.protocol", "smtps"); properties.put("mail.debug", true); properties.put("mail.smtps.host", gmailSmtpHost); properties.put("mail.smtps.port", gmailSmtpPort); if (proxy != null) { log.debug("Setting proxy: " + proxy.address()); final SocketFactory sf = new HttpProxyAwareSslSocketFactory(proxy, proxyCredentials); properties.put("mail.imap.host", gmailImapHost); properties.put("mail.imaps.ssl.socketFactory", sf); properties.put("mail.imaps.ssl.socketFactory.fallback", "false"); properties.put("mail.imaps.ssl.socketFactory.port", gmailImapPort); } if (proxyCredentials != null) { mailSession = Session.getInstance(properties); } else { mailSession = Session.getInstance(properties); } for (Provider p : mailSession.getProviders()) { log.debug(p); } } } return mailSession; } @Override public void disconnect() { try { if (isConnected()) { store.close(); } } catch (final Exception e) { log.warn("Failed closing Gmail IMAP store", e); } } /** * Is this service currently connected? * <p/> * <p/> * This implementation uses a private boolean field to * store the connection state. This method returns the value * of that field. <p> * * @return true if the service is connected, false if it is not connected */ public boolean isConnected() { return connected; } /** * Sets {@link #connected}. * <p/> * * @param connected Gmail SMTP port */ public void setConnected(final boolean connected) { this.connected = connected; } public Proxy getProxy() { return this.proxy; } public void setProxy(final Proxy proxy) { this.proxy = proxy; } public void setProxy(final String host, final int port) { setProxy(new Proxy(Type.HTTP, InetSocketAddress.createUnresolved(host, port))); } public void setProxyCredentials(final Credentials credentials) { credentials.validate(); proxyCredentials = credentials; } public void setProxyCredentials(final String user, final char[] pass) { setProxyCredentials(new Credentials(user, pass)); } private String getUsername(Credentials loginCredentials) { String username = loginCredentials.getUsername(); if (!username.contains("@")) { username += Constants.GMAIL_EXTENSION; } return username; } @Override protected void finalize() throws Throwable { disconnect(); super.finalize(); } public void setDebug(boolean debug) { this.debug = debug; } public static final class OAuth2Provider extends java.security.Provider { private static final long serialVersionUID = 1L; public OAuth2Provider() { super("Google OAuth2 Provider", 1.0, "Provides the XOAUTH2 SASL Mechanism"); put("SaslClientFactory.XOAUTH2", "com.googlecode.gmail4j.javamail.OAuth2SaslClientFactory"); } } }