View Javadoc

1   /*
2    * Copyright (C) 2017-2019 Centre National d'Etudes Spatiales (CNES).
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 3.0 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not, write to the Free Software
16   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17   * MA 02110-1301  USA
18   */
19  package fr.cnes.doi.settings;
20  
21  import fr.cnes.doi.utils.spec.Requirement;
22  import java.util.Locale;
23  import java.util.Map;
24  import java.util.concurrent.ConcurrentHashMap;
25  import org.apache.logging.log4j.Level;
26  import org.apache.logging.log4j.LogManager;
27  import org.apache.logging.log4j.Logger;
28  import org.restlet.Client;
29  import org.restlet.Context;
30  import org.restlet.Request;
31  import org.restlet.Response;
32  import org.restlet.data.ChallengeResponse;
33  import org.restlet.data.ChallengeScheme;
34  import org.restlet.data.LocalReference;
35  import org.restlet.data.MediaType;
36  import org.restlet.data.Method;
37  import org.restlet.data.Parameter;
38  import org.restlet.data.Protocol;
39  import org.restlet.data.Status;
40  import org.restlet.ext.freemarker.TemplateRepresentation;
41  import org.restlet.ext.javamail.JavaMailClientHelper;
42  import org.restlet.representation.Representation;
43  import org.restlet.resource.ClientResource;
44  import org.restlet.util.Series;
45  
46  /**
47   * Singleton to load and use Email settings.
48   *
49   * @author Jean-Christophe Malapert
50   */
51  @Requirement(reqId = Requirement.DOI_CONFIG_010, reqName = Requirement.DOI_CONFIG_010_NAME)
52  public final class EmailSettings {
53  
54      /**
55       * Default debug : {@value #DEFAULT_DEBUG}.
56       */
57      private static final boolean DEFAULT_DEBUG = false;
58  
59      /**
60       * Logger.
61       */
62      private static final Logger LOG = LogManager.getLogger(EmailSettings.class.getName());
63  
64      /**
65       * SMTP URL.
66       */
67      private String smtpUrl;
68  
69      /**
70       * SMTP protocol.
71       */
72      private String smtpProtocol;
73  
74      /**
75       * TLS.
76       */
77      private String tlsEnable;
78  
79      /**
80       * login.
81       */
82      private String authUser;
83  
84      /**
85       * Password.
86       */
87      private String authPwd;
88  
89      /**
90       * Contact admin uRL.
91       */
92      private String contactAdmin;
93  
94      /**
95       * debug.
96       */
97      private boolean debug = DEFAULT_DEBUG;
98  
99      /**
100      * Constructor
101      */
102     private EmailSettings() {
103         init();
104     }
105 
106     /**
107      * Access to unique INSTANCE of Settings
108      *
109      * @return the configuration instance.
110      */
111     public static EmailSettings getInstance() {
112         return EmailSettingsHolder.INSTANCE;
113     }
114 
115     /**
116      * Init singleton.
117      */
118     public void init() {
119         LOG.traceEntry();
120         LOG.info("----- Email parameters ----");
121 
122         final DoiSettings settings = DoiSettings.getInstance();
123 
124         this.smtpProtocol = settings.getString(Consts.SMTP_PROTOCOL, "");
125         LOG.info(String.format("smtp protocol : %s", this.smtpProtocol));
126 
127         this.smtpUrl = settings.getString(Consts.SMTP_URL, "");
128         LOG.info(String.format("smtp URL : %s", this.smtpUrl));
129 
130         this.authUser = settings.getString(Consts.SMTP_AUTH_USER, "");
131         LOG.info(String.format("auth user : %s", this.authUser));
132 
133         this.authPwd = settings.getSecret(Consts.SMTP_AUTH_PWD);
134         LOG.info(String.format("auth pwd : %s", this.authPwd));
135 
136         this.tlsEnable = settings.getString(Consts.SMTP_STARTTLS_ENABLE, "false");
137         LOG.info(String.format("TLS enable : %s", this.tlsEnable));
138 
139         this.contactAdmin = settings.getString(Consts.SERVER_CONTACT_ADMIN, "L-doi-support@cnes.fr");
140         LOG.info(String.format("Contact admin : %s", this.contactAdmin));
141 
142         LOG.info("Email settings have been loaded");
143         LOG.info("---------------------------");
144 
145         LOG.traceExit();
146     }
147 
148     /**
149      * Sets the debug.
150      *
151      * @param isEnabled True if debug is enabled otherwise False
152      */
153     public void setDebug(final boolean isEnabled) {
154         LOG.traceEntry("Parameter : {}", isEnabled);
155         this.debug = isEnabled;
156     }
157 
158     /**
159      * Returns the debug.
160      *
161      * @return debug
162      */
163     public boolean isDebug() {
164         return LOG.traceExit(this.debug);
165     }
166 
167     /**
168      * Sends a message by email.
169      *
170      * @param subject Email's subject
171      * @param msg Email's message
172      * @return True when the message is sent
173      */
174     public boolean sendMessage(final String subject, final String msg) {
175         final String email = EmailSettings.getInstance().getContactAdmin();
176         return this.sendMessage(subject, msg, email);
177     }
178 
179     /**
180      * Sends a message by email to receiver.
181      *
182      * @param subject Email's subject
183      * @param msg Email's message
184      * @param receiverEmail receiver
185      * @return True when the message is sent
186      */
187     public boolean sendMessage(final String subject, final String msg, final String receiverEmail) {
188         LOG.traceEntry("Parameters : {}, {} and {}", subject, msg, receiverEmail);
189         boolean result;
190         try {
191             if (isConfigureForSendingEmail()) {
192                 LOG.info("Try to send this message {} to {}", msg, receiverEmail);
193                 result = processMessage(subject, msg, receiverEmail);
194             } else {
195                 LOG.warn("Cannot send the email, please fill the configuration file");
196                 result = false;
197             }
198         } catch (RuntimeException ex) {
199             LOG.catching(Level.DEBUG, ex);
200             LOG.error("Cannot send the message with the subject {} : {}", subject, msg);
201             result = false;
202         } catch (Exception ex) {
203             LOG.catching(Level.DEBUG, ex);
204             LOG.error("Cannot send the message with the subject {} : {}", subject, msg);
205             result = false;
206         }
207         return LOG.traceExit(result);
208     }
209 
210     /**
211      * Process the message. Sends to SMTP server
212      *
213      * @param subject subject of the email
214      * @param message message
215      * @param receiverEmail receiver
216      * @return true when the message has been send otherwise false
217      * @throws Exception when an error happens
218      */
219     private boolean processMessage(final String subject,
220             final String message, final String receiverEmail) throws Exception {
221         LOG.debug("Enough information to send the email.");
222         final Request request = new Request(Method.POST, getSmtpURL());
223         setSmtpCredentials(request);
224         return sendMail(Protocol.valueOf(getSmtpProtocol()), request, Boolean.getBoolean(
225                 getTlsEnable()), subject, message, receiverEmail);
226     }
227 
228     /**
229      * Sets the SMTP credential when the SMTP needs an authentication.
230      *
231      * @param request request
232      */
233     private void setSmtpCredentials(final Request request) {
234         if (isAuthenticate()) {
235             LOG.debug("Sets the login/passwd for the SMTP server");
236             request.setChallengeResponse(
237                     new ChallengeResponse(ChallengeScheme.SMTP_PLAIN, getAuthUser(), getAuthPwd()));
238         } else {
239             LOG.debug("No required login/pwd for the SMTP server");
240             request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.SMTP_PLAIN));
241         }
242     }
243 
244     /**
245      * Returns true when configuration may be enough to send an email. Checks
246      * the SMTP URL and the SMTP protocol.
247      *
248      * @return true when configuration may be enough to send an email
249      */
250     private boolean isConfigureForSendingEmail() {
251         return !(this.getSmtpURL().isEmpty() && this.getSmtpProtocol().isEmpty());
252     }
253 
254     /**
255      * Returns true when sendmail neeed to be authenticated otherwide false.
256      *
257      * @return true when sendmail neeed to be authenticated otherwide false
258      */
259     private boolean isAuthenticate() {
260         return !(this.getAuthUser().isEmpty() && this.getAuthPwd().isEmpty());
261     }
262 
263     /**
264      * Creates the email representation.
265      *
266      * @param subject Email's subject
267      * @param msg Email's message
268      * @param to receiver
269      * @return the email representation
270      */
271     private Representation createMailRepresentation(final String subject, final String msg,
272             final String to) {
273         final Map<String, String> dataModel = new ConcurrentHashMap<>();
274         dataModel.put("subject", subject);
275         dataModel.put("message", msg);
276         dataModel.put("from", this.getContactAdmin());
277         dataModel.put("to", to);
278         final Representation mailFtl = new ClientResource(LocalReference.createClapReference(
279                 "class/email.ftl")).get();
280         return new TemplateRepresentation(mailFtl, dataModel, MediaType.TEXT_XML);
281     }
282 
283     /**
284      * Sends an email only when
285      * {@value fr.cnes.doi.settings.Consts#CONTEXT_MODE} is set to PROD.
286      *
287      * @param protocol Protocol (SMTP or SMTPS)
288      * @param request request
289      * @param startTls startTls
290      * @param subject Email's subject
291      * @param msg Email's message
292      * @param to receiver
293      * @return True when the message is sent
294      * @throws Exception - if an error happens when stopping the request
295      */
296     private boolean sendMail(final Protocol protocol,
297             final Request request,
298             final boolean startTls,
299             final String subject,
300             final String msg,
301             final String to) throws Exception {
302         LOG.traceEntry("Parameters\n  protocol:{}\n  startTls:{}, request:{}\n  subject:{}\n  "
303                 + "msg:{}\n  to:{}",
304                 protocol, request, startTls, subject, msg, to);
305         final String contextMode = DoiSettings.getInstance().getString(Consts.CONTEXT_MODE);
306         final Representation mail = this.createMailRepresentation(subject, msg, to);
307         request.setEntity(mail);
308         final boolean result;
309         if ("PROD".equals(contextMode)) {
310             final Client client = new Client(new Context(), protocol);
311             final Series<Parameter> parameters = client.getContext().getParameters();
312             parameters.add("debug", String.valueOf(isDebug()));
313             parameters.add("startTls", Boolean.toString(startTls).toLowerCase(Locale.ENGLISH));
314             final JavaMailClientHelper smtpClient = new JavaMailClientHelper(client);
315             final Response response = new Response(request);
316             smtpClient.handle(request, response);
317             final Status status = response.getStatus();
318             if (status.isSuccess()) {
319                 result = true;
320                 LOG.info("Message sent to {}", to);
321             } else {
322                 result = false;
323                 LOG.error("Cannot connect to SMTP server", status.getThrowable());
324             }
325         } else {
326             result = true;
327             LOG.warn("The configuration context {} is not PROD, do not send the email : {}",
328                     contextMode, mail.getText());
329         }
330         return LOG.traceExit(result);
331     }
332 
333     /**
334      * Returns the protocol URL.
335      *
336      * @return the URL
337      */
338     public String getSmtpURL() {
339         LOG.traceEntry();
340         return LOG.traceExit(smtpUrl);
341     }
342 
343     /**
344      * Returns the protocol.
345      *
346      * @return the port
347      */
348     public String getSmtpProtocol() {
349         LOG.traceEntry();
350         return LOG.traceExit(smtpProtocol);
351     }
352 
353     /**
354      * Returns True when TLS is enable otherwise False.
355      *
356      * @return the tlsEnable
357      */
358     public String getTlsEnable() {
359         LOG.traceEntry();
360         return LOG.traceExit(tlsEnable);
361     }
362 
363     /**
364      * Returns the decrypted login.
365      *
366      * @return the authUser
367      */
368     public String getAuthUser() {
369         LOG.traceEntry();
370         return LOG.traceExit(authUser);
371     }
372 
373     /**
374      * Returns the decrypted password.
375      *
376      * @return the authPwd
377      */
378     public String getAuthPwd() {
379         LOG.traceEntry();
380         return LOG.traceExit(authPwd);
381     }
382 
383     /**
384      * Returns the administrator's email.
385      *
386      * @return the contactAdmin
387      */
388     public String getContactAdmin() {
389         LOG.traceEntry();
390         return LOG.traceExit(contactAdmin);
391     }
392 
393     /**
394      *
395      */
396     private static class EmailSettingsHolder {
397 
398         /**
399          * Unique Instance unique not pre-initiliaze
400          */
401         private static final EmailSettings INSTANCE = new EmailSettings();
402     }
403 
404 }