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