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