1   
2   package com.atlassian.mail.server.impl;
3   
4   import alt.javax.mail.Session;
5   import alt.javax.mail.SessionImpl;
6   import alt.javax.mail.Transport;
7   import alt.javax.mail.internet.MimeMessage;
8   import alt.javax.mail.internet.MimeMessageImpl;
9   import com.atlassian.mail.Email;
10  import com.atlassian.mail.MailException;
11  import com.atlassian.mail.MailFactory;
12  import com.atlassian.mail.MailProtocol;
13  import com.atlassian.mail.server.AbstractMailServer;
14  import com.atlassian.mail.server.MailServerManager;
15  import com.atlassian.mail.server.SMTPMailServer;
16  import com.atlassian.mail.server.impl.util.MessageCreator;
17  import org.apache.commons.lang.builder.ToStringBuilder;
18  import org.apache.log4j.Category;
19  
20  import javax.mail.Authenticator;
21  import javax.mail.MessagingException;
22  import javax.mail.PasswordAuthentication;
23  import javax.naming.Context;
24  import javax.naming.InitialContext;
25  import javax.naming.NamingException;
26  import java.io.UnsupportedEncodingException;
27  import java.util.Properties;
28  
29  import static com.atlassian.mail.MailConstants.DEFAULT_SMTP_PROTOCOL;
30  import static com.atlassian.mail.MailConstants.DEFAULT_TIMEOUT;
31  
32  public class SMTPMailServerImpl extends AbstractMailServer implements SMTPMailServer
33  {
34      private static final Category log = Category.getInstance(SMTPMailServerImpl.class);
35  
36      private boolean isSessionServer;
37      private String defaultFrom;
38      private String prefix;
39      private String jndiLocation;
40      private boolean removePrecedence;
41      private transient Session session;
42  
43      public SMTPMailServerImpl()
44      {
45      }
46  
47      public SMTPMailServerImpl(Long id, String name, String description, String from, String prefix, boolean isSession, String location, String username, String password)
48      {
49          this(id, name, description, from, prefix, isSession, DEFAULT_SMTP_PROTOCOL, location, DEFAULT_SMTP_PORT, false, username, password);
50      }
51  
52      public SMTPMailServerImpl(Long id, String name, String description, String from, String prefix, boolean isSession, MailProtocol protocol, String location, String smtpPort, boolean tlsRequired, String username, String password)
53      {
54          this(id, name, description, from, prefix, isSession, false, protocol, location, smtpPort, tlsRequired, username, password, DEFAULT_TIMEOUT);
55      }
56  
57      public SMTPMailServerImpl(Long id, String name, String description, String from, String prefix, boolean isSession, MailProtocol protocol, String location, String smtpPort, boolean tlsRequired, String username, String password, long timeout)
58      {
59          this(id, name, description, from, prefix, isSession, false, protocol, location, smtpPort, tlsRequired, username, password, timeout);
60      }
61  
62      public SMTPMailServerImpl(Long id, String name, String description, String from, String prefix, boolean isSession, boolean removePrecedence, MailProtocol protocol, String location, String smtpPort, boolean tlsRequired, String username, String password, long timeout)
63      {
64          super(id, name, description, protocol, location, smtpPort, username, password, timeout);
65          setDefaultFrom(from);
66          setPrefix(prefix);
67          setSessionServer(isSession);
68          setRemovePrecedence(removePrecedence);
69          setTlsRequired(tlsRequired);
70  
71          if (isSession)
72          {
73              setJndiLocation(location);
74              setHostname(null);
75          }
76      }
77  
78      public String getJndiLocation()
79      {
80          return jndiLocation;
81      }
82  
83      public void setJndiLocation(String jndiLocation)
84      {
85          this.jndiLocation = jndiLocation;
86          propertyChanged();
87      }
88  
89      protected Authenticator getAuthenticator()
90      {
91          return new MyAuthenticator();
92      }
93  
94      /**
95       * get the mail session
96       */
97      public Session getSession() throws NamingException, MailException
98      {
99          if (session == null)
100 
101         {
102             if (isSessionServer())
103             {
104                 Object jndiSession = getJndiSession();
105                 if (jndiSession instanceof javax.mail.Session)
106                 {
107                     session = new SessionImpl((javax.mail.Session) jndiSession);
108                 }
109                 else
110                 {
111                     log.error("Mail server at location [" + getJndiLocation() + "] is not of required type javax.mail.Session, or is in different classloader. " +
112                             "It is of type '" + (jndiSession != null ? jndiSession.getClass().getName() : null) + "' in classloader '"+jndiSession.getClass().getClassLoader()+"' instead");
113                     throw new IllegalArgumentException("Mail server at location [" + getJndiLocation() + "] is not of required type javax.mail.Session. ");
114                 }
115             }
116             else
117             {
118                 final Properties props = loadSystemProperties(getProperties());
119                 final Authenticator auth = isAuthenticating ? getAuthenticator() : null;
120                 session = MailFactory.getServerManager().getSession(props, auth);
121                 if (getDebugStream() != null)
122                 {
123                     try
124                     {
125                     session.getWrappedSession().setDebugOut(getDebugStream());
126                     } catch (NoSuchMethodError nsme)
127                     {
128                         // JRA-8543
129                         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.");
130                     }
131                 }
132             }
133         }
134         return session;
135     }
136 
137     protected Object getJndiSession() throws NamingException
138     {
139         Context ctx = new InitialContext();
140         Object jndiSession = ctx.lookup(getJndiLocation());
141         return jndiSession;
142     }
143 
144     public void send(Email email) throws MailException
145     {
146         try
147         {
148             Session thisSession = getSession();
149 
150             MimeMessage message = new MimeMessageImpl(thisSession);
151             MessageCreator messageCreator = new MessageCreator();
152 
153             messageCreator.updateMimeMessage(email, getDefaultFrom(), prefix, message);
154 
155             // Send the message using the transport configured for this mail server - JRA-24549/MAIL-61
156             final Transport transport = thisSession.getTransport(getMailProtocol().getProtocol());
157             try
158             {
159                 transport.connect();
160                 transport.sendMessage(message, message.getAllRecipients());
161             }
162             finally
163             {
164                 transport.close();
165             }
166 
167             // Message-Id is set by the MTA (I think) so is only accessible after sending
168             if (message.getHeader("Message-Id") != null && message.getHeader("Message-Id").length > 0)
169             {
170                 email.setMessageId(message.getHeader("Message-Id")[0]);
171             }
172         }
173         catch (NamingException e)
174         {
175             throw new MailException(e);
176         }
177         catch (MessagingException e)
178         {
179             throw new MailException(e);
180         }
181         catch (UnsupportedEncodingException e)
182         {
183             log.debug("Error setting the 'from' address with an email and the user's fullname");
184             e.printStackTrace();
185         }
186     }
187 
188 
189     /**
190      * Send a message - but don't throw exceptions, just log the errors
191      */
192     public void quietSend(Email email) throws MailException
193     {
194         try
195         {
196             send(email);
197         }
198         catch (Exception e)
199         {
200             log.error("Error sending mail. to:" + email.getTo() + ", cc:" + email.getCc() + ", bcc:" + email.getBcc() + ", subject:" + email.getSubject() + ", body:" + email.getBody() + ", mimeType:" + email.getMimeType() + ", encoding:" + email.getEncoding() + ", multipart:" + email.getMultipart() + ", error:" + e, e);
201         }
202     }
203 
204     public String getType()
205     {
206         return MailServerManager.SERVER_TYPES[1];
207     }
208 
209     public String getDefaultFrom()
210     {
211         return defaultFrom;
212     }
213 
214     public void setDefaultFrom(String defaultFrom)
215     {
216         this.defaultFrom = defaultFrom;
217         propertyChanged();
218     }
219 
220     public String getPrefix()
221     {
222         return prefix;
223     }
224 
225     public void setPrefix(String prefix)
226     {
227         this.prefix = prefix;
228     }
229 
230     public boolean isRemovePrecedence()
231     {
232         return removePrecedence;
233     }
234 
235     public void setRemovePrecedence(boolean precedence)
236     {
237         this.removePrecedence = precedence;
238         propertyChanged();
239     }
240 
241     public boolean isSessionServer()
242     {
243         return isSessionServer;
244     }
245 
246     public void setSessionServer(boolean sessionServer)
247     {
248         isSessionServer = sessionServer;
249         propertyChanged();
250     }
251 
252     private class MyAuthenticator extends Authenticator
253     {
254         public PasswordAuthentication getPasswordAuthentication()
255         {
256             return new PasswordAuthentication(getUsername(), getPassword());
257         }
258     }
259 
260     ///CLOVER:OFF
261     public boolean equals(Object o)
262     {
263         if (this == o)
264         {
265             return true;
266         }
267         if (!(o instanceof SMTPMailServerImpl))
268         {
269             return false;
270         }
271         if (!super.equals(o))
272         {
273             return false;
274         }
275 
276         final SMTPMailServerImpl smtpMailServer = (SMTPMailServerImpl) o;
277 
278         if (isSessionServer != smtpMailServer.isSessionServer)
279         {
280             return false;
281         }
282         if (defaultFrom != null ? !defaultFrom.equals(smtpMailServer.defaultFrom) : smtpMailServer.defaultFrom != null)
283         {
284             return false;
285         }
286         if (prefix != null ? !prefix.equals(smtpMailServer.prefix) : smtpMailServer.prefix != null)
287         {
288             return false;
289         }
290         if (removePrecedence !=  smtpMailServer.removePrecedence)
291         {
292             return false;
293         }
294 
295         return true;
296     }
297 
298     public int hashCode()
299     {
300         int result = super.hashCode();
301         result = 29 * result + (isSessionServer ? 1 : 0);
302         result = 29 * result + (defaultFrom != null ? defaultFrom.hashCode() : 0);
303         result = 29 * result + (prefix != null ? prefix.hashCode() : 0);
304         result = 29 * result + (removePrecedence ? 1 : 0);
305         return result;
306     }
307 
308     public String toString()
309     {
310        return new ToStringBuilder(this).append("id", getId()).append("name", getName()).append("description", getDescription()).append("server name", getHostname()).append("username", getUsername()).append("password", getPassword()).append("isSessionServer", isSessionServer).append("defaultFrom", defaultFrom).append("prefix", prefix).append("smtpPort", getPort()).toString();
311     }
312 
313     /**
314      * Discard the cached session when a property of the server changes.
315      */
316     protected void propertyChanged()
317     {
318         super.propertyChanged();
319         session = null;
320     }
321 }