1 /*
2 * Abstract SMTP support for libwebsockets - SMTP sequencer
3 *
4 * Copyright (C) 2016-2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * This sequencer sits above the abstract protocol, and manages queueing,
25 * retrying mail transmission, and retry limits.
26 *
27 * Having the sequencer means that, eg, we can manage retries after complete
28 * connection failure.
29 *
30 * Connections to the smtp server are serialized
31 */
32
33 #include "private-lib-core.h"
34 #include "private-lib-abstract-protocols-smtp.h"
35
36 typedef enum {
37 LSMTPSS_DISCONNECTED,
38 LSMTPSS_CONNECTING,
39 LSMTPSS_CONNECTED,
40 LSMTPSS_BUSY,
41 } smtpss_connstate_t;
42
43 typedef struct lws_smtp_sequencer {
44 struct lws_dll2_owner emails_owner; /* email queue */
45
46 lws_abs_t *abs, *instance;
47 lws_smtp_sequencer_args_t args;
48 struct lws_sequencer *seq;
49
50 smtpss_connstate_t connstate;
51
52 time_t email_connect_started;
53
54 /* holds the HELO for the smtp protocol to consume */
55 lws_token_map_t apt[3];
56 } lws_smtp_sequencer_t;
57
58 /* sequencer messages specific to this sequencer */
59
60 enum {
61 SEQ_MSG_CLIENT_FAILED = LWSSEQ_USER_BASE,
62 SEQ_MSG_CLIENT_DONE,
63 };
64
65 /*
66 * We're going to bind to the raw-skt transport, so tell that what we want it
67 * to connect to
68 */
69
70 static const lws_token_map_t smtp_rs_transport_tokens[] = {
71 {
72 .u = { .value = "127.0.0.1" },
73 .name_index = LTMI_PEER_V_DNS_ADDRESS,
74 }, {
75 .u = { .lvalue = 25 },
76 .name_index = LTMI_PEER_LV_PORT,
77 }, {
78 }
79 };
80
81 static void
lws_smtpc_kick_internal(lws_smtp_sequencer_t * s)82 lws_smtpc_kick_internal(lws_smtp_sequencer_t *s)
83 {
84 lws_smtp_email_t *e;
85 lws_dll2_t *d;
86 char buf[64];
87 int n;
88 lws_dll2_t *pd2;
89
90 pd2 = lws_dll2_get_head(&s->emails_owner);
91 if (!pd2)
92 return;
93
94 e = lws_container_of(pd2, lws_smtp_email_t, list);
95 if (!e)
96 return;
97
98 /* Is there something to do? If so, we need a connection... */
99
100 if (s->connstate == LSMTPSS_DISCONNECTED) {
101
102 s->apt[0].u.value = s->args.helo;
103 s->apt[0].name_index = LTMI_PSMTP_V_HELO;
104 s->apt[1].u.value = (void *)e;
105 s->apt[1].name_index = LTMI_PSMTP_V_LWS_SMTP_EMAIL_T;
106
107 /*
108 * create and connect the smtp protocol + transport
109 */
110
111 s->abs = lws_abstract_alloc(s->args.vhost, NULL, "smtp.raw_skt",
112 s->apt, smtp_rs_transport_tokens,
113 s->seq, NULL);
114 if (!s->abs)
115 return;
116
117 s->instance = lws_abs_bind_and_create_instance(s->abs);
118 if (!s->instance) {
119 lws_abstract_free(&s->abs);
120 lwsl_err("%s: failed to create SMTP client\n", __func__);
121
122 goto bail1;
123 }
124
125 s->connstate = LSMTPSS_CONNECTING;
126 lws_seq_timeout_us(s->seq, 10 * LWS_USEC_PER_SEC);
127 return;
128 }
129
130 /* ask the transport if we have a connection to the server ongoing */
131
132 if (s->abs->at->state(s->abs->ati)) {
133 /*
134 * there's a connection, it could be still trying to connect
135 * or established
136 */
137 s->abs->at->ask_for_writeable(s->abs->ati);
138
139 return;
140 }
141
142 /* there's no existing connection */
143
144 lws_smtpc_state_transition(c, LGSSMTP_CONNECTING);
145
146 if (s->abs->at->client_conn(s->abs)) {
147 lwsl_err("%s: failed to connect\n", __func__);
148
149 return;
150 }
151
152 e->tries++;
153 e->last_try = lws_now_secs();
154 }
155
156
157 /*
158 * The callback we get from the smtp protocol... we use this to drive
159 * decisions about destroy email, retry and fail.
160 *
161 * Sequencer will handle it via the event loop.
162 */
163
164 static int
email_result(void * e,void * d,int disp,void * b,size_t l)165 email_result(void *e, void *d, int disp, void *b, size_t l)
166 {
167 lws_smtp_sequencer_t *s = (lws_smtp_sequencer_t *)d;
168
169 lws_sequencer_event(s->seq, LWSSEQ_USER_BASE + disp, e);
170
171 return 0;
172 }
173
174 static int
cleanup(struct lws_dll2 * d,void * user)175 cleanup(struct lws_dll2 *d, void *user)
176 {
177 lws_smtp_email_t *e;
178
179 e = lws_container_of(d, lws_smtp_email_t, list);
180 if (e->done)
181 e->done(e, "destroying", 10);
182
183 lws_dll2_remove(d);
184 lws_free(e);
185
186 return 0;
187 }
188
189 static lws_seq_cb_return_t
smtp_sequencer_cb(struct lws_sequencer * seq,void * user,int event,void * data)190 smtp_sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data)
191 {
192 struct lws_smtp_sequencer_t *s = (struct lws_smtp_sequencer_t *)user;
193
194 switch ((int)event) {
195 case LWSSEQ_CREATED: /* our sequencer just got started */
196 lwsl_notice("%s: %s: created\n", __func__,
197 lws_sequencer_name(seq));
198 s->connstate = LSMTPSS_DISCONNECTED;
199 s->state = 0; /* first thing we'll do is the first url */
200 goto step;
201
202 case LWSSEQ_DESTROYED:
203 lws_dll2_foreach_safe(&s->pending_owner, NULL, cleanup);
204 break;
205
206 case LWSSEQ_TIMED_OUT:
207 lwsl_notice("%s: LWSSEQ_TIMED_OUT\n", __func__);
208 break;
209
210 case LWSSEQ_USER_BASE + LWS_SMTP_DISPOSITION_SENT:
211 lws_smtpc_free_email(data);
212 break;
213
214 case LWSSEQ_WSI_CONNECTED:
215 s->connstate = LSMTPSS_CONNECTED;
216 lws_smtpc_kick_internal(s);
217 break;
218
219 case LWSSEQ_WSI_CONN_FAIL:
220 case LWSSEQ_WSI_CONN_CLOSE:
221 s->connstate = LSMTPSS_DISCONNECTED;
222 lws_smtpc_kick_internal(s);
223 break;
224
225 case SEQ_MSG_SENT:
226 break;
227
228 default:
229 break;
230 }
231
232 return LWSSEQ_RET_CONTINUE;
233 }
234
235 /*
236 * Creates an lws_sequencer to manage the test sequence
237 */
238
239 lws_smtp_sequencer_t *
lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t * args)240 lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t *args)
241 {
242 lws_smtp_sequencer_t *s;
243 struct lws_sequencer *seq;
244
245 /*
246 * Create a sequencer in the event loop to manage the SMTP queue
247 */
248
249 seq = lws_sequencer_create(args->vhost->context, 0,
250 sizeof(lws_smtp_sequencer_t), (void **)&s,
251 smtp_sequencer_cb, "smtp-seq");
252 if (!seq) {
253 lwsl_err("%s: unable to create sequencer\n", __func__);
254 return NULL;
255 }
256
257 s->abs = *args->abs;
258 s->args = *args;
259 s->seq = seq;
260
261 /* set defaults in our copy of the args */
262
263 if (!s->args.helo[0])
264 strcpy(s->args.helo, "default-helo");
265 if (!s->args.email_queue_max)
266 s->args.email_queue_max = 8;
267 if (!s->args.retry_interval)
268 s->args.retry_interval = 15 * 60;
269 if (!s->args.delivery_timeout)
270 s->args.delivery_timeout = 12 * 60 * 60;
271
272 return s;
273 }
274
275 void
lws_smtp_sequencer_destroy(lws_smtp_sequencer_t * s)276 lws_smtp_sequencer_destroy(lws_smtp_sequencer_t *s)
277 {
278 /* sequencer destruction destroys all assets */
279 lws_sequencer_destroy(&s->seq);
280 }
281
282 int
lws_smtpc_add_email(lws_smtp_sequencer_t * s,const char * payload,size_t payload_len,const char * sender,const char * recipient,void * data,lws_smtp_cb_t done)283 lws_smtpc_add_email(lws_smtp_sequencer_t *s, const char *payload,
284 size_t payload_len, const char *sender,
285 const char *recipient, void *data, lws_smtp_cb_t done)
286 {
287 lws_smtp_email_t *e;
288
289 if (s->emails_owner.count > s->args.email_queue_max) {
290 lwsl_err("%s: email queue at limit of %d\n", __func__,
291 (int)s->args.email_queue_max);
292
293 return 1;
294 }
295
296 if (!done)
297 return 1;
298
299 e = malloc(sizeof(*e) + payload_len + 1);
300 if (!e)
301 return 1;
302
303 memset(e, 0, sizeof(*e));
304
305 e->data = data;
306 e->done = done;
307
308 lws_strncpy(e->from, sender, sizeof(e->from));
309 lws_strncpy(e->to, recipient, sizeof(e->to));
310
311 memcpy((char *)&e[1], payload, payload_len + 1);
312
313 e->added = lws_now_secs();
314 e->last_try = 0;
315 e->tries = 0;
316
317 lws_dll2_clear(&e->list);
318 lws_dll2_add_tail(&e->list, &s->emails_owner);
319
320 lws_smtpc_kick_internal(s);
321
322 return 0;
323 }
324