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