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  
50      public AbstractMailServer()
51      {
52      }
53  
54      public AbstractMailServer(Long id, String name, String description, MailProtocol protocol, String hostName, String port, String username, String password, long timeout)
55      {
56          setId(id);
57          setName(name);
58          setDescription(description);
59          setHostname(hostName);
60          setUsername(username);
61          setPassword(password);
62          setMailProtocol(protocol);
63          setPort(port);
64          setTimeout(timeout);
65          //MAIL-60: Need to ensure that system properties get set for mail servers.
66          this.props = loadSystemProperties(props);
67      }
68  
69      private void setInitialProperties()
70      {
71          if (getMailProtocol() != null)
72          {
73              final String protocol = getMailProtocol().getProtocol();
74              props.put("mail."+protocol+".host",""+getHostname());
75              props.put("mail."+protocol+".port", ""+getPort());
76              props.put("mail."+protocol+".timeout",""+getTimeout());
77              props.put("mail.transport.protocol",""+ protocol);
78              if (isTlsRequired())
79              {
80                  props.put("mail."+protocol+".starttls.enable","true");
81              }
82              if (StringUtils.isNotBlank(getUsername()))
83              {
84                  props.put("mail."+protocol+".auth", "true");
85                  isAuthenticating = true;
86              }
87          }
88          props.put("mail.debug", ""+getDebug());
89          if (Boolean.getBoolean("mail.debug"))
90          {
91              props.put("mail.debug", "true");
92          }
93      }
94  
95      protected abstract Authenticator getAuthenticator();
96  
97      public Long getId()
98      {
99          return id;
100     }
101 
102     public void setId(Long id)
103     {
104         this.id = id;
105         propertyChanged();
106     }
107 
108     public String getName()
109     {
110         return name;
111     }
112 
113     public void setName(String name)
114     {
115         this.name = name;
116         propertyChanged();
117     }
118 
119     public String getDescription()
120     {
121         return description;
122     }
123 
124     public void setDescription(String description)
125     {
126         this.description = description;
127         propertyChanged();
128     }
129 
130     public String getHostname()
131     {
132         return hostname;
133     }
134 
135     public void setHostname(String serverName)
136     {
137         this.hostname = serverName;
138         propertyChanged();
139     }
140 
141     public String getUsername()
142     {
143         return username;
144     }
145 
146     public void setUsername(String username)
147     {
148         if (StringUtils.isNotBlank(username))
149             this.username = username;
150         else
151             this.username = null;
152         propertyChanged();
153     }
154 
155     public String getPassword()
156     {
157         return password;
158     }
159 
160     public void setPassword(String password)
161     {
162         if (StringUtils.isNotBlank(password))
163             this.password = password;
164         else
165             this.password = null;
166         propertyChanged();
167     }
168 
169     public MailProtocol getMailProtocol()
170      {
171          return mailProtocol;
172      }
173 
174      public void setMailProtocol(final MailProtocol protocol)
175      {
176          this.mailProtocol = protocol;
177          propertyChanged();
178      }
179 
180      public String getPort()
181      {
182          return port;
183      }
184 
185      public void setPort(final String port)
186      {
187          this.port = port;
188          propertyChanged();
189      }
190 
191     public long getTimeout()
192     {
193         return timeout;
194     }
195 
196     public void setTimeout(long timeout)
197     {
198         this.timeout = timeout;
199         propertyChanged();
200     }
201 
202     public boolean isTlsRequired()
203     {
204         return tlsRequired;
205     }
206 
207     public void setTlsRequired(final boolean tlsRequired)
208     {
209         this.tlsRequired = tlsRequired;
210         propertyChanged();
211     }
212 
213     public Properties getProperties()
214     {
215         return props;
216     }
217 
218     public void setProperties(Properties props)
219     {
220         this.props = props;
221         propertyChanged();
222     }
223 
224     public void setDebug(boolean debug) {
225         this.debug = debug;
226         propertyChanged();
227     }
228 
229     public void setDebugStream(PrintStream debugStream) {
230         this.debugStream = debugStream;
231         propertyChanged();
232     }
233 
234 
235     public boolean getDebug() {
236         return this.debug;
237     }
238 
239     public PrintStream getDebugStream() {
240         return this.debugStream;
241     }
242 
243     ///CLOVER:OFF
244     public boolean equals(Object o)
245     {
246         if (this == o) return true;
247         if (!(o instanceof AbstractMailServer)) return false;
248         final AbstractMailServer abstractMailServer = (AbstractMailServer) o;
249         return new EqualsBuilder()
250                 .append(id, abstractMailServer.id)
251                 .append(name, abstractMailServer.name)
252                 .append(description, abstractMailServer.description)
253                 .append(hostname, abstractMailServer.hostname)
254                 .append(username, abstractMailServer.username)
255                 .append(password, abstractMailServer.password)
256                 .append(mailProtocol, abstractMailServer.mailProtocol)
257                 .append(port, abstractMailServer.port)
258                 .isEquals();
259     }
260 
261     public int hashCode() {
262         return new HashCodeBuilder()
263                 .append(id)
264                 .append(name)
265                 .append(description)
266                 .append(hostname)
267                 .append(username)
268                 .append(password)
269                 .append(mailProtocol)
270                 .append(port)
271                 .toHashCode();
272     }
273 
274     public String toString()
275     {
276         return new ToStringBuilder(this).append("id", id).append("name", name).append("description", description).append("server name", hostname).append("username", username).append("password", password != null ? "***" : "<unset>").toString();
277     }
278 
279     /**
280      * Call this method whenever a property of the server changes.
281      * Subclasses should override it to clear any cached information.
282      */
283     protected void propertyChanged()
284     {
285         setInitialProperties();
286     }
287 
288     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException
289     {
290         ois.defaultReadObject();
291         log = Logger.getLogger(this.getClass());
292     }
293 
294     /**
295      * This allows users of atlassian mail to add command line properties to modify session defaults
296      * See JRA-11452
297      *
298      * The hierarchy now is - default properties, then from the command line, then properties added via setProperties
299      *
300      * @param p the default properties for the current mail session
301      * @return  the properties with the system properties loaded
302      */
303     protected synchronized Properties loadSystemProperties(Properties p)
304     {
305         Properties props = new Properties();
306         props.putAll(p);
307         props.putAll(System.getProperties());
308         if (this.props != null)
309         {
310             props.putAll(this.props);
311         }
312         return props;
313     }
314 
315 	public void setLogger(Logger logger) {
316 		this.log = logger;
317 	}
318 
319 	// This whole ugly code below is just to log some internal Session details, which are sometimes
320 	// necessary for debugging purposes by our support, but they are not logged where they should be
321 	// namely because Session logs a lot of stuff in its constructor before you can pass it a desired
322 	// debug stream.
323 	protected void getMoreDebugInfoAboutCreatedSession(Session session) {
324 		log.debug("Session providers: [" + Arrays.toString(session.getProviders()) + "]");
325 		try
326 		{
327 			final Field addressMapField = Session.class.getDeclaredField("addressMap");
328 			final boolean originalAccessibility = addressMapField.isAccessible();
329 			addressMapField.setAccessible(true);
330 			try
331 			{
332 				log.debug("Session addressMap: [" + addressMapField.get(session) + "]");
333 			}
334 			finally
335 			{
336 				addressMapField.setAccessible(originalAccessibility);
337 			}
338 
339 		}
340 		catch (Exception e)
341 		{
342 			log.debug("Cannot retrieve Session details via reflections: " + e.getMessage(), e);
343 		}
344 	}
345 
346 	protected Session getSessionFromServerManager(Properties props, final Authenticator authenticator) throws MailException {
347 		log.debug("Getting session");
348 		if (getDebug())
349 		{
350 			log.debug("Debug messages from JavaMail session initialization will not appear in this log." +
351 					" These messages are sent to standard out.");
352 		}
353 		final Session session = MailFactory.getServerManager().getSession(props, authenticator);
354 
355 		if (log.isDebugEnabled())
356 		{
357 			getMoreDebugInfoAboutCreatedSession(session);
358 		}
359 		if (getDebugStream() != null)
360 		{
361 			try
362 			{
363 				session.setDebugOut(getDebugStream());
364 			}
365 			catch (NoSuchMethodError nsme)
366 			{
367 				// JRA-8543
368 				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.");
369 			}
370 		}
371 		return session;
372 	}
373 
374 }