1 /**
2 * Copyright © DiamondMVC 2019
3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 */
6 module diamond.mail.smtp;
7 
8 import diamond.core.apptype;
9 
10 static if (isWeb)
11 {
12   import vibe.d : SMTPClientSettings, SMTPAuthType, SMTPConnectionType,
13                   TLSContext, TLSPeerValidationMode, TLSVersion,
14                   Mail, sendMail;
15 
16   import diamond.errors.checks;
17   import diamond.core.traits;
18   import diamond.security.validation.sensitive;
19 
20   // Alias to SMTPAuthType.
21   mixin(createEnumAlias!SMTPAuthType("SmtpAuthType"));
22 
23   // Alias to SMTPConnectionType.
24   mixin(createEnumAlias!SMTPConnectionType("SmtpConnectionType"));
25 
26   /// Wrapper around smtp client settings.
27   final class SmtpClientSettings
28   {
29     private:
30     /// The raw vibe.d smtp settings.
31     SMTPClientSettings _settings;
32 
33     /// Boolean determining whether the mail should allow sensitive data or not.
34     bool _allowSensitiveData;
35 
36     public:
37     final:
38     /// Creates new smtp client settings.
39     this()
40     {
41       _settings = new SMTPClientSettings;
42     }
43 
44     /**
45     * Creates new smtp client settings.
46     * Params:
47     *   host = The host of the smtp server.
48     *   port = The port of the smtp server.
49     */
50     this(string host, ushort port)
51     {
52       _settings = new SMTPClientSettings(host, port);
53     }
54 
55     /**
56     * Creates new smtp client settings.
57     * Params:
58     *   host =     The host of the smtp server.
59     *   port =     The port of the smtp server.
60     *   username = The username to use for the authentication.
61     *   password = The password to use for the authentication.
62     */
63     this(string host, ushort port, string username, string password)
64     {
65       this(host, port);
66 
67       this.username = username;
68       this.password = password;
69     }
70 
71     @property
72     {
73       /// Gets the authentication type.
74       SmtpAuthType authType() { return cast(SmtpAuthType)_settings.authType; }
75 
76       /// Sets the authentication type.
77       void authType(SmtpAuthType newAuthType)
78       {
79         _settings.authType = cast(SMTPAuthType)newAuthType;
80       }
81 
82       /// Gets the connection type.
83       SmtpConnectionType connectionType() { return cast(SmtpConnectionType)_settings.connectionType; }
84 
85       /// Sets the connection type.
86       void connectionType(SmtpConnectionType newConnectionType)
87       {
88         _settings.connectionType = cast(SMTPConnectionType)newConnectionType;
89       }
90 
91       /// Gets the host.
92       string host() { return _settings.host; }
93 
94       /// Sets the host.
95       void host(string newHost)
96       {
97         _settings.host = newHost;
98       }
99 
100       /// Gets the local name.
101       string localName() { return _settings.localname; }
102 
103       /// Sets the local name.
104       void localName(string newLocalName)
105       {
106         _settings.localname = newLocalName;
107       }
108 
109       /// Gets the username for the authentication.
110       string username() { return _settings.username; }
111 
112       /// Sets the username for the authentication.
113       void username(string newUsername)
114       {
115         _settings.username = newUsername;
116       }
117 
118       /// Gets the password for the authentication.
119       string password() { return _settings.password; }
120 
121       /// Sets the password for the authentication.
122       void password(string newPassword)
123       {
124         _settings.password = newPassword;
125       }
126 
127       /// Gets the port.
128       ushort port() { return _settings.port; }
129 
130       /// Sets the port.
131       void port(ushort newPort)
132       {
133         _settings.port = newPort;
134       }
135 
136       /// Get the tls context setup.
137       void delegate(scope TLSContext) @safe tlsContextSetup() { return _settings.tlsContextSetup; }
138 
139       /// Sets the tls context setup.
140       void tlsContextSetup(void delegate(scope TLSContext) @safe newContextSetup)
141       {
142         _settings.tlsContextSetup = newContextSetup;
143       }
144 
145       /// Gets the tls validation mode.
146       TLSPeerValidationMode tlsValidationMode() { return _settings.tlsValidationMode; }
147 
148       /// Sets the tls validation mode.
149       void tlsValidationMode(TLSPeerValidationMode newValidationMode)
150       {
151         _settings.tlsValidationMode = newValidationMode;
152       }
153 
154       /// Gets the tls version.
155       TLSVersion tlsVersion() { return _settings.tlsVersion; }
156 
157       /// Sets the tls version.
158       void tlsVersion(TLSVersion newVersion)
159       {
160         _settings.tlsVersion = newVersion;
161       }
162 
163       /// Gets a boolean determining whether the mail should allow sensitive data or not.
164       bool allowSensitiveData() { return _allowSensitiveData; }
165 
166       /// Sets a boolean determining whether the mail should allow sensitive data or not.
167       void allowSensitiveData(bool shouldAllowSensitiveData)
168       {
169         _allowSensitiveData = shouldAllowSensitiveData;
170       }
171     }
172   }
173 
174   /// Wrapper around an smtp mail.
175   final class SmtpMail
176   {
177     private:
178     /// The raw vibe.d mail.
179     Mail _mail;
180     /// The smtp client settings.
181     SmtpClientSettings _settings;
182     /// The sender.
183     string _sender;
184     /// The from-mail.
185     string _fromMail;
186     /// The recipient;
187     string _recipient;
188     /// The subject.
189     string _subject;
190     /// The message.
191     string _message;
192     /// The content type.
193     string _contentType;
194 
195     public:
196     final:
197     /// Creates a new mail.
198     this()
199     {
200       _mail = new Mail;
201     }
202 
203     /**
204     * Creates a new mail.
205     * Params:
206     *   settings = The settings for the mail.
207     */
208     this(SmtpClientSettings settings)
209     {
210       _settings = settings;
211     }
212 
213     @property
214     {
215       /// Gets the sender.
216       string sender() { return _sender; }
217 
218       /// Sets the sender.
219       void sender(string newSender)
220       {
221         _sender = newSender;
222       }
223 
224       /// Gets the from-mail.
225       string fromMail() { return _fromMail; }
226 
227       /// Sets the from-mail.
228       void fromMail(string newFromMail)
229       {
230         _fromMail = newFromMail;
231       }
232 
233       /// Gets the recipient.
234       string recipient() { return _recipient; }
235 
236       /// Sets the recipient.
237       void recipient(string newRecipient)
238       {
239         _recipient = newRecipient;
240       }
241 
242       /// Gets the subject.
243       string subject() { return _subject; }
244 
245       /// Sets the subject.
246       void subject(string newSubject)
247       {
248         _subject = newSubject;
249       }
250 
251       /// Gets the message.
252       string message() { return _message; }
253 
254       /// Set the message.
255       void message(string newMessage)
256       {
257         _message = newMessage;
258       }
259 
260       /// Gets the content type.
261       string contentType() { return _contentType; }
262 
263       /// Sets the content type.
264       void contentType(string newContentType)
265       {
266         _contentType = newContentType;
267       }
268     }
269 
270     /**
271     * Adds a header.
272     * Params:
273     *   name =  The name of the header.
274     *   value = The value of the header.
275     */
276     void addHeader(string name, string value)
277     {
278       _mail.headers[name] = value;
279     }
280 
281     /**
282     * Sends a mail with the mails current settings.
283     * Params:
284     *   level =    The security level to use for sensitive data if validation is turned on.
285     */
286     void send(SecurityLevel level = SecurityLevel.maximum)
287     {
288       enforce(_settings !is null, "The mail has no settings configured.");
289 
290       send(_settings, level);
291     }
292 
293     /**
294     * Sends a mail using specific settings.
295     * Params:
296     *   settings = The settings to use.
297     *   level =    The security level to use for sensitive data if validation is turned on.
298     */
299     void send(SmtpClientSettings settings, SecurityLevel level = SecurityLevel.maximum)
300     {
301       enforce(settings !is null, "Cannot send a mail without settings.");
302 
303       enforce(_fromMail && _fromMail.length, "From-mail is missing.");
304       enforce(_recipient && _recipient.length, "Recipient is missing.");
305       enforce(_subject && _subject.length, "Subject is missing.");
306       enforce(_message && _message.length, "Message is missing.");
307 
308       if (!settings.allowSensitiveData)
309       {
310         validateSensitiveData(_message, level);
311       }
312 
313       if (_sender && _sender.length)
314       {
315         addHeader("Sender", _sender);
316       }
317 
318       addHeader("From", _fromMail);
319       addHeader("To", _recipient);
320       addHeader("Subject", _subject);
321 
322       _mail.bodyText = _message;
323 
324       addHeader(
325         "Content-Type",
326         _contentType && _contentType.length ?
327         _contentType : "text/plain;charset=utf-8"
328       );
329 
330       sendMail(settings._settings, _mail);
331     }
332   }
333 }