View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.email;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.MuleException;
11  import org.mule.api.endpoint.EndpointURI;
12  import org.mule.api.endpoint.ImmutableEndpoint;
13  import org.mule.api.lifecycle.InitialisationException;
14  import org.mule.transport.AbstractConnector;
15  import org.mule.util.PropertiesUtils;
16  import org.mule.util.StringUtils;
17  
18  import java.io.UnsupportedEncodingException;
19  import java.net.URLDecoder;
20  import java.util.Enumeration;
21  import java.util.HashMap;
22  import java.util.Map;
23  import java.util.Properties;
24  
25  import javax.mail.Authenticator;
26  import javax.mail.Session;
27  import javax.mail.URLName;
28  
29  /**
30   * Abstract superclass for mail connectors. Provides Mule with an Authenticator
31   * object and other shared functionality like e.g. MuleSession creation.
32   */
33  public abstract class AbstractMailConnector extends AbstractConnector
34  {
35      public static final String ATTACHMENT_HEADERS_PROPERTY_POSTFIX = "Headers";
36      public static final String MAILBOX = "INBOX";
37  
38      private Map<ImmutableEndpoint, SessionDetails> sessions = new HashMap<ImmutableEndpoint, SessionDetails>();
39      private String mailboxFolder;
40      private int defaultPort;
41  
42      /**
43       * A custom authenticator to be used on any mail sessions created with this
44       * connector. This will only be used if user name credendials are set on the
45       * endpoint.
46       */
47      private Authenticator authenticator = null;
48  
49      public AbstractMailConnector(int defaultPort, String mailboxFolder, MuleContext context)
50      {
51          super(context);
52          this.defaultPort = defaultPort;
53          this.mailboxFolder = mailboxFolder;
54      }
55  
56      public int getDefaultPort()
57      {
58          return defaultPort;
59      }
60  
61      public Authenticator getAuthenticator()
62      {
63          return authenticator;
64      }
65  
66      public void setAuthenticator(Authenticator authenticator)
67      {
68          this.authenticator = authenticator;
69      }
70      
71      public String getMailboxFolder()
72      {
73          return mailboxFolder;
74      }
75  
76      public void setMailboxFolder(String mailboxFolder)
77      {
78          this.mailboxFolder = mailboxFolder;
79      }
80  
81      public SessionDetails getSessionDetails(ImmutableEndpoint endpoint) throws UnsupportedEncodingException
82      {
83          // do not use this connector's implicit mutex by making this method synchronized. This
84          // may interfere with other methods using the same mutex for different purposes.
85          synchronized (sessions)
86          {
87              SessionDetails sessionDetails = sessions.get(endpoint);
88              if (null == sessionDetails)
89              {
90                  sessionDetails = newSession(endpoint);
91                  sessions.put(endpoint, sessionDetails);
92              }
93              return sessionDetails;
94          }
95      }
96      
97      public URLName urlFromEndpoint(ImmutableEndpoint endpoint) throws UnsupportedEncodingException
98      {
99          String inbox = endpoint.getEndpointURI().getPath();
100         if (inbox.length() == 0)
101         {
102             inbox = getMailboxFolder();
103         }
104         else
105         {
106             inbox = inbox.substring(1);
107         }
108 
109         EndpointURI uri = endpoint.getEndpointURI();
110         String user = uri.getUser();
111         if (user != null)
112         {
113             user = URLDecoder.decode(user, endpoint.getEncoding());
114         }
115         String pass = uri.getPassword();
116         if (pass != null)
117         {
118             pass = URLDecoder.decode(pass, endpoint.getEncoding());
119         }
120 
121         return new URLName(uri.getScheme(), uri.getHost(), uri.getPort(), inbox, user, pass);
122     }
123     
124     /**
125      * Some protocols (eg secure extensions) extend a "base" protocol.
126      * Subclasses for such protocols should override this method.
127      * 
128      * @return the underlying (eg non-secure) protocol
129      */
130     protected String getBaseProtocol()
131     {
132         return getProtocol();
133     }
134     
135     /**
136      * Subclasses should extend this to add further properties.  
137      * Synchronization is managed outside this call (so no need to synchronize further on properties)
138      * 
139      * @param global system properties 
140      * @param local local properties (specific to one session)
141      * @param url the endpoint url
142      */
143     protected void extendPropertiesForSession(Properties global, Properties local, URLName url)
144     {
145         int port = url.getPort();
146         if (port == -1)
147         {
148             port = this.getDefaultPort();
149         }
150         local.setProperty("mail." + getBaseProtocol() + ".socketFactory.port", Integer.toString(port));
151 
152         if (StringUtils.isNotBlank(url.getPassword()))
153         {
154             local.setProperty("mail." + getBaseProtocol() + ".auth", "true");
155             if (getAuthenticator() == null)
156             {
157                 setAuthenticator(new DefaultAuthenticator(url.getUsername(), url.getPassword()));
158                 if (logger.isDebugEnabled())
159                 {
160                     logger.debug("No Authenticator set on connector: " + getName() + "; using default.");
161                 }
162             }
163         }
164         else
165         {
166             local.setProperty("mail." + getBaseProtocol() + ".auth", "false");
167         }
168         
169         // TODO - i'm not at all certain that these properties (especially the ones
170         // using the base protocol) are needed.  they are inherited from old, gnarly
171         // code.
172 
173         if (StringUtils.isNotBlank(url.getHost())) {
174             local.setProperty("mail." + getBaseProtocol() + ".host", url.getHost());
175         }
176         local.setProperty("mail." + getBaseProtocol() + ".rsetbeforequit", "true");
177     }
178 
179     protected SessionDetails newSession(ImmutableEndpoint endpoint) throws UnsupportedEncodingException
180     {
181         URLName url = urlFromEndpoint(endpoint);
182 
183         Properties global = System.getProperties();
184         Properties local = new Properties();
185         //Allow properties to be set on the endpoint
186         PropertiesUtils.getPropertiesWithPrefix(endpoint.getProperties(), "mail.", local);
187         Session session;
188 
189         // make sure we do not mess with authentication set via system properties
190         synchronized (global)
191         {
192             extendPropertiesForSession(global, local, url);
193             session = Session.getInstance(local, getAuthenticator());
194         }
195 
196         if (logger.isDebugEnabled())
197         {
198             local.setProperty("mail.debug", "true");
199             
200             dumpProperties("MuleSession local properties", local, true);
201             dumpProperties("System global properties", global, true);
202             logger.debug("Creating mail session: host = " + url.getHost() + ", port = " + url.getPort()
203                 + ", user = " + url.getUsername() + ", pass = " + url.getPassword());
204         }
205 
206         return new SessionDetails(session, url);
207     }
208     
209     protected void dumpProperties(String title, Properties properties, boolean filter)
210     {
211         int skipped = 0;
212         logger.debug(title + " =============");
213         Enumeration keys = properties.keys();
214         while (keys.hasMoreElements())
215         {
216             String key = (String) keys.nextElement();
217             if (!filter || key.startsWith("mule.") || key.startsWith("mail.") || key.startsWith("javax."))
218             {
219                 String value = properties.getProperty(key);
220                 logger.debug(key + ": " + value);
221             }
222             else 
223             {
224                 ++skipped;
225             }
226         }
227         if (filter)
228         {
229             logger.debug("skipped " + skipped);
230         }
231     }
232     
233     // supply these here because sub-classes are very simple
234 
235     @Override
236     protected void doInitialise() throws InitialisationException
237     {
238         // template method, nothing to do
239     }
240 
241     @Override
242     protected void doDispose()
243     {
244         // template method, nothing to do
245     }
246 
247     @Override
248     protected void doConnect() throws Exception
249     {
250         // template method, nothing to do
251     }
252 
253     @Override
254     protected void doDisconnect() throws Exception
255     {
256         // template method, nothing to do
257     }
258 
259     @Override
260     protected void doStart() throws MuleException
261     {
262         // template method, nothing to do
263     }
264 
265     @Override
266     protected void doStop() throws MuleException
267     {
268         // template method, nothing to do
269     }
270 
271 }