Mstdlib-1.24.0
m_net_smtp.h
1/* The MIT License (MIT)
2 *
3 * Copyright (c) 2020 Monetra Technologies, LLC.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#ifndef __M_NET_SMTP_H__
25#define __M_NET_SMTP_H__
26
27/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
28
29#include <mstdlib/mstdlib.h>
30#include <mstdlib/mstdlib_io.h>
31#include <mstdlib/mstdlib_formats.h>
32#include <mstdlib/mstdlib_tls.h>
33
34/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
35
36__BEGIN_DECLS
37
38/*! \addtogroup m_net_smtp SMTP mailer
39 * \ingroup m_net
40 *
41 * SMTP mailer
42 *
43 * Defaults to 3 send attempts.
44 *
45 * Will start running processing queued messages soon as an endpoint is added.
46 *
47 * @{
48 *
49 */
50
51struct M_net_smtp;
52typedef struct M_net_smtp M_net_smtp_t;
53
54
55/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
56
57/*! Current processing status. */
58typedef enum {
59 M_NET_SMTP_STATUS_IDLE = 0, /*!< Currently up and able to process. */
60 M_NET_SMTP_STATUS_PROCESSING, /*!< Currently processing. */
61 M_NET_SMTP_STATUS_STOPPED, /*!< Not processing. */
62 M_NET_SMTP_STATUS_NOENDPOINTS, /*!< Not processing due to no endpoints configured. */
63 M_NET_SMTP_STATUS_STOPPING /*!< In the process of stopping. Messages will not continue to
64 be sent but current messages that are processing will
65 process until finished. */
67
68
69/*! Pool operation mode. */
70typedef enum {
71 M_NET_SMTP_LOAD_BALANCE_FAILOVER = 0, /*!< Only one endpoint should be used and others should
72 be used when the current endpoint has a failure. */
73 M_NET_SMTP_LOAD_BALANCE_ROUNDROBIN /*!< Connections should rotate across all endpoints. */
75
76
77/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
78
79/*! Callback when connected.
80 *
81 * Only used by TCP endpoints.
82 *
83 * \param[in] address Address of the server. This is the same address passed
84 * to M_net_smtp_add_endpoint_tcp.
85 * \param[in] port Port connected to. This is the same port passed
86 * to M_net_smtp_add_endpoint_tcp.
87 * \param[in] thunk Thunk parameter provided during create.
88 */
89typedef void (*M_net_smtp_connect_cb)(const char *address, M_uint16 port, void *thunk);
90
91
92/*! Callback when a connection to a server fails.
93 *
94 * Only used by TCP endpoints.
95 *
96 * \param[in] address Address of the server. This is the same address passed
97 * to M_net_smtp_add_endpoint_tcp.
98 * \param[in] port Port connected to. This is the same port passed
99 * to M_net_smtp_add_endpoint_tcp.
100 * \param[in] net_err Indicates where there was a network problem of some type or
101 * if the network operation succeeded.
102 * \param[in] error Error message.
103 * \param[in] thunk Thunk parameter provided during create.
104 *
105 * \return 0 if the endpoint should be be removed from the pool. Otherwise number of seconds to wait before
106 * retrying the server later.
107 */
108typedef M_uint64 (*M_net_smtp_connect_fail_cb)(const char *address, M_uint16 port, M_net_error_t net_err, const char *error, void *thunk);
109
110
111/*! Callback when the connection to the server disconnects.
112 *
113 * Only used by TCP endpoints.
114 *
115 * This does not represent an error. Server connections will be establish and disconnected
116 * periodically as part of normal processing.
117 *
118 * \param[in] address Address of the server. This is the same address passed
119 * to M_net_smtp_add_endpoint_tcp.
120 * \param[in] port Port connected to. This is the same port passed
121 * to M_net_smtp_add_endpoint_tcp.
122 * \param[in] thunk Thunk parameter provided during create.
123 */
124typedef void (*M_net_smtp_disconnect_cb)(const char *address, M_uint16 port, void *thunk);
125
126
127/*! Callback when a process endpoint fails.
128 *
129 * Only used by process endpoints.
130 *
131 * \param[in] command Command executed. Same as passed to M_net_smtp_add_endpoint_process.
132 * \param[in] result_code Exit code of the process.
133 * \param[in] proc_stdout Output of the process.
134 * \param[in] proc_stderror Error output of the process.
135 * \param[in] thunk Thunk parameter provided during create.
136 *
137 * \return 0 if the endpoint should be be removed from the pool. Otherwise the number of seconds to wait before
138 * retrying the server later.
139 */
140typedef M_uint64 (*M_net_smtp_process_fail_cb)(const char *command, int result_code, const char *proc_stdout, const char *proc_stderror, void *thunk);
141
142
143/*! Callback when all endpoints have failed.
144 *
145 * \param[in] no_endpoints M_TRUE if processing was halted due to no endpoints configured.
146 * \param[in] thunk Thunk parameter provided during create.
147 *
148 * \return The number of seconds to wait before retrying to process. Use 0 to stop
149 * automatic reconnect attempts. When 0, M_net_smtp_resume must be called to
150 * restart processing. The return value is ignored if no endpoints are configured.
151 */
152typedef M_uint64 (*M_net_smtp_processing_halted_cb)(M_bool no_endpoints, void *thunk);
153
154
155/*! Callback when a message was sent successfully.
156 *
157 * \param[in] headers Message headers provided as metadata to identify
158 * the message that was sent.
159 * \param[in] thunk Thunk parameter provided during create.
160 */
161typedef void (*M_net_smtp_sent_cb)(const M_hash_dict_t *headers, void *thunk);
162
163
164/*! Callback when sending a message failed.
165 *
166 * \param[in] headers Message headers provided as metadata to identify
167 * the message that failed.
168 * \param[in] error Error message.
169 * \param[in] attempt_num Current attempt number to send this message. Will be 0 when
170 * using an external queue. Otherwise, >= 1.
171 * \param[in] can_requeue M_TRUE when the message can be requed to try again. Will be
172 * M_FALSE if the message has reached the maximum send attempts
173 * when using the internal queue. Or when an external queue is
174 * in use.
175 * \param[in] thunk Thunk parameter provided during create.
176 *
177 * \return M_TRUE to requeue the message. Ignored if using an external queue.
178 */
179typedef M_bool (*M_net_smtp_send_failed_cb)(const M_hash_dict_t *headers, const char *error, size_t attempt_num, M_bool can_requeue, void *thunk);
180
181
182/*! Callback when a message needs to be requeued.
183 *
184 * Only called when an external queue is used. Will be called when a message that was dequeued
185 * failed to send.
186 *
187 * \param[in] msg Raw email message.
188 * \param[in] wait_sec Number of seconds the queue should hold the message before attempting
189 * to allow the message to resend. Typically set due to gray listing.
190 * \param[in] thunk Thunk parameter provided during create.
191 */
192typedef void (*M_net_smtp_reschedule_cb)(const char *msg, M_uint64 wait_sec, void *thunk);
193
194
195/*! Callback to set additional I/O layers on the internal network request I/O object.
196 *
197 * The primary use for this callback is to add tracing or bandwidth shaping. TLS
198 * should not be added here because it is handled internally.
199 *
200 * Due to connections being in a dynamic pool, the callback may be called multiple times.
201 *
202 * \param[in] io The base I/O object to add layers on top of.
203 * \param[in] error Error buffer to set a textual error message when returning a failure response.
204 * \param[in] errlen Size of error buffer.
205 * \param[in] thunk Thunk parameter provided during create.
206 *
207 * \return M_TRUE on success. M_FALSE if setting up the I/O object failed and the operation should abort.
208 */
209typedef M_bool (*M_net_smtp_iocreate_cb)(M_io_t *io, char *error, size_t errlen, void *thunk);
210
211
212/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
213
214/*! Structure of callbacks to inform and control operating behavior. */
225};
226
227
228/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
229
230/*! Create an SMTP pool.
231 *
232 * \param[in] el Event loop to operate on.
233 * \param[in] cbs Callbacks for getting information about state and controlling behavior.
234 * \param[in] thunk Optional thunk passed to callbacks.
235 *
236 * \return SMTP network object.
237 */
238M_API M_net_smtp_t *M_net_smtp_create(M_event_t *el, const struct M_net_smtp_callbacks *cbs, void *thunk);
239
240
241/*! Destroy an SMTP pool.
242 *
243 * \param[in] sp SMTP pool.
244 */
246
247
248/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
249
250/*! Pause processing.
251 *
252 * \param[in] sp SMTP pool.
253 */
255
256
257/*! Resume processing.
258 *
259 * \param[in] sp SMTP pool.
260 *
261 * \return M_TRUE if resumed. Otherwise M_FALSE. Can return M_FALSE for
262 * conditions, such as, no end points.
263 */
265
266
267/*! Get the status of the SMTP pool.
268 *
269 * \param[in] sp SMTP pool.
270 *
271 * \return Status.
272 */
274
275
276/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
277
278/*! Setup parameters for TCP endpoints.
279 *
280 * This must be called before any TCP end points can be added.
281 *
282 * It is highly recommend a TLS client context be provided even
283 * It is possible for the server to request a TLS connection
284 * due to START TLS. It is also required if a TLS only connection
285 * endpoint is configured.
286 *
287 * \param[in] sp SMTP pool.
288 * \param[in] dns DNS object. Must be valid for the duration of this object's life.
289 * \param[in] ctx The TLS client context. The context does not have to persist after being set here.
290 */
292
293
294/*! Setup timeout parameters for TCP endpoints.
295 *
296 * \param[in] sp SMTP pool.
297 * \param[in] connect_ms Connect timeout in milliseconds. Will trigger when a connection
298 * has not been established within this time.
299 * \param[in] stall_ms Stall timeout in milliseconds. Will trigger when the time between read
300 * and write events has been exceeded. This helps prevent a server from causing
301 * a denial of service by sending 1 byte at a time with a large internal between
302 * each one.
303 * \param[in] idle_ms Overall time the connection can be idle before being closed. 0 will cause the
304 * connection to be closed after a single message.
305 */
306M_API void M_net_smtp_setup_tcp_timeouts(M_net_smtp_t *sp, M_uint64 connect_ms, M_uint64 stall_ms, M_uint64 idle_ms);
307
308
309/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
310
311/*! Add a TCP endpoint.
312 *
313 * \param[in] sp SMTP pool.
314 * \param[in] address Address of server.
315 * \param[in] port Port to connect on. If 0 will use port 25.
316 * \param[in] connect_tls Server requires a TLS connection and the connection.
317 * \param[in] username Authentication username.
318 * \param[in] password Authentication password.
319 * \param[in] max_conns Maximum connections to this server that should be opened.
320 * Scales up to max based on the number of messages queued.
321 *
322 * \return M_TRUE if the end point was added. Otherwise M_FALSE. Will always return M_FALSE
323 * if M_net_smtp_setup_tcp was not called and provided with DNS or if M_net_smtp_setup_tcp
324 * was called without a TLS context and connect_tls is set.
325 */
326M_API M_bool M_net_smtp_add_endpoint_tcp(M_net_smtp_t *sp, const char *address, M_uint16 port, M_bool connect_tls, const char *username, const char *password, size_t max_conns);
327
328
329/*! Add a process endpoint.
330 *
331 * \param[in] sp SMTP pool.
332 * \param[in] command Command to send message using. Must accept message as STDIN.
333 * \param[in] args Optional. List of arguments to pass to command.
334 * \param[in] env Optional. List of environment variables to pass on to process. Use NULL to pass current environment through.
335 * \param[in] timeout_ms Optional. Maximum execution time of the process before it is forcibly terminated. Use 0 for infinite.
336 * \param[in] max_processes Optional. Maximum number of processes to open simultaneously. Default is 1.
337 *
338 * \return M_TRUE if the endpoint was added. Otherwise, M_FALSE.
339 */
340M_API M_bool M_net_smtp_add_endpoint_process(M_net_smtp_t *sp, const char *command, const M_list_str_t *args, const M_hash_dict_t *env, M_uint64 timeout_ms, size_t max_processes);
341
342
343/*! Set how the pool should handle multiple endpoints.
344 *
345 * \param[in] sp SMTP pool.
346 * \param[in] mode Load balancing method that should be used.
347 */
349
350
351/*! Number of resend attempts allowed per message.
352 *
353 * Only applies to internal queue processing.
354 *
355 * \param[in] sp SMTP pool.
356 * \param[in] num Number of send attempts per message.
357 */
358M_API void M_net_smtp_set_num_attempts(M_net_smtp_t *sp, size_t num);
359
360/*! Number of consecutive stall timeouts allowed per endpoint.
361 *
362 * \param[in] sp SMTP pool.
363 * \param[in] num Number of stall retries per endpoint. Can be 0 to fail endpoint on first stall.
364 */
365M_API void M_net_smtp_set_stall_retries(M_net_smtp_t *sp, size_t num);
366
367
368/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
369
370/*! Remove all queued messages from the queue.
371 *
372 * \param[in] sp SMTP pool.
373 *
374 * It is recommended to call M_net_smtp_pause
375 * and wait until the status is stopped before calling
376 * this function.
377 *
378 * \return List of messages that were queued. Will not
379 * include messages that are currently processing.
380 */
382
383
384/*! Add an email object to the queue.
385 *
386 * \param[in] sp SMTP pool.
387 * \param[in] e Email message.
388 *
389 * \return M_TRUE if the message was added. Otherwise, M_FALSE.
390 */
391M_API M_bool M_net_smtp_queue_smtp(M_net_smtp_t *sp, const M_email_t *e);
392
393
394/*! Add an email message as a string to the queue.
395 *
396 * \param[in] sp SMTP pool.
397 * \param[in] e Message.
398 *
399 * \return M_TRUE if the message was added. Otherwise, M_FALSE.
400 */
401M_API M_bool M_net_smtp_queue_message(M_net_smtp_t *sp, const char *e);
402
403
404/*! Tell the pool to use an external queue.
405 *
406 * Can only be called when the queue is empty.
407 * Once an external queue is setup, the internal queue cannot be used.
408 *
409 * \param[in] sp SMTP pool.
410 * \param[in] get_cb Callback used by the pool to get messages from the queue.
411 * Callback should return NULL if no messages are available.
412 *
413 * \return M_TRUE if the external queue was set.
414 */
415M_API M_bool M_net_smtp_use_external_queue(M_net_smtp_t *sp, char *(*get_cb)(void));
416
417
418/*! Tell the pool messages are available in the external queue.
419 *
420 * The pool will run though messages in the queue until no more messages are available
421 * However, the pool does not know when messages have been added to the external
422 * queue. It is up to the queue manager to inform the pool messages are available
423 * to process. It is recommended this be called after one or more messages are
424 * added.
425 *
426 * \param[in] sp SMTP pool.
427 */
429
430/*! @} */
431
432__END_DECLS
433
434#endif /* __M_NET_SMTP_H__ */
struct M_dns M_dns_t
Definition: m_dns.h:43
struct M_email M_email_t
Definition: m_email.h:109
struct M_event M_event_t
Definition: m_event.h:210
struct M_hash_dict M_hash_dict_t
Definition: m_hash_dict.h:52
struct M_io M_io_t
Definition: m_io.h:59
struct M_list_str M_list_str_t
Definition: m_list_str.h:80
M_net_error_t
Definition: m_net.h:44
M_net_smtp_connect_fail_cb connect_fail_cb
Definition: m_net_smtp.h:217
M_net_smtp_iocreate_cb iocreate_cb
Definition: m_net_smtp.h:224
M_net_smtp_sent_cb sent_cb
Definition: m_net_smtp.h:221
M_net_smtp_process_fail_cb process_fail_cb
Definition: m_net_smtp.h:219
M_net_smtp_reschedule_cb reschedule_cb
Definition: m_net_smtp.h:223
M_net_smtp_connect_cb connect_cb
Definition: m_net_smtp.h:216
M_net_smtp_disconnect_cb disconnect_cb
Definition: m_net_smtp.h:218
M_net_smtp_processing_halted_cb processing_halted_cb
Definition: m_net_smtp.h:220
M_net_smtp_send_failed_cb send_failed_cb
Definition: m_net_smtp.h:222
M_net_smtp_status_t M_net_smtp_status(const M_net_smtp_t *sp)
M_bool(* M_net_smtp_send_failed_cb)(const M_hash_dict_t *headers, const char *error, size_t attempt_num, M_bool can_requeue, void *thunk)
Definition: m_net_smtp.h:179
void M_net_smtp_setup_tcp(M_net_smtp_t *sp, M_dns_t *dns, M_tls_clientctx_t *ctx)
M_bool M_net_smtp_load_balance(M_net_smtp_t *sp, M_net_smtp_load_balance_t mode)
M_uint64(* M_net_smtp_process_fail_cb)(const char *command, int result_code, const char *proc_stdout, const char *proc_stderror, void *thunk)
Definition: m_net_smtp.h:140
M_bool M_net_smtp_queue_smtp(M_net_smtp_t *sp, const M_email_t *e)
void M_net_smtp_external_queue_have_messages(M_net_smtp_t *sp)
M_net_smtp_load_balance_t
Definition: m_net_smtp.h:70
M_bool(* M_net_smtp_iocreate_cb)(M_io_t *io, char *error, size_t errlen, void *thunk)
Definition: m_net_smtp.h:209
void M_net_smtp_set_stall_retries(M_net_smtp_t *sp, size_t num)
M_bool M_net_smtp_add_endpoint_tcp(M_net_smtp_t *sp, const char *address, M_uint16 port, M_bool connect_tls, const char *username, const char *password, size_t max_conns)
M_uint64(* M_net_smtp_processing_halted_cb)(M_bool no_endpoints, void *thunk)
Definition: m_net_smtp.h:152
M_uint64(* M_net_smtp_connect_fail_cb)(const char *address, M_uint16 port, M_net_error_t net_err, const char *error, void *thunk)
Definition: m_net_smtp.h:108
M_net_smtp_status_t
Definition: m_net_smtp.h:58
void M_net_smtp_setup_tcp_timeouts(M_net_smtp_t *sp, M_uint64 connect_ms, M_uint64 stall_ms, M_uint64 idle_ms)
M_list_str_t * M_net_smtp_dump_queue(M_net_smtp_t *sp)
void(* M_net_smtp_connect_cb)(const char *address, M_uint16 port, void *thunk)
Definition: m_net_smtp.h:89
void M_net_smtp_destroy(M_net_smtp_t *sp)
void(* M_net_smtp_disconnect_cb)(const char *address, M_uint16 port, void *thunk)
Definition: m_net_smtp.h:124
M_bool M_net_smtp_resume(M_net_smtp_t *sp)
void M_net_smtp_pause(M_net_smtp_t *sp)
M_bool M_net_smtp_use_external_queue(M_net_smtp_t *sp, char *(*get_cb)(void))
void M_net_smtp_set_num_attempts(M_net_smtp_t *sp, size_t num)
M_net_smtp_t * M_net_smtp_create(M_event_t *el, const struct M_net_smtp_callbacks *cbs, void *thunk)
M_bool M_net_smtp_add_endpoint_process(M_net_smtp_t *sp, const char *command, const M_list_str_t *args, const M_hash_dict_t *env, M_uint64 timeout_ms, size_t max_processes)
struct M_net_smtp M_net_smtp_t
Definition: m_net_smtp.h:52
void(* M_net_smtp_sent_cb)(const M_hash_dict_t *headers, void *thunk)
Definition: m_net_smtp.h:161
void(* M_net_smtp_reschedule_cb)(const char *msg, M_uint64 wait_sec, void *thunk)
Definition: m_net_smtp.h:192
M_bool M_net_smtp_queue_message(M_net_smtp_t *sp, const char *e)
@ M_NET_SMTP_LOAD_BALANCE_FAILOVER
Definition: m_net_smtp.h:71
@ M_NET_SMTP_LOAD_BALANCE_ROUNDROBIN
Definition: m_net_smtp.h:73
@ M_NET_SMTP_STATUS_NOENDPOINTS
Definition: m_net_smtp.h:62
@ M_NET_SMTP_STATUS_STOPPING
Definition: m_net_smtp.h:63
@ M_NET_SMTP_STATUS_STOPPED
Definition: m_net_smtp.h:61
@ M_NET_SMTP_STATUS_PROCESSING
Definition: m_net_smtp.h:60
@ M_NET_SMTP_STATUS_IDLE
Definition: m_net_smtp.h:59
Definition: m_net_smtp.h:215
struct M_tls_clientctx M_tls_clientctx_t
Definition: m_tls.h:45