/* * eXist Mail Module Extension * Copyright (C) 2006-09 Adam Retter <adam.retter@devon.gov.uk> * www.adamretter.co.uk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id$ */ package org.exist.xquery.modules.mail; import org.exist.xquery.AbstractInternalModule; import org.exist.xquery.FunctionDef; import org.exist.xquery.XQueryContext; import org.apache.log4j.Logger; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Store; /** * eXist Mail Module Extension * * An extension module for the eXist Native XML Database that allows email to * be sent from XQuery using either SMTP or Sendmail. * * @author Adam Retter <adam.retter@devon.gov.uk> * @author Andrzej Taramina <andrzej@chaeron.com> * @author ljo * @author José María Fernández <josemariafg@gmail.com> * @serial 2010-03-19 * @version 1.4 * * @see org.exist.xquery.AbstractInternalModule#AbstractInternalModule(org.exist.xquery.FunctionDef[], java.util.Map) */ public class MailModule extends AbstractInternalModule { protected final static Logger LOG = Logger.getLogger( MailModule.class ); public final static String NAMESPACE_URI = "http://exist-db.org/xquery/mail"; public final static String PREFIX = "mail"; // JavaMail-based from 2009-03-14 // makes the need for versioning of the functions obvious too /ljo public final static String INCLUSION_DATE = "2005-05-12, 2009-03-14"; public final static String RELEASED_IN_VERSION = "eXist-1.2 (JavaMail-based in trunk)"; private final static FunctionDef[] functions = { new FunctionDef( MailSessionFunctions.signatures[0], MailSessionFunctions.class ), new FunctionDef( MailStoreFunctions.signatures[0], MailStoreFunctions.class ), new FunctionDef( MailStoreFunctions.signatures[1], MailStoreFunctions.class ), new FunctionDef( MailFolderFunctions.signatures[0], MailFolderFunctions.class ), new FunctionDef( MailFolderFunctions.signatures[1], MailFolderFunctions.class ), new FunctionDef( MessageListFunctions.signatures[0], MessageListFunctions.class ), new FunctionDef( MessageListFunctions.signatures[1], MessageListFunctions.class ), new FunctionDef( MessageListFunctions.signatures[2], MessageListFunctions.class ), new FunctionDef( MessageListFunctions.signatures[3], MessageListFunctions.class ), new FunctionDef( SendEmailFunction.signatures[0], SendEmailFunction.class ), // deprecated functions: new FunctionDef( SendEmailFunction.deprecated, SendEmailFunction.class ) }; public final static String SESSIONS_CONTEXTVAR = "_eXist_mail_sessions"; public final static String STORES_CONTEXTVAR = "_eXist_mail_stores"; public final static String FOLDERS_CONTEXTVAR = "_eXist_mail_folders"; public final static String FOLDERMSGLISTS_CONTEXTVAR = "_eXist_folder_message_lists"; public final static String MSGLISTS_CONTEXTVAR = "_eXist_mail_message_lists"; private static long currentSessionHandle = System.currentTimeMillis(); public MailModule() { super(functions); } public String getNamespaceURI() { return( NAMESPACE_URI ); } public String getDefaultPrefix() { return( PREFIX ); } public String getDescription() { return( "A module for performing email related functions" ); } public String getReleaseVersion() { return RELEASED_IN_VERSION; } //*************************************************************************** //* //* Session Methods //* //***************************************************************************/ /** * Retrieves a previously stored Session from the Context of an XQuery * * @param context The Context of the XQuery containing the Session * @param sessionHandle The handle of the Session to retrieve from the Context of the XQuery */ final static Session retrieveSession( XQueryContext context, long sessionHandle ) { Session session = null; // get the existing sessions map from the context HashMap<Long, Session> sessions = (HashMap<Long, Session>)context.getXQueryContextVar( MailModule.SESSIONS_CONTEXTVAR ); if( sessions != null ) { session = sessions.get( new Long( sessionHandle ) ); } return( session ); } /** * Stores a Session in the Context of an XQuery * * @param context The Context of the XQuery to store the Session in * @param session The Session to store * * @return A unique handle representing the Session */ final static synchronized long storeSession( XQueryContext context, Session session ) { // get the existing sessions map from the context HashMap<Long, Session> sessions = (HashMap<Long, Session>)context.getXQueryContextVar( MailModule.SESSIONS_CONTEXTVAR ); if( sessions == null ) { // if there is no sessions map, create a new one sessions = new HashMap<Long, Session>(); } // get an handle for the session long sessionHandle = getHandle(); // place the session in the sessions map sessions.put( new Long( sessionHandle ), session ); // store the updated sessions map back in the context context.setXQueryContextVar( MailModule.SESSIONS_CONTEXTVAR, sessions ); return( sessionHandle ); } //*************************************************************************** //* //* Store Methods //* //***************************************************************************/ /** * Retrieves a previously saved Store from the Context of an XQuery * * @param context The Context of the XQuery containing the Store * @param storeHandle The handle of the Store to retrieve from the Context of the XQuery */ final static Store retrieveStore( XQueryContext context, long storeHandle ) { Store store = null; // get the existing stores map from the context HashMap<Long, Store> stores = (HashMap<Long, Store>)context.getXQueryContextVar( MailModule.STORES_CONTEXTVAR ); if( stores != null ) { store = stores.get( new Long( storeHandle ) ); } return( store ); } /** * Saves a Store in the Context of an XQuery * * @param context The Context of the XQuery to save the Store in * @param store The Store to store * * @return A unique handle representing the Store */ final static synchronized long storeStore( XQueryContext context, Store store ) { // get the existing stores map from the context HashMap<Long, Store> stores = (HashMap<Long, Store>)context.getXQueryContextVar( MailModule.STORES_CONTEXTVAR ); if( stores == null ) { // if there is no stores map, create a new one stores = new HashMap<Long, Store>(); } // get an handle for the store long storeHandle = getHandle(); // place the store in the stores map stores.put( new Long( storeHandle ), store ); // save the updated stores map back in the context context.setXQueryContextVar( MailModule.STORES_CONTEXTVAR, stores ); return( storeHandle ); } /** * Remove the store from the specified XQueryContext * * @param context The context to remove the store for */ final static synchronized void removeStore( XQueryContext context, long storeHandle ) { // get the existing stores map from the context HashMap<Long, Store> stores = (HashMap<Long, Store>)context.getXQueryContextVar( MailModule.STORES_CONTEXTVAR ); if( stores != null ) { stores.remove( new Long( storeHandle ) ) ; // update the context context.setXQueryContextVar( MailModule.STORES_CONTEXTVAR, stores ); } } /** * Closes all the open stores for the specified XQueryContext * * @param context The context to close stores for */ private final static synchronized void closeAllStores( XQueryContext context ) { // get the existing stores map from the context HashMap<Long, Store> stores = (HashMap<Long, Store>)context.getXQueryContextVar( MailModule.STORES_CONTEXTVAR ); if( stores != null ) { // iterate over each store Set<Long> keys = stores.keySet(); for( Iterator<Long> itKeys = keys.iterator(); itKeys.hasNext(); ) { // get the store Long storeHandle = itKeys.next(); Store store = stores.get( storeHandle ); try { // close the store store.close(); // remove it from the stores map stores.remove( storeHandle) ; } catch( MessagingException me ) { LOG.debug( "Unable to close Mail Store", me ); } } // update the context context.setXQueryContextVar( MailModule.STORES_CONTEXTVAR, stores ); } } //*************************************************************************** //* //* Folder Methods //* //***************************************************************************/ /** * Retrieves a previously saved Folder from the Context of an XQuery * * @param context The Context of the XQuery containing the Folder * @param folderHandle The handle of the Folder to retrieve from the Context of the XQuery */ final static Folder retrieveFolder( XQueryContext context, long folderHandle ) { Folder folder = null; // get the existing folders map from the context HashMap<Long, Folder> folders = (HashMap<Long, Folder>)context.getXQueryContextVar( MailModule.FOLDERS_CONTEXTVAR ); if( folders != null ) { folder = folders.get( new Long( folderHandle ) ); } return( folder ); } /** * Saves a Folder in the Context of an XQuery * * @param context The Context of the XQuery to save the Folder in * @param folder The Folder to store * * @return A unique handle representing the Store */ final static synchronized long storeFolder( XQueryContext context, Folder folder ) { // get the existing stores map from the context HashMap<Long, Folder> folders = (HashMap<Long, Folder>)context.getXQueryContextVar( MailModule.FOLDERS_CONTEXTVAR ); if( folders == null ) { // if there is no folders map, create a new one folders = new HashMap<Long, Folder>(); } // get an handle for the folder long folderHandle = getHandle(); // place the store in the folders map folders.put( new Long( folderHandle ), folder ); // save the updated folders map back in the context context.setXQueryContextVar( MailModule.FOLDERS_CONTEXTVAR, folders ); return( folderHandle ); } /** * Remove the folder from the specified XQueryContext * * @param context The context to remove the store for */ final static synchronized void removeFolder( XQueryContext context, long folderHandle ) { // get the existing folders map from the context HashMap<Long, Folder> folders = (HashMap<Long, Folder>)context.getXQueryContextVar( MailModule.FOLDERS_CONTEXTVAR ); if( folders != null ) { folders.remove( new Long( folderHandle ) ) ; // update the context context.setXQueryContextVar( MailModule.FOLDERS_CONTEXTVAR, folders ); // get the existing folderMsgLists map from the context and remove all the folder's message lists HashMap<Long, HashMap<Long, Object>> folderMsgLists = (HashMap<Long, HashMap<Long, Object>>)context.getXQueryContextVar( MailModule.FOLDERMSGLISTS_CONTEXTVAR ); HashMap msgLists = (HashMap)context.getXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR ); if( folderMsgLists != null ) { // get the folders message list HashMap<Long, Object> folderMsgList = (HashMap<Long, Object>)folderMsgLists.get( new Long( folderHandle ) ); if( folderMsgList != null ) { // iterate over each message list in this folder Set<Long> keys = folderMsgList.keySet(); for( Iterator<Long> itKeys = keys.iterator(); itKeys.hasNext(); ) { Long msgList = itKeys.next(); if( msgLists != null ) { msgLists.remove( msgList ) ; } } folderMsgLists.remove( new Long( folderHandle ) ); } // update the context context.setXQueryContextVar( MailModule.FOLDERMSGLISTS_CONTEXTVAR, folderMsgLists ); } } } /** * Closes all the open folders for the specified XQueryContext * * @param context The context to close folders for */ private final static synchronized void closeAllFolders( XQueryContext context ) { // get the existing folders map from the context HashMap<Long, Folder> folders = (HashMap<Long, Folder>)context.getXQueryContextVar( MailModule.FOLDERS_CONTEXTVAR ); if( folders != null ) { // iterate over each folder Set<Long> keys = folders.keySet(); for( Iterator<Long> itKeys = keys.iterator(); itKeys.hasNext(); ) { // get the folder Long folderHandle = itKeys.next(); Folder folder = folders.get( folderHandle ); try { // close the folder folder.close( false ); // remove it from the folders map folders.remove( folderHandle ) ; } catch( MessagingException me ) { LOG.debug( "Unable to close Mail Folder", me ); } } // update the context context.setXQueryContextVar( MailModule.FOLDERS_CONTEXTVAR, folders ); } } //*************************************************************************** //* //* Message List Methods //* //***************************************************************************/ /** * Retrieves a previously saved MessageList from the Context of an XQuery * * @param context The Context of the XQuery containing the Message List * @param msgListHandle The handle of the Message List to retrieve from the Context of the XQuery */ final static Message[] retrieveMessageList( XQueryContext context, long msgListHandle ) { Message[] msgList = null; // get the existing msgLists map from the context HashMap<Long, Message[]> msgLists = (HashMap<Long, Message[]>)context.getXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR ); if( msgLists != null ) { msgList = msgLists.get( new Long( msgListHandle ) ); } return( msgList ); } /** * Saves a MessageList in the Context of an XQuery * * @param context The Context of the XQuery to save the MessageList in * @param msgList The MessageList to store * * @return A unique handle representing the Store */ final static synchronized long storeMessageList( XQueryContext context, Message[] msgList, long folderHandle ) { // get the existing msgLists map from the context HashMap<Long, Message[]> msgLists = (HashMap<Long, Message[]>)context.getXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR ); if( msgLists == null ) { // if there is no msgLists map, create a new one msgLists = new HashMap<Long, Message[]>(); } // get an handle for the msgList long msgListHandle = getHandle(); // place the msgList in the msgLists map msgLists.put( new Long( msgListHandle ), msgList ); // save the updated msgLists map back in the context context.setXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR, msgLists ); // get the existing folderMsgLists map from the context HashMap<Long, HashMap<Long, Message[]>> folderMsgLists = (HashMap<Long, HashMap<Long, Message[]>>)context.getXQueryContextVar( MailModule.FOLDERMSGLISTS_CONTEXTVAR ); if( folderMsgLists == null ) { // if there is no folderMsgLists map, create a new one folderMsgLists = new HashMap<Long, HashMap<Long, Message[]>>(); } // get the folders message list HashMap<Long, Message[]> folderMsgList = (HashMap<Long, Message[]>)folderMsgLists.get( new Long( folderHandle ) ); if( folderMsgList == null ) { folderMsgList = new HashMap<Long, Message[]>(); folderMsgLists.put( new Long( folderHandle ), folderMsgList ); } // place the msgList in the folderMsgList map folderMsgList.put( new Long( msgListHandle ), msgList ); // save the updated folderMsgLists map back in the context context.setXQueryContextVar( MailModule.FOLDERMSGLISTS_CONTEXTVAR, folderMsgLists ); return( msgListHandle ); } /** * Remove the MessageList from the specified XQueryContext * * @param context The context to remove the MessageList for */ final static synchronized void removeMessageList( XQueryContext context, long msgListHandle ) { // get the existing msgLists map from the context HashMap<Long, Message[]> msgLists = (HashMap<Long, Message[]>)context.getXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR ); if( msgLists != null ) { msgLists.remove( new Long( msgListHandle ) ) ; // update the context context.setXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR, msgLists ); } } /** * Closes all the open MessageLists for the specified XQueryContext * * @param context The context to close MessageLists for */ private final static synchronized void closeAllMessageLists( XQueryContext context ) { // get the existing msgLists map from the context HashMap<Long, Message[]> msgLists = (HashMap<Long, Message[]>)context.getXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR ); if( msgLists != null ) { // iterate over each msgList Set<Long> keys = msgLists.keySet(); for( Iterator<Long> itKeys = keys.iterator(); itKeys.hasNext(); ) { msgLists.remove( itKeys.next() ) ; } // update the context context.setXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR, msgLists ); } } //*************************************************************************** //* //* Common Methods //* //***************************************************************************/ /** * Returns a Unique handle based on the System Time * * @return The Unique handle */ private static synchronized long getHandle() { return( currentSessionHandle++ ); } /** * Resets the Module Context and closes any open mail stores/folders/message lists for the XQueryContext * * @param context The XQueryContext */ public void reset( XQueryContext context ) { // reset the module context super.reset( context ); // close any open MessageLists closeAllMessageLists( context ); // close any open folders closeAllFolders( context ); // close any open stores closeAllStores( context ); } }