1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 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 
25 #include "private-lib-core.h"
26 #include "private-lib-abstract.h"
27 
28 /** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */
29 typedef enum lwsgs_smtp_states {
30 	LGSSMTP_IDLE,		/**< awaiting new email */
31 	LGSSMTP_CONNECTING,	/**< opening tcp connection to MTA */
32 	LGSSMTP_CONNECTED,	/**< tcp connection to MTA is connected */
33 		/* (server sends greeting) */
34 	LGSSMTP_SENT_HELO,	/**< sent the HELO */
35 
36 	LGSSMTP_SENT_FROM,	/**< sent FROM */
37 	LGSSMTP_SENT_TO,	/**< sent TO */
38 	LGSSMTP_SENT_DATA,	/**< sent DATA request */
39 	LGSSMTP_SENT_BODY,	/**< sent the email body */
40 
41 		/*
42 		 * (server sends, eg, "250 Ok: queued as 12345")
43 		 * at this point we can return to LGSSMTP_SENT_HELO and send a
44 		 * new email, or continue below to QUIT, or just wait
45 		 */
46 
47 	LGSSMTP_SENT_QUIT,	/**< sent the session quit */
48 
49 	/* (server sends, eg, "221 Bye" and closes the connection) */
50 } lwsgs_smtp_states_t;
51 
52 /** abstract protocol instance data */
53 
54 typedef struct lws_smtp_client_protocol {
55 	const struct lws_abs	*abs;
56 	lwsgs_smtp_states_t	estate;
57 
58 	lws_smtp_email_t	*e;	/* the email we are trying to send */
59 	const char		*helo;
60 
61 	unsigned char		send_pending:1;
62 } lws_smtpcp_t;
63 
64 static const short retcodes[] = {
65 	0,	/* idle */
66 	0,	/* connecting */
67 	220,	/* connected */
68 	250,	/* helo */
69 	250,	/* from */
70 	250,	/* to */
71 	354,	/* data */
72 	250,	/* body */
73 	221,	/* quit */
74 };
75 
76 static void
lws_smtpc_state_transition(lws_smtpcp_t * c,lwsgs_smtp_states_t s)77 lws_smtpc_state_transition(lws_smtpcp_t *c, lwsgs_smtp_states_t s)
78 {
79 	lwsl_debug("%s: cli %p: state %d -> %d\n", __func__, c, c->estate, s);
80 	c->estate = s;
81 }
82 
83 static lws_smtp_email_t *
lws_smtpc_get_email(lws_smtpcp_t * c)84 lws_smtpc_get_email(lws_smtpcp_t *c)
85 {
86 	const lws_token_map_t *tm;
87 
88 	/* ... the email we want to send */
89 	tm = lws_abs_get_token(c->abs->ap_tokens, LTMI_PSMTP_V_LWS_SMTP_EMAIL_T);
90 	if (!tm) {
91 		assert(0);
92 
93 		return NULL;
94 	}
95 
96 	return (lws_smtp_email_t *)tm->u.value;
97 }
98 
99 /*
100  * Called when something happened so that we know now the final disposition of
101  * the email send attempt, for good or ill.
102  *
103  * Inform the owner via the done callback and set up the next queued one if any.
104  *
105  * Returns nonzero if we queued a new one
106  */
107 
108 static int
lws_smtpc_email_disposition(lws_smtpcp_t * c,int disp,const void * buf,size_t len)109 lws_smtpc_email_disposition(lws_smtpcp_t *c, int disp, const void *buf,
110 			    size_t len)
111 {
112 	lws_smtpcp_t *ch;
113 	lws_abs_t *ach;
114 	lws_dll2_t *d;
115 
116 	lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO);
117 
118 	/* lifetime of the email object is handled by done callback */
119 	c->e->done(c->e, c->e->data, disp, buf, len);
120 	c->e = NULL;
121 
122 	/* this may not be the time to try to send anything else... */
123 
124 	if (disp == LWS_SMTP_DISPOSITION_FAILED_DESTROY)
125 		return 0;
126 
127 	/* ... otherwise... do we have another queued? */
128 
129 	d = lws_dll2_get_tail(&c->abs->children_owner);
130 	if (!d)
131 		return 0;
132 
133 	ach = lws_container_of(d, lws_abs_t, bound);
134 	ch = (lws_smtpcp_t *)ach->api;
135 
136 	c->e = lws_smtpc_get_email(ch);
137 
138 	/* since we took it on, remove it from the queue */
139 	lws_dll2_remove(d);
140 
141 	return 1;
142 }
143 
144 /*
145  * we became connected
146  */
147 
148 static int
lws_smtpc_abs_accept(lws_abs_protocol_inst_t * api)149 lws_smtpc_abs_accept(lws_abs_protocol_inst_t *api)
150 {
151 	lws_smtpcp_t *c = (lws_smtpcp_t *)api;
152 
153 	/* we have become connected in the tcp sense */
154 
155 	lws_smtpc_state_transition(c, LGSSMTP_CONNECTED);
156 
157 	/*
158 	 * From the accept(), the next thing that should happen is the SMTP
159 	 * server sends its greeting like "220 smtp2.example.com ESMTP Postfix",
160 	 * we'll hear about it in the rx callback, or time out
161 	 */
162 
163 	c->abs->at->set_timeout(c->abs->ati,
164 				PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, 3);
165 
166 	return 0;
167 }
168 
169 static int
lws_smtpc_abs_rx(lws_abs_protocol_inst_t * api,const uint8_t * buf,size_t len)170 lws_smtpc_abs_rx(lws_abs_protocol_inst_t *api, const uint8_t *buf, size_t len)
171 {
172 	lws_smtpcp_t *c = (lws_smtpcp_t *)api;
173 	char dotstar[96], at[5];
174 	int n;
175 
176 	c->abs->at->set_timeout(c->abs->ati, NO_PENDING_TIMEOUT, 0);
177 
178 	lws_strncpy(at, (const char *)buf, sizeof(at));
179 	n = atoi(at);
180 
181 	switch (c->estate) {
182 	case LGSSMTP_CONNECTED:
183 		if (n != 220) {
184 			/*
185 			 * The server did not properly greet us... we can't
186 			 * even get started, so fail the transport connection
187 			 * (and anything queued on it)
188 			 */
189 
190 			lws_strnncpy(dotstar, (const char *)buf, len, sizeof(dotstar));
191 			lwsl_err("%s: server: %s\n", __func__, dotstar);
192 
193 			return 1;
194 		}
195 		break;
196 
197 	case LGSSMTP_SENT_BODY:
198 		/*
199 		 * We finished one way or another... let's prepare to send a
200 		 * new one... or wait until server hangs up on us
201 		 */
202 		if (!lws_smtpc_email_disposition(c,
203 					n == 250 ? LWS_SMTP_DISPOSITION_SENT :
204 						   LWS_SMTP_DISPOSITION_FAILED,
205 					"destroyed", 0))
206 			return 0; /* become idle */
207 
208 		break; /* ask to send */
209 
210 	case LGSSMTP_SENT_QUIT:
211 		lwsl_debug("%s: done\n", __func__);
212 		lws_smtpc_state_transition(c, LGSSMTP_IDLE);
213 
214 		return 1;
215 
216 	default:
217 		if (n != retcodes[c->estate]) {
218 			lws_strnncpy(dotstar, buf, len, sizeof(dotstar));
219 			lwsl_notice("%s: bad response: %d (state %d) %s\n",
220 				    __func__, n, c->estate, dotstar);
221 
222 			lws_smtpc_email_disposition(c,
223 					LWS_SMTP_DISPOSITION_FAILED, buf, len);
224 
225 			return 0;
226 		}
227 		break;
228 	}
229 
230 	c->send_pending = 1;
231 	c->abs->at->ask_for_writeable(c->abs->ati);
232 
233 	return 0;
234 }
235 
236 static int
lws_smtpc_abs_writeable(lws_abs_protocol_inst_t * api,size_t budget)237 lws_smtpc_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget)
238 {
239 	char b[256 + LWS_PRE], *p = b + LWS_PRE;
240 	lws_smtpcp_t *c = (lws_smtpcp_t *)api;
241 	int n;
242 
243 	if (!c->send_pending || !c->e)
244 		return 0;
245 
246 	c->send_pending = 0;
247 
248 	lwsl_debug("%s: writing response for state %d\n", __func__, c->estate);
249 
250 	switch (c->estate) {
251 	case LGSSMTP_CONNECTED:
252 		n = lws_snprintf(p, sizeof(b) - LWS_PRE, "HELO %s\n", c->helo);
253 		lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO);
254 		break;
255 
256 	case LGSSMTP_SENT_HELO:
257 		n = lws_snprintf(p, sizeof(b) - LWS_PRE, "MAIL FROM: <%s>\n",
258 				 c->e->from);
259 		lws_smtpc_state_transition(c, LGSSMTP_SENT_FROM);
260 		break;
261 
262 	case LGSSMTP_SENT_FROM:
263 		n = lws_snprintf(p, sizeof(b) - LWS_PRE,
264 				 "RCPT TO: <%s>\n", c->e->to);
265 		lws_smtpc_state_transition(c, LGSSMTP_SENT_TO);
266 		break;
267 
268 	case LGSSMTP_SENT_TO:
269 		n = lws_snprintf(p, sizeof(b) - LWS_PRE, "DATA\n");
270 		lws_smtpc_state_transition(c, LGSSMTP_SENT_DATA);
271 		break;
272 
273 	case LGSSMTP_SENT_DATA:
274 		p = (char *)&c->e[1];
275 		n = strlen(p);
276 		lws_smtpc_state_transition(c, LGSSMTP_SENT_BODY);
277 		break;
278 
279 	case LGSSMTP_SENT_BODY:
280 		n = lws_snprintf(p, sizeof(b) - LWS_PRE, "quit\n");
281 		lws_smtpc_state_transition(c, LGSSMTP_SENT_QUIT);
282 		break;
283 
284 	case LGSSMTP_SENT_QUIT:
285 		return 0;
286 
287 	default:
288 		return 0;
289 	}
290 
291 	//puts(p);
292 	c->abs->at->tx(c->abs->ati, (uint8_t *)p, n);
293 
294 	return 0;
295 }
296 
297 static int
lws_smtpc_abs_closed(lws_abs_protocol_inst_t * api)298 lws_smtpc_abs_closed(lws_abs_protocol_inst_t *api)
299 {
300 	lws_smtpcp_t *c = (lws_smtpcp_t *)api;
301 
302 	if (c)
303 		lws_smtpc_state_transition(c, LGSSMTP_IDLE);
304 
305 	return 0;
306 }
307 
308 /*
309  * Creating for initial transport and for piggybacking on another transport
310  * both get created here the same.  But piggybackers have ai->bound attached.
311  */
312 
313 static int
lws_smtpc_create(const lws_abs_t * ai)314 lws_smtpc_create(const lws_abs_t *ai)
315 {
316 	lws_smtpcp_t *c = (lws_smtpcp_t *)ai->api;
317 
318 	memset(c, 0, sizeof(*c));
319 
320 	c->abs = ai;
321 	c->e = lws_smtpc_get_email(c);
322 
323 	lws_smtpc_state_transition(c, lws_dll2_is_detached(&ai->bound) ?
324 					LGSSMTP_CONNECTING : LGSSMTP_IDLE);
325 
326 	/* If we are initiating the transport, we will get an accept() next...
327 	 *
328 	 * If we are piggybacking, the parent will get a .child_bind() after
329 	 * this to give it a chance to act on us joining (eg, it was completely
330 	 * idle and we joined).
331 	 */
332 
333 	return 0;
334 }
335 
336 static void
lws_smtpc_destroy(lws_abs_protocol_inst_t ** _c)337 lws_smtpc_destroy(lws_abs_protocol_inst_t **_c)
338 {
339 	lws_smtpcp_t *c = (lws_smtpcp_t *)*_c;
340 
341 	if (!c)
342 		return;
343 
344 	/* so if we are still holding on to c->e, we have failed to send it */
345 	if (c->e)
346 		lws_smtpc_email_disposition(c,
347 			LWS_SMTP_DISPOSITION_FAILED_DESTROY, "destroyed", 0);
348 
349 	*_c = NULL;
350 }
351 
352 static int
lws_smtpc_compare(lws_abs_t * abs1,lws_abs_t * abs2)353 lws_smtpc_compare(lws_abs_t *abs1, lws_abs_t *abs2)
354 {
355 	return 0;
356 }
357 
358 static int
lws_smtpc_child_bind(lws_abs_t * abs)359 lws_smtpc_child_bind(lws_abs_t *abs)
360 {
361 	return 0;
362 }
363 
364 /* events the transport invokes (handled by abstract protocol) */
365 
366 const lws_abs_protocol_t lws_abs_protocol_smtp = {
367 	.name		= "smtp",
368 	.alloc		= sizeof(lws_smtpcp_t),
369 	.flags		= LWSABSPR_FLAG_PIPELINE,
370 
371 	.create		= lws_smtpc_create,
372 	.destroy	= lws_smtpc_destroy,
373 	.compare	= lws_smtpc_compare,
374 
375 	.accept		= lws_smtpc_abs_accept,
376 	.rx		= lws_smtpc_abs_rx,
377 	.writeable	= lws_smtpc_abs_writeable,
378 	.closed		= lws_smtpc_abs_closed,
379 	.heartbeat	= NULL,
380 
381 	.child_bind	= lws_smtpc_child_bind,
382 };
383