/*
* 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 java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.search.FlagTerm;
import javax.mail.search.SubjectTerm;
import javax.mail.search.RecipientStringTerm;
import javax.mail.search.SearchTerm;
import javax.mail.search.FromStringTerm;
import javax.mail.search.SentDateTerm;
import javax.mail.search.BodyTerm;
import com.googlecode.gmail4j.GmailClient;
import com.googlecode.gmail4j.GmailException;
import com.googlecode.gmail4j.GmailMessage;
import com.googlecode.gmail4j.GmailMessageList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* JavaMail IMAP based {@link GmailClient}
* <p>
* Example of getting unread messages:
* <p><blockquote><pre>
* GmailConnection conn = new ImapGmailConnection();
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* List<GmailMessage> unreadMessages = client.getUnreadMessages();
* </pre></blockquote><p>
* Example of getting priority messages:
* <p><blockquote><pre>
* GmailConnection conn = new ImapGmailConnection();
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* //set the {@code boolean} unreadOnly to {@code true} to retrive unread
* //priority messages only, {@code false} to read priority messages only
* List<GmailMessage> priorityMessages = client.getPriorityMessages(true);
* </pre></blockquote></p>
* Example of sending a simple message:
* <p><blockquote><pre>
* GmailConnection conn = new ImapGmailConnection();
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* GmailMessage message = new JavaMailGmailMessage();
* message.setSubject("Hi!");
* message.setContentText("A message from Gmail4J");
* message.addTo(new EmailAddress("j.smith@example.com"));
* client.send(message);
* </pre></blockquote></p>
* Example of moving message(s) to the trash folder:
* <p><blockquote><pre>
* GmailConnection conn = new ImapGmailConnection();
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* List<GmailMessage> messages = client.getUnreadMessages();
* client.moveToTrash(messages.toArray(new JavaMailGmailMessage[0]));
* </pre></blockquote></p>
* Example of marking a message as read:
* <p><blockquote><pre>
* GmailConnection conn = new ImapGmailConnection();
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* List<GmailMessage> messages = client.getUnreadMessages();
* // now get a GmailMessage item and pass it's message number
* client.markAsRead(message.getMessageNumber());
* </pre></blockquote></p>
* Example of marking all messages as read:
* <p><blockquote><pre>
* GmailConnection conn = new ImapGmailConnection();
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* client.markAllAsRead();
* </pre></blockquote></p>
* Example of marking a message as unread:
* <p><blockquote><pre>
* GmailConnection conn = new ImapGmailConnection();
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* // now get a read GmailMessage item and pass it's message number
* client.markAsUnread(message.getMessageNumber());
* </pre></blockquote></p>
* Example of flagging a message as starred:
* <p><blockquote><pre>
* GmailConnection conn = new ImapGmailConnection();
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* // now get a GmailMessage item and pass it's message number
* client.addStar(message.getMessageNumber());
* </pre></blockquote></p>
* Example of removing a message flagged as starred:
* <p><blockquote><pre>
* GmailConnection conn = new ImapGmailConnection();
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* // now get a GmailMessage item and pass it's message number
* client.removeStar(message.getMessageNumber());
* </pre></blockquote></p>
* Example of message move to destination folder:
* <p><blockquote><pre>
* // Constructor with the source folder name
* ImapGmailClient client = new ImapGmailClient(ImapGmailLabel.SENT_MAIL);
* //configure connection
* GmailClient client = new ImapGmailClient();
* client.setConnection(conn);
* // name the destination folder and the message # to be moved
* client.moveTo(ImapGmailLabel.SPAM, 1);
* </pre></blockquote></p>
*
* @see GmailClient
* @see ImapGmailConnection
* @author Tomas Varaneckas <tomas.varaneckas@gmail.com>
* @since 0.3
*/
public class ImapGmailClient extends GmailClient {
/**
* Source {@link Folder} label name
*/
private final String srcFolder;
/**
* Constructor that defaults to {@code ImapGmailLabel.INBOX.getName()}
* as source {@link Folder} name.
*/
public ImapGmailClient() {
this.srcFolder = ImapGmailLabel.INBOX.getName();
}
/**
* Constructor with the source {@link Folder} name
*
* @param label source {@link Folder} name.See {@see ImapGmailLabel}
* @see ImapGmailLabel
* @since 0.4
*/
public ImapGmailClient(final ImapGmailLabel label) {
this.srcFolder = ((label == null)
? ImapGmailLabel.INBOX.getName() : label.getName());
}
/**
* Logger
*/
private static final Log LOG = LogFactory.getLog(ImapGmailClient.class);
@Override
public List<GmailMessage> getUnreadMessages() {
return getMessagesBy(EmailSearchStrategy.UNREAD,"");
}
@Override
public GmailMessageList getMessagesBy(EmailSearchStrategy strategy, String value)
{
SearchTerm seekStrategy = null;
switch(strategy)
{
case SUBJECT:
seekStrategy = new SubjectTerm(value);
LOG.debug("Fetching emails with a subject of \"" + value + "\"");
break;
case TO:
seekStrategy = new RecipientStringTerm(Message.RecipientType.TO,value);
LOG.debug("Fetching emails sent to \"" + value + "\"");
break;
case FROM:
seekStrategy = new FromStringTerm(value);
LOG.debug("Fetching emails sent from \"" + value + "\"");
break;
case CC:
seekStrategy = new RecipientStringTerm(Message.RecipientType.CC,value);
LOG.debug("Fetching emails CC'd to \"" + value + "\"");
break;
case DATE_GT:
seekStrategy = new SentDateTerm(SentDateTerm.GT, parseDate(value));
LOG.debug("Fetching emails with a send date newer than \"" + value + "\"");
break;
case DATE_LT:
seekStrategy = new SentDateTerm(SentDateTerm.LT, parseDate(value));
LOG.debug("Fetching emails with a send date newer than \"" + value + "\"");
break;
case DATE_EQ:
seekStrategy = new SentDateTerm(SentDateTerm.EQ,parseDate(value));
LOG.debug("Fetching emails with a send date newer than \"" + value + "\"");
break;
case KEYWORD:
seekStrategy = new BodyTerm(value);
LOG.debug("Fetching emails containing the keyword \"" + value + "\"");
break;
case UNREAD:
seekStrategy = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
LOG.debug("Fetching all unread emails");
break;
}
try {
final GmailMessageList found = new GmailMessageList();
final Store store = openGmailStore();
final Folder folder = getFolder(this.srcFolder,store);
folder.open(Folder.READ_ONLY);
for (final Message msg : folder.search(seekStrategy)) {
found.add(new JavaMailGmailMessage(msg));
}
LOG.debug("Found " + found.size() + " emails");
return found;
} catch (final Exception e) {
throw new GmailException("Failed getting unread messages", e);
}
}
/**
* Opens Gmail {@link Store}
*
* @return instance of Gmail store
* @throws GmailException if GmailConnection is not ImapGmailConnection
*/
private Store openGmailStore() {
if (connection instanceof ImapGmailConnection) {
return ((ImapGmailConnection) connection).openGmailStore();
}
throw new GmailException("ImapGmailClient requires ImapGmailConnection!");
}
/**
* Gets Gmail {@link Transport} for sending messages
*
* @return Configured Gmail Transport ready for use
* @throws GmailException if GmailConnection is not ImapGmailConnection
*/
private Transport getGmailTransport() {
if (connection instanceof ImapGmailConnection) {
return ((ImapGmailConnection) connection).getTransport();
}
throw new GmailException("ImapGmailClient requires ImapGmailConnection!");
}
@Override
public void send(final GmailMessage message) {
if (message instanceof JavaMailGmailMessage) {
Transport transport = null;
try {
final JavaMailGmailMessage msg = (JavaMailGmailMessage) message;
transport = getGmailTransport();
transport.sendMessage(
msg.getMessage(),
msg.getMessage().getAllRecipients());
} catch (final Exception e) {
throw new GmailException("Failed sending message: " + message, e);
}
finally{
if(transport.isConnected())
{
try {
transport.close();
} catch (final Exception e) {
LOG.warn("Cannot Close ImapGmailConnection : " + transport, e);
}
}
}
} else {
throw new GmailException("ImapGmailClient requires JavaMailGmailMessage!");
}
}
/**
* Moves given {@link GmailMessage}'s to {@link ImapGmailLabel.TRASH} folder.
*
* @param gmailMessages {@link GmailMessage} message(s)
* @throws GmailException if unable to move {@link GmailMessage}'s to
* the Trash Folder
*/
public void moveToTrash(final GmailMessage[] gmailMessages) {
if (gmailMessages == null || gmailMessages.length <= 0) {
LOG.warn("ImapGmailClient requires GmailMessage(s) to move"
+ " to move messages to trash folder");
return;
}
Folder folder = null;
try {
final Store store = openGmailStore();
folder = getFolder(this.srcFolder,store);
if(!folder.isOpen())
{
folder.open(Folder.READ_WRITE);
}
List<Message> markedMsgList = new ArrayList<Message>();
for (GmailMessage gmailMessage : gmailMessages) {
// get only messages that match to the specified message number
Message message = folder.getMessage(gmailMessage.getMessageNumber());
message.setFlag(Flags.Flag.SEEN, true);
// mark message as delete
message.setFlag(Flags.Flag.DELETED, true);
markedMsgList.add(message);
}
Folder trash = getFolder(ImapGmailLabel.TRASH.getName(),store);
if(folder.getURLName().equals(trash.getURLName())){
LOG.warn("ImapGmailClient trying to move GmailMessage(s) within"
+ " same folder(ImapGmailLabel.TRASH.getName())");
}
// move the marked messages to trash folder
if (!markedMsgList.isEmpty()) {
folder.copyMessages(markedMsgList.toArray(new Message[0]), trash);
}
} catch (Exception e) {
throw new GmailException("ImapGmailClient failed moving GmailMessage(s)"
+ " to trash folder: " + e);
} finally {
closeFolder(folder);
}
}
/**
* Mark a given {@link GmailMessage} as read.
*
* @param messageNumber the message number ex:{@code gmailMessage.getMessageNumber()}
* @throws GmailException if unable to mark {@link GmailMessage} as read
*/
public void markAsRead(int messageNumber) {
if (messageNumber <= 0) {
throw new GmailException("ImapGmailClient invalid "
+ "GmailMessage number");
}
Folder folder = null;
try {
final Store store = openGmailStore();
folder = getFolder(this.srcFolder, store);
folder.open(Folder.READ_WRITE);
Message message = folder.getMessage(messageNumber);
if (!message.isSet(Flags.Flag.SEEN)) {
message.setFlag(Flags.Flag.SEEN, true);
}
} catch (Exception e) {
throw new GmailException("ImapGmailClient failed marking"
+ " GmailMessage as read : " + messageNumber, e);
} finally {
closeFolder(folder);
}
}
/**
* Mark all {@link GmailMessage}(s) as read in a folder.
*
* @throws GmailException if unable to mark all {@link GmailMessage} as read
*/
public void markAllAsRead() {
Folder folder = null;
try {
final Store store = openGmailStore();
folder = getFolder(this.srcFolder, store);
folder.open(Folder.READ_WRITE);
for (final Message message : folder.search(new FlagTerm(
new Flags(Flags.Flag.SEEN), false))) {
message.setFlag(Flags.Flag.SEEN, true);
}
} catch (Exception e) {
throw new GmailException("ImapGmailClient failed marking"
+ " all GmailMessage as read" , e);
} finally {
closeFolder(folder);
}
}
/**
* Mark a given {@link GmailMessage} as unread.
*
* @param messageNumber the message number ex:{@code gmailMessage.getMessageNumber()}
* @throws GmailException if unable to mark {@link GmailMessage} as unread
*/
public void markAsUnread(int messageNumber) {
if (messageNumber <= 0) {
throw new GmailException("ImapGmailClient invalid "
+ "GmailMessage number");
}
Folder folder = null;
try {
final Store store = openGmailStore();
folder = getFolder(this.srcFolder, store);
folder.open(Folder.READ_WRITE);
Message message = folder.getMessage(messageNumber);
if (message.isSet(Flags.Flag.SEEN)) {
message.setFlag(Flags.Flag.SEEN, false);
}
} catch (Exception e) {
throw new GmailException("ImapGmailClient failed marking"
+ " GmailMessage as unread : " + messageNumber , e);
} finally {
closeFolder(folder);
}
}
/**
* Flag a given {@link GmailMessage} as Starred.
*
* @param messageNumber the message number ex:{@code gmailMessage.getMessageNumber()}
* @throws GmailException if unable to flag {@link GmailMessage} as starred
*/
public void addStar(int messageNumber){
if (messageNumber <= 0) {
throw new GmailException("ImapGmailClient invalid "
+ "GmailMessage number");
}
Folder folder = null;
try {
final Store store = openGmailStore();
folder = getFolder(this.srcFolder, store);
folder.open(Folder.READ_WRITE);
Message message = folder.getMessage(messageNumber);
if (!message.isSet(Flags.Flag.FLAGGED)) {
message.setFlag(Flags.Flag.FLAGGED, true);
}
} catch (Exception e) {
throw new GmailException("ImapGmailClient failed flagging"
+ " GmailMessage as starred : " + messageNumber ,e);
} finally {
closeFolder(folder);
}
}
/**
* Removes Star Flag of a given Starred {@link GmailMessage}.
*
* @param messageNumber the message number ex:{@code gmailMessage.getMessageNumber()}
* @throws GmailException if unable to remove star flag from {@link GmailMessage}
*/
public void removeStar(int messageNumber){
if (messageNumber <= 0) {
throw new GmailException("ImapGmailClient invalid "
+ "GmailMessage number");
}
Folder folder = null;
try {
final Store store = openGmailStore();
folder = getFolder(ImapGmailLabel.STARRED.getName(), store);
folder.open(Folder.READ_WRITE);
Message message = folder.getMessage(messageNumber);
if (message.isSet(Flags.Flag.FLAGGED)) {
message.setFlag(Flags.Flag.FLAGGED, false);
}
} catch (Exception e) {
throw new GmailException("ImapGmailClient failed removing"
+ " GmailMessage star flag : " + messageNumber , e);
} finally {
closeFolder(folder);
}
}
/**
* Move {@link GmailMessage} to a given destination folder.
*
* @param destFolder the destination {@link Folder} name.See {@see ImapGmailLabel}
* @param messageNumber the message number ex:{@code gmailMessage.getMessageNumber()}
* @throws GmailException if it fails to move {@link GmailMessage} to the
* destination folder
*/
public void moveTo(ImapGmailLabel destFolder,int messageNumber){
if (messageNumber <= 0) {
throw new GmailException("ImapGmailClient invalid GmailMessage number");
}
Folder fromFolder = null;
Folder toFolder = null;
try {
final Store store = openGmailStore();
fromFolder = getFolder(this.srcFolder, store);
fromFolder.open(Folder.READ_WRITE);
Message message = fromFolder.getMessage(messageNumber);
if (message != null) {
toFolder = getFolder(destFolder.getName(), store);
if (fromFolder.getURLName().equals(toFolder.getURLName())) {
throw new GmailException("ImapGmailClient cannot move "
+ "GmailMessage within same folder "
+ "(from " + fromFolder.getFullName() + " to "
+ toFolder.getFullName() + ")");
}
// copy from source folder to destination folder
fromFolder.copyMessages(new Message[]{message}, toFolder);
// move the copied message to trash folder
moveToTrash(new GmailMessage[]{new JavaMailGmailMessage(message)});
}
} catch (GmailException ge) {
throw ge;
} catch (Exception e) {
throw new GmailException("ImapGmailClient failed moving"
+ " GmailMessage from " + fromFolder.getFullName(), e);
} finally {
closeFolder(fromFolder);
}
}
/**
* Returns list of unread/read priority {@link GmailMessage} objects
* based on the {@code unreadOnly} value
*
* @param unreadOnly {@code true} to unread priority {@link GmailMessage}
* objects only, {@code false} to read priority {@link GmailMessage}
* objects only
* @return List of unread/read priority messages
* @throws GmailException if unable to get unread/read priority messages
*/
public List<GmailMessage> getPriorityMessages(boolean unreadOnly){
try {
final List<GmailMessage> priorityMessages = new ArrayList<GmailMessage>();
final Store store = openGmailStore();
Folder folder = getFolder(ImapGmailLabel.IMPORTANT.getName(),store);
folder.open(Folder.READ_ONLY);
for (final Message msg : folder.search(new FlagTerm(
new Flags(Flags.Flag.SEEN), !unreadOnly))) {
priorityMessages.add(new JavaMailGmailMessage(msg));
}
return priorityMessages;
} catch (final Exception e) {
throw new GmailException("Failed getting priority messages", e);
}
}
/**
* Return the {@link Folder} object corresponding to the given
* {@link ImapGmailLabel} name.
* Note that a {@link Folder} object is returned only if the named
* {@link Folder} physically exist on the Store.
*
* @param name the name of the folder
* @param store instance of Gmail {@link Store}
*/
private Folder getFolder(String name, final Store store) {
if (store == null) {
throw new GmailException("Gmail IMAP store cannot be null");
}
try {
name = ((name == null) ? this.srcFolder : name);
Folder folder = store.getFolder(name);
if (folder.exists()) {
return folder;
}
} catch (final Exception e) {
throw new GmailException("ImapGmailClient failed getting "
+ "Folder: " + name, e);
}
throw new GmailException("ImapGmailClient Folder name cannot be null");
}
/**
* Close any {@link Folder} that contain {@link Message} and are in open state.
*
* @param folder {@link Folder} to be closed
*/
private void closeFolder(Folder folder) {
if (folder != null) {
try {
if (folder.isOpen()) {
folder.close(true);
} else {
LOG.info(folder.getName() + " folder is already open.");
}
} catch (Exception e) {
LOG.warn("Cannot close folder : " + folder.getName(), e);
}
}
}
/**
* Parse a date in "yyyy-MM-dd HH:mm:ss" format
*
* @param date
* @return
*/
private Date parseDate(String date) {
try {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(date);
} catch (ParseException e) {
LOG.error("Bad date format in " + date + ". Use yyyy-MM-dd HH:mm:ss");
return new Date();
}
}
@Override
public void disconnect() {
if (connection != null) {
connection.disconnect();
}
}
}