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