1   /*
2    * Created by IntelliJ IDEA.
3    * User: owen
4    * Date: Nov 22, 2002
5    * Time: 3:33:27 PM
6    * CVS Revision: $Revision: 1.6 $
7    * Last CVS Commit: $Date: 2006/09/29 02:48:01 $
8    * Author of last CVS Commit: $Author: cowen $
9    * To change this template use Options | File Templates.
10   */
11  package com.atlassian.mail.server;
12  
13  import com.atlassian.mail.MailException;
14  import com.atlassian.mail.MailFactory;
15  import com.atlassian.mail.MailProtocol;
16  import org.apache.commons.lang.StringUtils;
17  import org.apache.commons.lang.builder.EqualsBuilder;
18  import org.apache.commons.lang.builder.HashCodeBuilder;
19  import org.apache.commons.lang.builder.ToStringBuilder;
20  import org.apache.log4j.Logger;
21  
22  import javax.mail.Authenticator;
23  import javax.mail.Session;
24  import java.io.PrintStream;
25  import java.io.Serializable;
26  import java.io.ObjectInputStream;
27  import java.io.IOException;
28  import java.lang.reflect.Field;
29  import java.util.Arrays;
30  import java.util.Properties;
31  
32  public abstract class AbstractMailServer implements MailServer, Serializable
33  {
34      protected transient Logger log = Logger.getLogger(this.getClass());
35      private Long id;
36      private String name;
37      private String description;
38      private String hostname;
39      private String username = null;
40      private String password = null;
41      private MailProtocol mailProtocol = null;
42      private String port = null;
43      private long timeout;
44      private boolean debug;
45      private boolean tlsRequired;
46      private transient PrintStream debugStream;
47      private Properties props = new Properties();
48      protected boolean isAuthenticating;
49      private String socksHost;
50      private String socksPort;
51  
52      public AbstractMailServer()
53      {
54      }
55  
56      public AbstractMailServer(Long id, String name, String description, MailProtocol protocol, String hostName, String port, String username, String password, long timeout, String socksHost, String socksPort)
57      {
58          setId(id);
59          setName(name);
60          setDescription(description);
61          setHostname(hostName);
62          setUsername(username);
63          setPassword(password);
64          setMailProtocol(protocol);
65          setPort(port);
66          setTimeout(timeout);
67          setSocksHost(socksHost);
68          setSocksPort(socksPort);
69          //MAIL-60: Need to ensure that system properties get set for mail servers.
70          this.props = loadSystemProperties(props);
71      }
72  
73      private void setInitialProperties()
74      {
75          if (getMailProtocol() != null)
76          {
77              final String protocol = getMailProtocol().getProtocol();
78              props.put("mail."+protocol+".host",""+getHostname());
79              props.put("mail."+protocol+".port", ""+getPort());
80              props.put("mail."+protocol+".timeout",""+getTimeout());
81              props.put("mail.transport.protocol",""+ protocol);
82              if (isTlsRequired())
83              {
84                  props.put("mail."+protocol+".starttls.enable","true");
85              }
86              if (StringUtils.isNotBlank(getUsername()))
87              {
88                  props.put("mail."+protocol+".auth", "true");
89                  isAuthenticating = true;
90              }
91              
92              if (StringUtils.isNotBlank(getSocksHost()))
93              {
94                  props.put("mail." + protocol + ".socks.host", getSocksHost());
95              }
96              
97              if (StringUtils.isNotBlank(getSocksPort()))
98              {
99                  props.put("mail." + protocol + ".socks.port", getSocksPort());
100             }
101         }
102         props.put("mail.debug", ""+getDebug());
103         if (Boolean.getBoolean("mail.debug"))
104         {
105             props.put("mail.debug", "true");
106         }
107     }
108 
109     protected abstract Authenticator getAuthenticator();
110 
111     public Long getId()
112     {
113         return id;
114     }
115 
116     public void setId(Long id)
117     {
118         this.id = id;
119         propertyChanged();
120     }
121 
122     public String getName()
123     {
124         return name;
125     }
126 
127     public void setName(String name)
128     {
129         this.name = name;
130         propertyChanged();
131     }
132 
133     public String getDescription()
134     {
135         return description;
136     }
137 
138     public void setDescription(String description)
139     {
140         this.description = description;
141         propertyChanged();
142     }
143 
144     public String getHostname()
145     {
146         return hostname;
147     }
148 
149     public void setHostname(String serverName)
150     {
151         this.hostname = serverName;
152         propertyChanged();
153     }
154 
155     public String getUsername()
156     {
157         return username;
158     }
159 
160     public void setUsername(String username)
161     {
162         if (StringUtils.isNotBlank(username))
163             this.username = username;
164         else
165             this.username = null;
166         propertyChanged();
167     }
168 
169     public String getPassword()
170     {
171         return password;
172     }
173 
174     public void setPassword(String password)
175     {
176         if (StringUtils.isNotBlank(password))
177             this.password = password;
178         else
179             this.password = null;
180         propertyChanged();
181     }
182 
183     public MailProtocol getMailProtocol()
184      {
185          return mailProtocol;
186      }
187 
188      public void setMailProtocol(final MailProtocol protocol)
189      {
190          this.mailProtocol = protocol;
191          propertyChanged();
192      }
193 
194      public String getPort()
195      {
196          return port;
197      }
198 
199      public void setPort(final String port)
200      {
201          this.port = port;
202          propertyChanged();
203      }
204 
205     public long getTimeout()
206     {
207         return timeout;
208     }
209 
210     public void setTimeout(long timeout)
211     {
212         this.timeout = timeout;
213         propertyChanged();
214     }
215     
216     public String getSocksHost()
217     {
218         return socksHost;
219     }
220 
221     public void setSocksHost(String socksHost)
222     {
223         this.socksHost = socksHost;
224         propertyChanged();
225     }
226 
227     public String getSocksPort()
228     {
229         return socksPort;
230     }
231 
232     public void setSocksPort(String socksPort)
233     {
234         this.socksPort = socksPort;
235         propertyChanged();
236     }
237 
238     public boolean isTlsRequired()
239     {
240         return tlsRequired;
241     }
242 
243     public void setTlsRequired(final boolean tlsRequired)
244     {
245         this.tlsRequired = tlsRequired;
246         propertyChanged();
247     }
248 
249     public Properties getProperties()
250     {
251         return props;
252     }
253 
254     public void setProperties(Properties props)
255     {
256         this.props = props;
257         propertyChanged();
258     }
259 
260     public void setDebug(boolean debug) {
261         this.debug = debug;
262         propertyChanged();
263     }
264 
265     public void setDebugStream(PrintStream debugStream) {
266         this.debugStream = debugStream;
267         propertyChanged();
268     }
269 
270 
271     public boolean getDebug() {
272         return this.debug;
273     }
274 
275     public PrintStream getDebugStream() {
276         return this.debugStream;
277     }
278 
279     ///CLOVER:OFF
280     public boolean equals(Object o)
281     {
282         if (this == o) return true;
283         if (!(o instanceof AbstractMailServer)) return false;
284         final AbstractMailServer abstractMailServer = (AbstractMailServer) o;
285         return new EqualsBuilder()
286                 .append(id, abstractMailServer.id)
287                 .append(name, abstractMailServer.name)
288                 .append(description, abstractMailServer.description)
289                 .append(hostname, abstractMailServer.hostname)
290                 .append(username, abstractMailServer.username)
291                 .append(password, abstractMailServer.password)
292                 .append(mailProtocol, abstractMailServer.mailProtocol)
293                 .append(port, abstractMailServer.port)
294                 .append(socksHost, abstractMailServer.socksHost)
295                 .append(socksPort, abstractMailServer.socksPort)
296                 .isEquals();
297     }
298 
299     public int hashCode() {
300         return new HashCodeBuilder()
301                 .append(id)
302                 .append(name)
303                 .append(description)
304                 .append(hostname)
305                 .append(username)
306                 .append(password)
307                 .append(mailProtocol)
308                 .append(port)
309                 .append(socksHost)
310                 .append(socksPort)
311                 .toHashCode();
312     }
313 
314     public String toString()
315     {
316         return new ToStringBuilder(this)
317                 .append("id", id)
318                 .append("name", name)
319                 .append("description", description)
320                 .append("server name", hostname)
321                 .append("username", username)
322                 .append("password", password != null ? "***" : "<unset>")
323                 .append("socks host", socksHost)
324                 .append("socks port", socksPort)
325                 .toString();
326     }
327 
328     /**
329      * Call this method whenever a property of the server changes.
330      * Subclasses should override it to clear any cached information.
331      */
332     protected void propertyChanged()
333     {
334         setInitialProperties();
335     }
336 
337     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException
338     {
339         ois.defaultReadObject();
340         log = Logger.getLogger(this.getClass());
341     }
342 
343     /**
344      * This allows users of atlassian mail to add command line properties to modify session defaults
345      * See JRA-11452
346      *
347      * The hierarchy now is - default properties, then from the command line, then properties added via setProperties
348      *
349      * @param p the default properties for the current mail session
350      * @return  the properties with the system properties loaded
351      */
352     protected synchronized Properties loadSystemProperties(Properties p)
353     {
354         Properties props = new Properties();
355         props.putAll(p);
356         props.putAll(System.getProperties());
357         if (this.props != null)
358         {
359             props.putAll(this.props);
360         }
361         return props;
362     }
363 
364 	public void setLogger(Logger logger) {
365 		this.log = logger;
366 	}
367 
368 	// This whole ugly code below is just to log some internal Session details, which are sometimes
369 	// necessary for debugging purposes by our support, but they are not logged where they should be
370 	// namely because Session logs a lot of stuff in its constructor before you can pass it a desired
371 	// debug stream.
372 	protected void getMoreDebugInfoAboutCreatedSession(Session session) {
373 		log.debug("Session providers: [" + Arrays.toString(session.getProviders()) + "]");
374 		try
375 		{
376 			final Field addressMapField = Session.class.getDeclaredField("addressMap");
377 			final boolean originalAccessibility = addressMapField.isAccessible();
378 			addressMapField.setAccessible(true);
379 			try
380 			{
381 				log.debug("Session addressMap: [" + addressMapField.get(session) + "]");
382 			}
383 			finally
384 			{
385 				addressMapField.setAccessible(originalAccessibility);
386 			}
387 
388 		}
389 		catch (Exception e)
390 		{
391 			log.debug("Cannot retrieve Session details via reflections: " + e.getMessage(), e);
392 		}
393 	}
394 
395 	protected Session getSessionFromServerManager(Properties props, final Authenticator authenticator) throws MailException {
396 		log.debug("Getting session");
397 		if (getDebug())
398 		{
399 			log.debug("Debug messages from JavaMail session initialization will not appear in this log." +
400 					" These messages are sent to standard out.");
401 		}
402 		final Session session = MailFactory.getServerManager().getSession(props, authenticator);
403 
404 		if (log.isDebugEnabled())
405 		{
406 			getMoreDebugInfoAboutCreatedSession(session);
407 		}
408 		if (getDebugStream() != null)
409 		{
410 			try
411 			{
412 				session.setDebugOut(getDebugStream());
413 			}
414 			catch (NoSuchMethodError nsme)
415 			{
416 				// JRA-8543
417 				log.error("Warning: An old (pre-1.3.2) version of the JavaMail library (javamail.jar or mail.jar) bundled with your app server, is in use. Some functions such as IMAPS/POPS/SMTPS will not work. Consider upgrading the app server's javamail jar to the version JIRA provides.");
418 			}
419 		}
420 		return session;
421 	}
422 
423 }