BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file
3 * SMTP client module
4 *
5 * Author: Simon Goldschmidt
6 *
7 * @defgroup smtp SMTP client
8 * @ingroup apps
9 *
10 * This is simple SMTP client for raw API.
11 * It is a minimal implementation of SMTP as specified in RFC 5321.
12 *
13 * Example usage:
14 @code{.c}
15 void my_smtp_result_fn(void *arg, u8_t smtp_result, u16_t srv_err, err_t err)
16 {
17 printf("mail (%p) sent with results: 0x%02x, 0x%04x, 0x%08x\n", arg,
18 smtp_result, srv_err, err);
19 }
20 static void my_smtp_test(void)
21 {
22 smtp_set_server_addr("mymailserver.org");
23 -> set both username and password as NULL if no auth needed
24 smtp_set_auth("username", "password");
25 smtp_send_mail("sender", "recipient", "subject", "body", my_smtp_result_fn,
26 some_argument);
27 }
28 @endcode
29  
30 * When using from any other thread than the tcpip_thread (for NO_SYS==0), use
31 * smtp_send_mail_int()!
32 *
33 * SMTP_BODYDH usage:
34 @code{.c}
35 int my_smtp_bodydh_fn(void *arg, struct smtp_bodydh *bdh)
36 {
37 if(bdh->state >= 10) {
38 return BDH_DONE;
39 }
40 sprintf(bdh->buffer,"Line #%2d\r\n",bdh->state);
41 bdh->length = strlen(bdh->buffer);
42 ++bdh->state;
43 return BDH_WORKING;
44 }
45  
46 smtp_send_mail_bodycback("sender", "recipient", "subject",
47 my_smtp_bodydh_fn, my_smtp_result_fn, some_argument);
48 @endcode
49 *
50 * @todo:
51 * - attachments (the main difficulty here is streaming base64-encoding to
52 * prevent having to allocate a buffer for the whole encoded file at once)
53 * - test with more mail servers...
54 *
55 */
56  
57 #include "lwip/apps/smtp.h"
58  
59 #if LWIP_TCP && LWIP_CALLBACK_API
60 #include "lwip/sys.h"
61 #include "lwip/sockets.h"
62 #include "lwip/altcp.h"
63 #include "lwip/dns.h"
64 #include "lwip/mem.h"
65 #include "lwip/altcp_tcp.h"
66 #include "lwip/altcp_tls.h"
67  
68 #include <string.h> /* strnlen, memcpy */
69 #include <stdlib.h>
70  
71 /** TCP poll interval. Unit is 0.5 sec. */
72 #define SMTP_POLL_INTERVAL 4
73 /** TCP poll timeout while sending message body, reset after every
74 * successful write. 3 minutes */
75 #define SMTP_TIMEOUT_DATABLOCK ( 3 * 60 * SMTP_POLL_INTERVAL / 2)
76 /** TCP poll timeout while waiting for confirmation after sending the body.
77 * 10 minutes */
78 #define SMTP_TIMEOUT_DATATERM (10 * 60 * SMTP_POLL_INTERVAL / 2)
79 /** TCP poll timeout while not sending the body.
80 * This is somewhat lower than the RFC states (5 minutes for initial, MAIL
81 * and RCPT) but still OK for us here.
82 * 2 minutes */
83 #define SMTP_TIMEOUT ( 2 * 60 * SMTP_POLL_INTERVAL / 2)
84  
85 /* the various debug levels for this file */
86 #define SMTP_DEBUG_TRACE (SMTP_DEBUG | LWIP_DBG_TRACE)
87 #define SMTP_DEBUG_STATE (SMTP_DEBUG | LWIP_DBG_STATE)
88 #define SMTP_DEBUG_WARN (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
89 #define SMTP_DEBUG_WARN_STATE (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
90 #define SMTP_DEBUG_SERIOUS (SMTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
91  
92  
93 #define SMTP_RX_BUF_LEN 255
94 #define SMTP_TX_BUF_LEN 255
95 #define SMTP_CRLF "\r\n"
96 #define SMTP_CRLF_LEN 2
97  
98 #define SMTP_RESP_220 "220"
99 #define SMTP_RESP_235 "235"
100 #define SMTP_RESP_250 "250"
101 #define SMTP_RESP_334 "334"
102 #define SMTP_RESP_354 "354"
103 #define SMTP_RESP_LOGIN_UNAME "VXNlcm5hbWU6"
104 #define SMTP_RESP_LOGIN_PASS "UGFzc3dvcmQ6"
105  
106 #define SMTP_KEYWORD_AUTH_SP "AUTH "
107 #define SMTP_KEYWORD_AUTH_EQ "AUTH="
108 #define SMTP_KEYWORD_AUTH_LEN 5
109 #define SMTP_AUTH_PARAM_PLAIN "PLAIN"
110 #define SMTP_AUTH_PARAM_LOGIN "LOGIN"
111  
112 #define SMTP_CMD_EHLO_1 "EHLO ["
113 #define SMTP_CMD_EHLO_1_LEN 6
114 #define SMTP_CMD_EHLO_2 "]\r\n"
115 #define SMTP_CMD_EHLO_2_LEN 3
116 #define SMTP_CMD_AUTHPLAIN_1 "AUTH PLAIN "
117 #define SMTP_CMD_AUTHPLAIN_1_LEN 11
118 #define SMTP_CMD_AUTHPLAIN_2 "\r\n"
119 #define SMTP_CMD_AUTHPLAIN_2_LEN 2
120 #define SMTP_CMD_AUTHLOGIN "AUTH LOGIN\r\n"
121 #define SMTP_CMD_AUTHLOGIN_LEN 12
122 #define SMTP_CMD_MAIL_1 "MAIL FROM: <"
123 #define SMTP_CMD_MAIL_1_LEN 12
124 #define SMTP_CMD_MAIL_2 ">\r\n"
125 #define SMTP_CMD_MAIL_2_LEN 3
126 #define SMTP_CMD_RCPT_1 "RCPT TO: <"
127 #define SMTP_CMD_RCPT_1_LEN 10
128 #define SMTP_CMD_RCPT_2 ">\r\n"
129 #define SMTP_CMD_RCPT_2_LEN 3
130 #define SMTP_CMD_DATA "DATA\r\n"
131 #define SMTP_CMD_DATA_LEN 6
132 #define SMTP_CMD_HEADER_1 "From: <"
133 #define SMTP_CMD_HEADER_1_LEN 7
134 #define SMTP_CMD_HEADER_2 ">\r\nTo: <"
135 #define SMTP_CMD_HEADER_2_LEN 8
136 #define SMTP_CMD_HEADER_3 ">\r\nSubject: "
137 #define SMTP_CMD_HEADER_3_LEN 12
138 #define SMTP_CMD_HEADER_4 "\r\n\r\n"
139 #define SMTP_CMD_HEADER_4_LEN 4
140 #define SMTP_CMD_BODY_FINISHED "\r\n.\r\n"
141 #define SMTP_CMD_BODY_FINISHED_LEN 5
142 #define SMTP_CMD_QUIT "QUIT\r\n"
143 #define SMTP_CMD_QUIT_LEN 6
144  
145 #if defined(SMTP_STAT_TX_BUF_MAX) && SMTP_STAT_TX_BUF_MAX
146 #define SMTP_TX_BUF_MAX(len) LWIP_MACRO(if((len) > smtp_tx_buf_len_max) smtp_tx_buf_len_max = (len);)
147 #else /* SMTP_STAT_TX_BUF_MAX */
148 #define SMTP_TX_BUF_MAX(len)
149 #endif /* SMTP_STAT_TX_BUF_MAX */
150  
151 #if SMTP_COPY_AUTHDATA
152 #define SMTP_USERNAME(session) (session)->username
153 #define SMTP_PASS(session) (session)->pass
154 #define SMTP_AUTH_PLAIN_DATA(session) (session)->auth_plain
155 #define SMTP_AUTH_PLAIN_LEN(session) (session)->auth_plain_len
156 #else /* SMTP_COPY_AUTHDATA */
157 #define SMTP_USERNAME(session) smtp_username
158 #define SMTP_PASS(session) smtp_pass
159 #define SMTP_AUTH_PLAIN_DATA(session) smtp_auth_plain
160 #define SMTP_AUTH_PLAIN_LEN(session) smtp_auth_plain_len
161 #endif /* SMTP_COPY_AUTHDATA */
162  
163 #if SMTP_BODYDH
164 #ifndef SMTP_BODYDH_MALLOC
165 #define SMTP_BODYDH_MALLOC(size) mem_malloc(size)
166 #define SMTP_BODYDH_FREE(ptr) mem_free(ptr)
167 #endif
168  
169 /* Some internal state return values */
170 #define BDHALLDATASENT 2
171 #define BDHSOMEDATASENT 1
172  
173 enum bdh_handler_state {
174 BDH_SENDING, /* Serving the user function generating body content */
175 BDH_STOP /* User function stopped, closing */
176 };
177 #endif
178  
179 /** State for SMTP client state machine */
180 enum smtp_session_state {
181 SMTP_NULL,
182 SMTP_HELO,
183 SMTP_AUTH_PLAIN,
184 SMTP_AUTH_LOGIN_UNAME,
185 SMTP_AUTH_LOGIN_PASS,
186 SMTP_AUTH_LOGIN,
187 SMTP_MAIL,
188 SMTP_RCPT,
189 SMTP_DATA,
190 SMTP_BODY,
191 SMTP_QUIT,
192 SMTP_CLOSED
193 };
194  
195 #ifdef LWIP_DEBUG
196 /** State-to-string table for debugging */
197 static const char *smtp_state_str[] = {
198 "SMTP_NULL",
199 "SMTP_HELO",
200 "SMTP_AUTH_PLAIN",
201 "SMTP_AUTH_LOGIN_UNAME",
202 "SMTP_AUTH_LOGIN_PASS",
203 "SMTP_AUTH_LOGIN",
204 "SMTP_MAIL",
205 "SMTP_RCPT",
206 "SMTP_DATA",
207 "SMTP_BODY",
208 "SMTP_QUIT",
209 "SMTP_CLOSED",
210 };
211  
212 static const char *smtp_result_strs[] = {
213 "SMTP_RESULT_OK",
214 "SMTP_RESULT_ERR_UNKNOWN",
215 "SMTP_RESULT_ERR_CONNECT",
216 "SMTP_RESULT_ERR_HOSTNAME",
217 "SMTP_RESULT_ERR_CLOSED",
218 "SMTP_RESULT_ERR_TIMEOUT",
219 "SMTP_RESULT_ERR_SVR_RESP",
220 "SMTP_RESULT_ERR_MEM"
221 };
222 #endif /* LWIP_DEBUG */
223  
224 #if SMTP_BODYDH
225 struct smtp_bodydh_state {
226 smtp_bodycback_fn callback_fn; /* The function to call (again) */
227 u16_t state;
228 struct smtp_bodydh exposed; /* the user function structure */
229 };
230 #endif /* SMTP_BODYDH */
231  
232 /** struct keeping the body and state of an smtp session */
233 struct smtp_session {
234 /** keeping the state of the smtp session */
235 enum smtp_session_state state;
236 /** timeout handling, if this reaches 0, the connection is closed */
237 u16_t timer;
238 /** helper buffer for transmit, not used for sending body */
239 char tx_buf[SMTP_TX_BUF_LEN + 1];
240 struct pbuf* p;
241 /** source email address */
242 const char* from;
243 /** size of the sourceemail address */
244 u16_t from_len;
245 /** target email address */
246 const char* to;
247 /** size of the target email address */
248 u16_t to_len;
249 /** subject of the email */
250 const char *subject;
251 /** length of the subject string */
252 u16_t subject_len;
253 /** this is the body of the mail to be sent */
254 const char* body;
255 /** this is the length of the body to be sent */
256 u16_t body_len;
257 /** amount of data from body already sent */
258 u16_t body_sent;
259 /** callback function to call when closed */
260 smtp_result_fn callback_fn;
261 /** argument for callback function */
262 void *callback_arg;
263 #if SMTP_COPY_AUTHDATA
264 /** Username to use for this request */
265 char *username;
266 /** Password to use for this request */
267 char *pass;
268 /** Username and password combined as necessary for PLAIN authentication */
269 char auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
270 /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
271 size_t auth_plain_len;
272 #endif /* SMTP_COPY_AUTHDATA */
273 #if SMTP_BODYDH
274 struct smtp_bodydh_state *bodydh;
275 #endif /* SMTP_BODYDH */
276 };
277  
278 /** IP address or DNS name of the server to use for next SMTP request */
279 static char smtp_server[SMTP_MAX_SERVERNAME_LEN + 1];
280 /** TCP port of the server to use for next SMTP request */
281 static u16_t smtp_server_port = SMTP_DEFAULT_PORT;
282 #if LWIP_ALTCP && LWIP_ALTCP_TLS
283 /** If this is set, mail is sent using SMTPS */
284 static struct altcp_tls_config *smtp_server_tls_config;
285 #endif
286 /** Username to use for the next SMTP request */
287 static char *smtp_username;
288 /** Password to use for the next SMTP request */
289 static char *smtp_pass;
290 /** Username and password combined as necessary for PLAIN authentication */
291 static char smtp_auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
292 /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
293 static size_t smtp_auth_plain_len;
294  
295 static err_t smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed);
296 static err_t smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err);
297 static void smtp_tcp_err(void *arg, err_t err);
298 static err_t smtp_tcp_poll(void *arg, struct altcp_pcb *pcb);
299 static err_t smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len);
300 static err_t smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err);
301 #if LWIP_DNS
302 static void smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg);
303 #endif /* LWIP_DNS */
304 static size_t smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len);
305 static enum smtp_session_state smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len);
306 static void smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb);
307 static void smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p);
308 #if SMTP_BODYDH
309 static void smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb);
310 #endif /* SMTP_BODYDH */
311  
312  
313 #ifdef LWIP_DEBUG
314 /** Convert an smtp result to a string */
315 const char*
316 smtp_result_str(u8_t smtp_result)
317 {
318 if (smtp_result >= LWIP_ARRAYSIZE(smtp_result_strs)) {
319 return "UNKNOWN";
320 }
321 return smtp_result_strs[smtp_result];
322 }
323  
324 /** Null-terminates the payload of p for printing out messages.
325 * WARNING: use this only if p is not needed any more as the last byte of
326 * payload is deleted!
327 */
328 static const char*
329 smtp_pbuf_str(struct pbuf* p)
330 {
331 if ((p == NULL) || (p->len == 0)) {
332 return "";
333 }
334 ((char*)p->payload)[p->len] = 0;
335 return (const char*)p->payload;
336 }
337 #endif /* LWIP_DEBUG */
338  
339 /** @ingroup smtp
340 * Set IP address or DNS name for next SMTP connection
341 *
342 * @param server IP address (in ASCII representation) or DNS name of the server
343 */
344 err_t
345 smtp_set_server_addr(const char* server)
346 {
347 size_t len = 0;
348 if (server != NULL) {
349 /* strnlen: returns length WITHOUT terminating 0 byte OR
350 * SMTP_MAX_SERVERNAME_LEN+1 when string is too long */
351 len = strnlen(server, SMTP_MAX_SERVERNAME_LEN+1);
352 }
353 if (len > SMTP_MAX_SERVERNAME_LEN) {
354 return ERR_MEM;
355 }
356 if (len != 0) {
357 MEMCPY(smtp_server, server, len);
358 }
359 smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */
360 return ERR_OK;
361 }
362  
363 /** @ingroup smtp
364 * Set TCP port for next SMTP connection
365 *
366 * @param port TCP port
367 */
368 void
369 smtp_set_server_port(u16_t port)
370 {
371 smtp_server_port = port;
372 }
373  
374 #if LWIP_ALTCP && LWIP_ALTCP_TLS
375 /** @ingroup smtp
376 * Set TLS configuration for next SMTP connection
377 *
378 * @param tls_config TLS configuration
379 */
380 void
381 smtp_set_tls_config(struct altcp_tls_config *tls_config)
382 {
383 smtp_server_tls_config = tls_config;
384 }
385 #endif
386  
387 /** @ingroup smtp
388 * Set authentication parameters for next SMTP connection
389 *
390 * @param username login name as passed to the server
391 * @param pass password passed to the server together with username
392 */
393 err_t
394 smtp_set_auth(const char* username, const char* pass)
395 {
396 size_t uname_len = 0;
397 size_t pass_len = 0;
398  
399 memset(smtp_auth_plain, 0xfa, 64);
400 if (username != NULL) {
401 uname_len = strlen(username);
402 if (uname_len > SMTP_MAX_USERNAME_LEN) {
403 LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n",
404 (int)uname_len, SMTP_MAX_USERNAME_LEN));
405 return ERR_ARG;
406 }
407 }
408 if (pass != NULL) {
409 #if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN
410 pass_len = strlen(pass);
411 if (pass_len > SMTP_MAX_PASS_LEN) {
412 LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n",
413 (int)uname_len, SMTP_MAX_USERNAME_LEN));
414 return ERR_ARG;
415 }
416 #else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
417 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n"));
418 #endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
419 }
420 *smtp_auth_plain = 0;
421 if (username != NULL) {
422 smtp_username = smtp_auth_plain + 1;
423 strcpy(smtp_username, username);
424 }
425 if (pass != NULL) {
426 smtp_pass = smtp_auth_plain + uname_len + 2;
427 strcpy(smtp_pass, pass);
428 }
429 smtp_auth_plain_len = uname_len + pass_len + 2;
430  
431 return ERR_OK;
432 }
433  
434 #if SMTP_BODYDH
435 static void smtp_free_struct(struct smtp_session *s)
436 {
437 if (s->bodydh != NULL) {
438 SMTP_BODYDH_FREE(s->bodydh);
439 }
440 SMTP_STATE_FREE(s);
441 }
442 #else /* SMTP_BODYDH */
443 #define smtp_free_struct(x) SMTP_STATE_FREE(x)
444 #endif /* SMTP_BODYDH */
445  
446 static struct altcp_pcb*
447 smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip)
448 {
449 struct altcp_pcb* pcb;
450 LWIP_UNUSED_ARG(remote_ip);
451  
452 pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip));
453 if (pcb != NULL) {
454 #if LWIP_ALTCP && LWIP_ALTCP_TLS
455 if (smtp_server_tls_config) {
456 struct altcp_pcb *pcb_tls = altcp_tls_new(smtp_server_tls_config, pcb);
457 if (pcb_tls == NULL) {
458 altcp_close(pcb);
459 return NULL;
460 }
461 pcb = pcb_tls;
462 }
463 #endif
464 altcp_arg(pcb, s);
465 altcp_recv(pcb, smtp_tcp_recv);
466 altcp_err(pcb, smtp_tcp_err);
467 altcp_poll(pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL);
468 altcp_sent(pcb, smtp_tcp_sent);
469 }
470 return pcb;
471 }
472  
473 /** The actual mail-sending function, called by smtp_send_mail and
474 * smtp_send_mail_static after setting up the struct smtp_session.
475 */
476 static err_t
477 smtp_send_mail_alloced(struct smtp_session *s)
478 {
479 err_t err;
480 struct altcp_pcb* pcb = NULL;
481 ip_addr_t addr;
482  
483 LWIP_ASSERT("no smtp_session supplied", s != NULL);
484  
485 #if SMTP_CHECK_DATA
486 /* check that body conforms to RFC:
487 * - convert all single-CR or -LF in body to CRLF
488 * - only 7-bit ASCII is allowed
489 */
490 if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) {
491 err = ERR_ARG;
492 goto leave;
493 }
494 if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) {
495 err = ERR_ARG;
496 goto leave;
497 }
498 if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) {
499 err = ERR_ARG;
500 goto leave;
501 }
502 #if SMTP_BODYDH
503 if (s->bodydh == NULL)
504 #endif /* SMTP_BODYDH */
505 {
506 if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) {
507 err = ERR_ARG;
508 goto leave;
509 }
510 }
511 #endif /* SMTP_CHECK_DATA */
512  
513 #if SMTP_COPY_AUTHDATA
514 /* copy auth data, ensuring the first byte is always zero */
515 MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1);
516 s->auth_plain_len = smtp_auth_plain_len;
517 /* default username and pass is empty string */
518 s->username = s->auth_plain;
519 s->pass = s->auth_plain;
520 if (smtp_username != NULL) {
521 s->username += smtp_username - smtp_auth_plain;
522 }
523 if (smtp_pass != NULL) {
524 s->pass += smtp_pass - smtp_auth_plain;
525 }
526 #endif /* SMTP_COPY_AUTHDATA */
527  
528 s->state = SMTP_NULL;
529 s->timer = SMTP_TIMEOUT;
530  
531 #if LWIP_DNS
532 err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s);
533 #else /* LWIP_DNS */
534 err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG;
535 #endif /* LWIP_DNS */
536 if (err == ERR_OK) {
537 pcb = smtp_setup_pcb(s, &addr);
538 if (pcb == NULL) {
539 err = ERR_MEM;
540 goto leave;
541 }
542 err = altcp_connect(pcb, &addr, smtp_server_port, smtp_tcp_connected);
543 if (err != ERR_OK) {
544 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
545 goto deallocate_and_leave;
546 }
547 } else if (err != ERR_INPROGRESS) {
548 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err));
549 goto deallocate_and_leave;
550 }
551 return ERR_OK;
552  
553 deallocate_and_leave:
554 if (pcb != NULL) {
555 altcp_arg(pcb, NULL);
556 altcp_close(pcb);
557 }
558 leave:
559 smtp_free_struct(s);
560 /* no need to call the callback here since we return != ERR_OK */
561 return err;
562 }
563  
564 /** @ingroup smtp
565 * Send an email via the currently selected server, username and password.
566 *
567 * @param from source email address (must be NULL-terminated)
568 * @param to target email address (must be NULL-terminated)
569 * @param subject email subject (must be NULL-terminated)
570 * @param body email body (must be NULL-terminated)
571 * @param callback_fn callback function
572 * @param callback_arg user argument to callback_fn
573 * @returns - ERR_OK if structures were allocated and no error occured starting the connection
574 * (this does not mean the email has been successfully sent!)
575 * - another err_t on error.
576 */
577 err_t
578 smtp_send_mail(const char* from, const char* to, const char* subject, const char* body,
579 smtp_result_fn callback_fn, void* callback_arg)
580 {
581 struct smtp_session* s;
582 size_t from_len = strlen(from);
583 size_t to_len = strlen(to);
584 size_t subject_len = strlen(subject);
585 size_t body_len = strlen(body);
586 size_t mem_len = sizeof(struct smtp_session);
587 char *sfrom, *sto, *ssubject, *sbody;
588  
589 mem_len += from_len + to_len + subject_len + body_len + 4;
590 if (mem_len > 0xffff) {
591 /* too long! */
592 return ERR_MEM;
593 }
594  
595 /* Allocate memory to keep this email's session state */
596 s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len);
597 if (s == NULL) {
598 return ERR_MEM;
599 }
600 /* initialize the structure */
601 memset(s, 0, mem_len);
602 s->from = sfrom = (char*)s + sizeof(struct smtp_session);
603 s->from_len = (u16_t)from_len;
604 s->to = sto = sfrom + from_len + 1;
605 s->to_len = (u16_t)to_len;
606 s->subject = ssubject = sto + to_len + 1;
607 s->subject_len = (u16_t)subject_len;
608 s->body = sbody = ssubject + subject_len + 1;
609 s->body_len = (u16_t)body_len;
610 /* copy source and target email address */
611 /* cast to size_t is a hack to cast away constness */
612 MEMCPY(sfrom, from, from_len + 1);
613 MEMCPY(sto, to, to_len + 1);
614 MEMCPY(ssubject, subject, subject_len + 1);
615 MEMCPY(sbody, body, body_len + 1);
616  
617 s->callback_fn = callback_fn;
618 s->callback_arg = callback_arg;
619  
620 /* call the actual implementation of this function */
621 return smtp_send_mail_alloced(s);
622 }
623  
624 /** @ingroup smtp
625 * Same as smtp_send_mail, but doesn't copy from, to, subject and body into
626 * an internal buffer to save memory.
627 * WARNING: the above data must stay untouched until the callback function is
628 * called (unless the function returns != ERR_OK)
629 */
630 err_t
631 smtp_send_mail_static(const char *from, const char* to, const char* subject,
632 const char* body, smtp_result_fn callback_fn, void* callback_arg)
633 {
634 struct smtp_session* s;
635 size_t len;
636  
637 s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
638 if (s == NULL) {
639 return ERR_MEM;
640 }
641 memset(s, 0, sizeof(struct smtp_session));
642 /* initialize the structure */
643 s->from = from;
644 len = strlen(from);
645 LWIP_ASSERT("string is too long", len <= 0xffff);
646 s->from_len = (u16_t)len;
647 s->to = to;
648 len = strlen(to);
649 LWIP_ASSERT("string is too long", len <= 0xffff);
650 s->to_len = (u16_t)len;
651 s->subject = subject;
652 len = strlen(subject);
653 LWIP_ASSERT("string is too long", len <= 0xffff);
654 s->subject_len = (u16_t)len;
655 s->body = body;
656 len = strlen(body);
657 LWIP_ASSERT("string is too long", len <= 0xffff);
658 s->body_len = (u16_t)len;
659 s->callback_fn = callback_fn;
660 s->callback_arg = callback_arg;
661 /* call the actual implementation of this function */
662 return smtp_send_mail_alloced(s);
663 }
664  
665  
666 /** @ingroup smtp
667 * Same as smtp_send_mail but takes a struct smtp_send_request as single
668 * parameter which contains all the other parameters.
669 * To be used with tcpip_callback to send mail from interrupt context or from
670 * another thread.
671 *
672 * WARNING: server and authentication must stay untouched until this function has run!
673 *
674 * Usage example:
675 * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context)
676 * - fill the members of the struct as if calling smtp_send_mail
677 * - specify a callback_function
678 * - set callback_arg to the structure itself
679 * - call this function
680 * - wait for the callback function to be called
681 * - in the callback function, deallocate the structure (passed as arg)
682 */
683 void
684 smtp_send_mail_int(void *arg)
685 {
686 struct smtp_send_request *req = (struct smtp_send_request*)arg;
687 err_t err;
688  
689 LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL);
690  
691 if (req->static_data) {
692 err = smtp_send_mail_static(req->from, req->to, req->subject, req->body,
693 req->callback_fn, req->callback_arg);
694 } else {
695 err = smtp_send_mail(req->from, req->to, req->subject, req->body,
696 req->callback_fn, req->callback_arg);
697 }
698 if ((err != ERR_OK) && (req->callback_fn != NULL)) {
699 req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err);
700 }
701 }
702  
703 #if SMTP_CHECK_DATA
704 /** Verify that a given string conforms to the SMTP rules
705 * (7-bit only, no single CR or LF,
706 * @todo: no line consisting of a single dot only)
707 */
708 static err_t
709 smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed)
710 {
711 size_t i;
712 u8_t last_was_cr = 0;
713 for (i = 0; i < data_len; i++) {
714 char current = data[i];
715 if ((current & 0x80) != 0) {
716 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: no 8-bit data supported: %s\n", data));
717 return ERR_ARG;
718 }
719 if (current == '\r') {
720 if (!linebreaks_allowed) {
721 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found CR where no linebreaks allowed: %s\n", data));
722 return ERR_ARG;
723 }
724 if (last_was_cr) {
725 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found double CR: %s\n", data));
726 return ERR_ARG;
727 }
728 last_was_cr = 1;
729 } else {
730 if (current == '\n') {
731 if (!last_was_cr) {
732 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found LF without CR before: %s\n", data));
733 return ERR_ARG;
734 }
735 }
736 last_was_cr = 0;
737 }
738 }
739 return ERR_OK;
740 }
741 #endif /* SMTP_CHECK_DATA */
742  
743 /** Frees the smtp_session and calls the callback function */
744 static void
745 smtp_free(struct smtp_session *s, u8_t result, u16_t srv_err, err_t err)
746 {
747 smtp_result_fn fn = s->callback_fn;
748 void *arg = s->callback_arg;
749 if (s->p != NULL) {
750 pbuf_free(s->p);
751 }
752 smtp_free_struct(s);
753 if (fn != NULL) {
754 fn(arg, result, srv_err, err);
755 }
756 }
757  
758 /** Try to close a pcb and free the arg if successful */
759 static void
760 smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result,
761 u16_t srv_err, err_t err)
762 {
763 if (pcb != NULL) {
764 altcp_arg(pcb, NULL);
765 if (altcp_close(pcb) == ERR_OK) {
766 if (s != NULL) {
767 smtp_free(s, result, srv_err, err);
768 }
769 } else {
770 /* close failed, set back arg */
771 altcp_arg(pcb, s);
772 }
773 } else {
774 if (s != NULL) {
775 smtp_free(s, result, srv_err, err);
776 }
777 }
778 }
779  
780 /** Raw API TCP err callback: pcb is already deallocated */
781 static void
782 smtp_tcp_err(void *arg, err_t err)
783 {
784 LWIP_UNUSED_ARG(err);
785 if (arg != NULL) {
786 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_err: connection reset by remote host\n"));
787 smtp_free((struct smtp_session*)arg, SMTP_RESULT_ERR_CLOSED, 0, err);
788 }
789 }
790  
791 /** Raw API TCP poll callback */
792 static err_t
793 smtp_tcp_poll(void *arg, struct altcp_pcb *pcb)
794 {
795 if (arg != NULL) {
796 struct smtp_session *s = (struct smtp_session*)arg;
797 if (s->timer != 0) {
798 s->timer--;
799 }
800 }
801 smtp_process(arg, pcb, NULL);
802 return ERR_OK;
803 }
804  
805 /** Raw API TCP sent callback */
806 static err_t
807 smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
808 {
809 LWIP_UNUSED_ARG(len);
810  
811 smtp_process(arg, pcb, NULL);
812  
813 return ERR_OK;
814 }
815  
816 /** Raw API TCP recv callback */
817 static err_t
818 smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
819 {
820 LWIP_UNUSED_ARG(err);
821 if (p != NULL) {
822 altcp_recved(pcb, p->tot_len);
823 smtp_process(arg, pcb, p);
824 } else {
825 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_recv: connection closed by remote host\n"));
826 smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CLOSED, 0, err);
827 }
828 return ERR_OK;
829 }
830  
831 static err_t
832 smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
833 {
834 LWIP_UNUSED_ARG(arg);
835  
836 if (err == ERR_OK) {
837 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_connected: Waiting for 220\n"));
838 } else {
839 /* shouldn't happen, but we still check 'err', only to be sure */
840 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_connected: %d\n", (int)err));
841 smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CONNECT, 0, err);
842 }
843 return ERR_OK;
844 }
845  
846 #if LWIP_DNS
847 /** DNS callback
848 * If ipaddr is non-NULL, resolving succeeded, otherwise it failed.
849 */
850 static void
851 smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
852 {
853 struct smtp_session *s = (struct smtp_session*)arg;
854 struct altcp_pcb *pcb;
855 err_t err;
856 u8_t result;
857  
858 LWIP_UNUSED_ARG(hostname);
859  
860 if (ipaddr != NULL) {
861 pcb = smtp_setup_pcb(s, ipaddr);
862 if (pcb != NULL) {
863 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: hostname resolved, connecting\n"));
864 err = altcp_connect(pcb, ipaddr, smtp_server_port, smtp_tcp_connected);
865 if (err == ERR_OK) {
866 return;
867 }
868 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
869 result = SMTP_RESULT_ERR_CONNECT;
870 } else {
871 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: failed to allocate tcp pcb\n"));
872 result = SMTP_RESULT_ERR_MEM;
873 err = ERR_MEM;
874 }
875 } else {
876 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_dns_found: failed to resolve hostname: %s\n",
877 hostname));
878 pcb = NULL;
879 result = SMTP_RESULT_ERR_HOSTNAME;
880 err = ERR_ARG;
881 }
882 smtp_close(s, pcb, result, 0, err);
883 }
884 #endif /* LWIP_DNS */
885  
886 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
887  
888 /** Table 6-bit-index-to-ASCII used for base64-encoding */
889 static const char base64_table[] = {
890 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
891 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
892 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
893 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
894 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
895 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
896 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
897 '+', '/'
898 };
899  
900 /** Base64 encoding */
901 static size_t
902 smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len)
903 {
904 size_t i;
905 s8_t j;
906 size_t target_idx = 0;
907 size_t longer = (source_len % 3) ? (3 - (source_len % 3)) : 0;
908 size_t source_len_b64 = source_len + longer;
909 size_t len = (((source_len_b64) * 4) / 3);
910 u8_t x = 5;
911 u8_t current = 0;
912 LWIP_UNUSED_ARG(target_len);
913  
914 LWIP_ASSERT("target_len is too short", target_len >= len);
915  
916 for (i = 0; i < source_len_b64; i++) {
917 u8_t b = (i < source_len ? (u8_t)source[i] : 0);
918 for (j = 7; j >= 0; j--, x--) {
919 if ((b & (1 << j)) != 0) {
920 current = (u8_t)(current | (1U << x));
921 }
922 if (x == 0) {
923 target[target_idx++] = base64_table[current];
924 x = 6;
925 current = 0;
926 }
927 }
928 }
929 for (i = len - longer; i < len; i++) {
930 target[i] = '=';
931 }
932 return len;
933 }
934 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
935  
936 /** Parse pbuf to see if it contains the beginning of an answer.
937 * If so, it returns the contained response code as number between 1 and 999.
938 * If not, zero is returned.
939 *
940 * @param s smtp session struct
941 */
942 static u16_t
943 smtp_is_response(struct smtp_session *s)
944 {
945 char digits[4];
946 long num;
947  
948 if (s->p == NULL) {
949 return 0;
950 }
951 /* copy three digits and convert them to int */
952 if (pbuf_copy_partial(s->p, digits, 3, 0) != 3) {
953 /* pbuf was too short */
954 return 0;
955 }
956 digits[3] = 0;
957 num = strtol(digits, NULL, 10);
958 if ((num <= 0) || (num >= 1000)) {
959 /* failed to find response code at start of line */
960 return 0;
961 }
962 return (u16_t)num;
963 }
964  
965 /** Parse pbuf to see if it contains a fully received answer.
966 * If one is found, ERR_OK is returned.
967 * If none is found, ERR_VAL is returned.
968 *
969 * A fully received answer is a 3-digit number followed by a space,
970 * some string and a CRLF as line ending.
971 *
972 * @param s smtp session struct
973 */
974 static err_t
975 smtp_is_response_finished(struct smtp_session *s)
976 {
977 u8_t sp;
978 u16_t crlf;
979 u16_t offset;
980  
981 if (s->p == NULL) {
982 return ERR_VAL;
983 }
984 offset = 0;
985 again:
986 /* We could check the response number here, but we trust the
987 * protocol definition which says the client can rely on it being
988 * the same on every line. */
989  
990 /* find CRLF */
991 if (offset > 0xFFFF - 4) {
992 /* would overflow */
993 return ERR_VAL;
994 }
995 crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, (u16_t)(offset + 4));
996 if (crlf == 0xFFFF) {
997 /* no CRLF found */
998 return ERR_VAL;
999 }
1000 sp = pbuf_get_at(s->p, (u16_t)(offset + 3));
1001 if (sp == '-') {
1002 /* no space after response code -> try next line */
1003 offset = (u16_t)(crlf + 2);
1004 if (offset < crlf) {
1005 /* overflow */
1006 return ERR_VAL;
1007 }
1008 goto again;
1009 } else if (sp == ' ') {
1010 /* CRLF found after response code + space -> valid response */
1011 return ERR_OK;
1012 }
1013 /* sp contains invalid character */
1014 return ERR_VAL;
1015 }
1016  
1017 /** Prepare HELO/EHLO message */
1018 static enum smtp_session_state
1019 smtp_prepare_helo(struct smtp_session *s, u16_t *tx_buf_len, struct altcp_pcb *pcb)
1020 {
1021 size_t ipa_len;
1022 const char *ipa = ipaddr_ntoa(altcp_get_ip(pcb, 1));
1023 LWIP_ASSERT("ipaddr_ntoa returned NULL", ipa != NULL);
1024 ipa_len = strlen(ipa);
1025 LWIP_ASSERT("string too long", ipa_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_EHLO_1_LEN-SMTP_CMD_EHLO_2_LEN));
1026  
1027 *tx_buf_len = (u16_t)(SMTP_CMD_EHLO_1_LEN + (u16_t)ipa_len + SMTP_CMD_EHLO_2_LEN);
1028 LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
1029  
1030 SMEMCPY(s->tx_buf, SMTP_CMD_EHLO_1, SMTP_CMD_EHLO_1_LEN);
1031 MEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN], ipa, ipa_len);
1032 SMEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN + ipa_len], SMTP_CMD_EHLO_2, SMTP_CMD_EHLO_2_LEN);
1033 return SMTP_HELO;
1034 }
1035  
1036 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
1037 /** Parse last server response (in rx_buf) for supported authentication method,
1038 * create data to send out (to tx_buf), set tx_data_len correctly
1039 * and return the next state.
1040 */
1041 static enum smtp_session_state
1042 smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len)
1043 {
1044 /* check response for supported authentication method */
1045 u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP);
1046 if (auth == 0xFFFF) {
1047 auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ);
1048 }
1049 if (auth != 0xFFFF) {
1050 u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth);
1051 if ((crlf != 0xFFFF) && (crlf > auth)) {
1052 /* use tx_buf temporarily */
1053 u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, (u16_t)(crlf - auth), auth);
1054 if (copied != 0) {
1055 char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN;
1056 s->tx_buf[copied] = 0;
1057 #if SMTP_SUPPORT_AUTH_PLAIN
1058 /* favour PLAIN over LOGIN since it involves less requests */
1059 if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) {
1060 size_t auth_len;
1061 /* server supports AUTH PLAIN */
1062 SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN);
1063  
1064 /* add base64-encoded string "\0username\0password" */
1065 auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN],
1066 SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s),
1067 SMTP_AUTH_PLAIN_LEN(s));
1068 LWIP_ASSERT("string too long", auth_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_AUTHPLAIN_1_LEN-SMTP_CMD_AUTHPLAIN_2_LEN));
1069 *tx_buf_len = (u16_t)(SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len);
1070 SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2,
1071 SMTP_CMD_AUTHPLAIN_2_LEN);
1072 return SMTP_AUTH_PLAIN;
1073 } else
1074 #endif /* SMTP_SUPPORT_AUTH_PLAIN */
1075 {
1076 #if SMTP_SUPPORT_AUTH_LOGIN
1077 if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) {
1078 /* server supports AUTH LOGIN */
1079 *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN;
1080 SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN);
1081 return SMTP_AUTH_LOGIN_UNAME;
1082 }
1083 #endif /* SMTP_SUPPORT_AUTH_LOGIN */
1084 }
1085 }
1086 }
1087 }
1088 /* server didnt's send correct keywords for AUTH, try sending directly */
1089 return smtp_prepare_mail(s, tx_buf_len);
1090 }
1091 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
1092  
1093 #if SMTP_SUPPORT_AUTH_LOGIN
1094 /** Send base64-encoded username */
1095 static enum smtp_session_state
1096 smtp_prepare_auth_login_uname(struct smtp_session *s, u16_t *tx_buf_len)
1097 {
1098 size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
1099 SMTP_USERNAME(s), strlen(SMTP_USERNAME(s)));
1100 /* @todo: support base64-encoded longer than 64k */
1101 LWIP_ASSERT("string too long", base64_len <= 0xffff);
1102 LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
1103 *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
1104  
1105 SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
1106 s->tx_buf[*tx_buf_len] = 0;
1107 return SMTP_AUTH_LOGIN_PASS;
1108 }
1109  
1110 /** Send base64-encoded password */
1111 static enum smtp_session_state
1112 smtp_prepare_auth_login_pass(struct smtp_session *s, u16_t *tx_buf_len)
1113 {
1114 size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
1115 SMTP_PASS(s), strlen(SMTP_PASS(s)));
1116 /* @todo: support base64-encoded longer than 64k */
1117 LWIP_ASSERT("string too long", base64_len <= 0xffff);
1118 LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
1119 *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
1120  
1121 SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
1122 s->tx_buf[*tx_buf_len] = 0;
1123 return SMTP_AUTH_LOGIN;
1124 }
1125 #endif /* SMTP_SUPPORT_AUTH_LOGIN */
1126  
1127 /** Prepare MAIL message */
1128 static enum smtp_session_state
1129 smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len)
1130 {
1131 char *target = s->tx_buf;
1132 LWIP_ASSERT("tx_buf overflow detected", s->from_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_MAIL_1_LEN - SMTP_CMD_MAIL_2_LEN));
1133 *tx_buf_len = (u16_t)(SMTP_CMD_MAIL_1_LEN + SMTP_CMD_MAIL_2_LEN + s->from_len);
1134 target[*tx_buf_len] = 0;
1135  
1136 SMEMCPY(target, SMTP_CMD_MAIL_1, SMTP_CMD_MAIL_1_LEN);
1137 target += SMTP_CMD_MAIL_1_LEN;
1138 MEMCPY(target, s->from, s->from_len);
1139 target += s->from_len;
1140 SMEMCPY(target, SMTP_CMD_MAIL_2, SMTP_CMD_MAIL_2_LEN);
1141 return SMTP_MAIL;
1142 }
1143  
1144 /** Prepare RCPT message */
1145 static enum smtp_session_state
1146 smtp_prepare_rcpt(struct smtp_session *s, u16_t *tx_buf_len)
1147 {
1148 char *target = s->tx_buf;
1149 LWIP_ASSERT("tx_buf overflow detected", s->to_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_RCPT_1_LEN - SMTP_CMD_RCPT_2_LEN));
1150 *tx_buf_len = (u16_t)(SMTP_CMD_RCPT_1_LEN + SMTP_CMD_RCPT_2_LEN + s->to_len);
1151 target[*tx_buf_len] = 0;
1152  
1153 SMEMCPY(target, SMTP_CMD_RCPT_1, SMTP_CMD_RCPT_1_LEN);
1154 target += SMTP_CMD_RCPT_1_LEN;
1155 MEMCPY(target, s->to, s->to_len);
1156 target += s->to_len;
1157 SMEMCPY(target, SMTP_CMD_RCPT_2, SMTP_CMD_RCPT_2_LEN);
1158 return SMTP_RCPT;
1159 }
1160  
1161 /** Prepare header of body */
1162 static enum smtp_session_state
1163 smtp_prepare_header(struct smtp_session *s, u16_t *tx_buf_len)
1164 {
1165 char *target = s->tx_buf;
1166 int len = SMTP_CMD_HEADER_1_LEN + SMTP_CMD_HEADER_2_LEN +
1167 SMTP_CMD_HEADER_3_LEN + SMTP_CMD_HEADER_4_LEN + s->from_len + s->to_len +
1168 s->subject_len;
1169 LWIP_ASSERT("tx_buf overflow detected", len > 0 && len <= SMTP_TX_BUF_LEN);
1170 *tx_buf_len = (u16_t)len;
1171 target[*tx_buf_len] = 0;
1172  
1173 SMEMCPY(target, SMTP_CMD_HEADER_1, SMTP_CMD_HEADER_1_LEN);
1174 target += SMTP_CMD_HEADER_1_LEN;
1175 MEMCPY(target, s->from, s->from_len);
1176 target += s->from_len;
1177 SMEMCPY(target, SMTP_CMD_HEADER_2, SMTP_CMD_HEADER_2_LEN);
1178 target += SMTP_CMD_HEADER_2_LEN;
1179 MEMCPY(target, s->to, s->to_len);
1180 target += s->to_len;
1181 SMEMCPY(target, SMTP_CMD_HEADER_3, SMTP_CMD_HEADER_3_LEN);
1182 target += SMTP_CMD_HEADER_3_LEN;
1183 MEMCPY(target, s->subject, s->subject_len);
1184 target += s->subject_len;
1185 SMEMCPY(target, SMTP_CMD_HEADER_4, SMTP_CMD_HEADER_4_LEN);
1186  
1187 return SMTP_BODY;
1188 }
1189  
1190 /** Prepare QUIT message */
1191 static enum smtp_session_state
1192 smtp_prepare_quit(struct smtp_session *s, u16_t *tx_buf_len)
1193 {
1194 *tx_buf_len = SMTP_CMD_QUIT_LEN;
1195 s->tx_buf[*tx_buf_len] = 0;
1196 SMEMCPY(s->tx_buf, SMTP_CMD_QUIT, SMTP_CMD_QUIT_LEN);
1197 LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
1198 return SMTP_CLOSED;
1199 }
1200  
1201 /** If in state SMTP_BODY, try to send more body data */
1202 static void
1203 smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb)
1204 {
1205 err_t err;
1206  
1207 if (s->state == SMTP_BODY) {
1208 #if SMTP_BODYDH
1209 if (s->bodydh) {
1210 smtp_send_body_data_handler(s, pcb);
1211 } else
1212 #endif /* SMTP_BODYDH */
1213 {
1214 u16_t send_len = (u16_t)(s->body_len - s->body_sent);
1215 if (send_len > 0) {
1216 u16_t snd_buf = altcp_sndbuf(pcb);
1217 if (send_len > snd_buf) {
1218 send_len = snd_buf;
1219 }
1220 if (send_len > 0) {
1221 /* try to send something out */
1222 err = altcp_write(pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY);
1223 if (err == ERR_OK) {
1224 s->timer = SMTP_TIMEOUT_DATABLOCK;
1225 s->body_sent = (u16_t)(s->body_sent + send_len);
1226 if (s->body_sent < s->body_len) {
1227 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n",
1228 s->body_sent, s->body_len));
1229 }
1230 }
1231 }
1232 }
1233 }
1234 if (s->body_sent == s->body_len) {
1235 /* the whole body has been written, write last line */
1236 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n",
1237 s->body_len));
1238 err = altcp_write(pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0);
1239 if (err == ERR_OK) {
1240 s->timer = SMTP_TIMEOUT_DATATERM;
1241 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n",
1242 smtp_state_str[SMTP_QUIT]));
1243 /* last line written, change state, wait for confirmation */
1244 s->state = SMTP_QUIT;
1245 }
1246 }
1247 }
1248 }
1249  
1250 /** State machine-like implementation of an SMTP client.
1251 */
1252 static void
1253 smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p)
1254 {
1255 struct smtp_session* s = (struct smtp_session*)arg;
1256 u16_t response_code = 0;
1257 u16_t tx_buf_len = 0;
1258 enum smtp_session_state next_state;
1259  
1260 if (arg == NULL) {
1261 /* already closed SMTP connection */
1262 if (p != NULL) {
1263 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n",
1264 p->tot_len, smtp_pbuf_str(p)));
1265 pbuf_free(p);
1266 }
1267 return;
1268 }
1269  
1270 next_state = s->state;
1271  
1272 if (p != NULL) {
1273 /* received data */
1274 if (s->p == NULL) {
1275 s->p = p;
1276 } else {
1277 pbuf_cat(s->p, p);
1278 }
1279 } else {
1280 /* idle timer, close connection if timed out */
1281 if (s->timer == 0) {
1282 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n"));
1283 smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT);
1284 return;
1285 }
1286 if (s->state == SMTP_BODY) {
1287 smtp_send_body(s, pcb);
1288 return;
1289 }
1290 }
1291 response_code = smtp_is_response(s);
1292 if (response_code) {
1293 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code));
1294 if (smtp_is_response_finished(s) != ERR_OK) {
1295 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code));
1296 /* wait for next packet to complete the respone */
1297 return;
1298 }
1299 } else {
1300 if (s->p != NULL) {
1301 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n",
1302 smtp_pbuf_str(s->p)));
1303 pbuf_free(s->p);
1304 s->p = NULL;
1305 }
1306 return;
1307 }
1308  
1309 switch(s->state)
1310 {
1311 case(SMTP_NULL):
1312 /* wait for 220 */
1313 if (response_code == 220) {
1314 /* then send EHLO */
1315 next_state = smtp_prepare_helo(s, &tx_buf_len, pcb);
1316 }
1317 break;
1318 case(SMTP_HELO):
1319 /* wait for 250 */
1320 if (response_code == 250) {
1321 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
1322 /* then send AUTH or MAIL */
1323 next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len);
1324 }
1325 break;
1326 case(SMTP_AUTH_LOGIN):
1327 case(SMTP_AUTH_PLAIN):
1328 /* wait for 235 */
1329 if (response_code == 235) {
1330 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
1331 /* send MAIL */
1332 next_state = smtp_prepare_mail(s, &tx_buf_len);
1333 }
1334 break;
1335 #if SMTP_SUPPORT_AUTH_LOGIN
1336 case(SMTP_AUTH_LOGIN_UNAME):
1337 /* wait for 334 Username */
1338 if (response_code == 334) {
1339 if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) {
1340 /* send username */
1341 next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len);
1342 }
1343 }
1344 break;
1345 case(SMTP_AUTH_LOGIN_PASS):
1346 /* wait for 334 Password */
1347 if (response_code == 334) {
1348 if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) {
1349 /* send username */
1350 next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len);
1351 }
1352 }
1353 break;
1354 #endif /* SMTP_SUPPORT_AUTH_LOGIN */
1355 case(SMTP_MAIL):
1356 /* wait for 250 */
1357 if (response_code == 250) {
1358 /* send RCPT */
1359 next_state = smtp_prepare_rcpt(s, &tx_buf_len);
1360 }
1361 break;
1362 case(SMTP_RCPT):
1363 /* wait for 250 */
1364 if (response_code == 250) {
1365 /* send DATA */
1366 SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN);
1367 tx_buf_len = SMTP_CMD_DATA_LEN;
1368 next_state = SMTP_DATA;
1369 }
1370 break;
1371 case(SMTP_DATA):
1372 /* wait for 354 */
1373 if (response_code == 354) {
1374 /* send email header */
1375 next_state = smtp_prepare_header(s, &tx_buf_len);
1376 }
1377 break;
1378 case(SMTP_BODY):
1379 /* nothing to be done here, handled somewhere else */
1380 break;
1381 case(SMTP_QUIT):
1382 /* wait for 250 */
1383 if (response_code == 250) {
1384 /* send QUIT */
1385 next_state = smtp_prepare_quit(s, &tx_buf_len);
1386 }
1387 break;
1388 case(SMTP_CLOSED):
1389 /* nothing to do, wait for connection closed from server */
1390 return;
1391 default:
1392 LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state,
1393 smtp_state_str[s->state]));
1394 break;
1395 }
1396 if (s->state == next_state) {
1397 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n",
1398 smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
1399 /* close connection */
1400 smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK);
1401 return;
1402 }
1403 if (tx_buf_len > 0) {
1404 SMTP_TX_BUF_MAX(tx_buf_len);
1405 if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) {
1406 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n",
1407 smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
1408 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n",
1409 smtp_state_str[s->state], tx_buf_len, s->tx_buf));
1410 s->timer = SMTP_TIMEOUT;
1411 pbuf_free(s->p);
1412 s->p = NULL;
1413 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n",
1414 smtp_state_str[s->state], smtp_state_str[next_state]));
1415 s->state = next_state;
1416 if (next_state == SMTP_BODY) {
1417 /* try to stream-send body data right now */
1418 smtp_send_body(s, pcb);
1419 } else if (next_state == SMTP_CLOSED) {
1420 /* sent out all data, delete structure */
1421 altcp_arg(pcb, NULL);
1422 smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK);
1423 }
1424 }
1425 }
1426 }
1427  
1428 #if SMTP_BODYDH
1429 /** Elementary sub-function to send data
1430 *
1431 * @returns: BDHALLDATASENT all data has been written
1432 * BDHSOMEDATASENT some data has been written
1433 * 0 no data has been written
1434 */
1435 static int
1436 smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany)
1437 {
1438 err_t err;
1439 u16_t len = *howmany;
1440  
1441 len = (u16_t)LWIP_MIN(len, altcp_sndbuf(pcb));
1442 err = altcp_write(pcb, *from, len, TCP_WRITE_FLAG_COPY);
1443 if (err == ERR_OK) {
1444 *from += len;
1445 if ((*howmany -= len) > 0) {
1446 return BDHSOMEDATASENT;
1447 }
1448 return BDHALLDATASENT;
1449 }
1450 return 0;
1451 }
1452  
1453 /** Same as smtp_send_mail_static, but uses a callback function to send body data
1454 */
1455 err_t
1456 smtp_send_mail_bodycback(const char *from, const char* to, const char* subject,
1457 smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg)
1458 {
1459 struct smtp_session* s;
1460 size_t len;
1461  
1462 s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
1463 if (s == NULL) {
1464 return ERR_MEM;
1465 }
1466 memset(s, 0, sizeof(struct smtp_session));
1467 s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state));
1468 if (s->bodydh == NULL) {
1469 SMTP_STATE_FREE(s);
1470 return ERR_MEM;
1471 }
1472 memset(s->bodydh, 0, sizeof(struct smtp_bodydh));
1473 /* initialize the structure */
1474 s->from = from;
1475 len = strlen(from);
1476 LWIP_ASSERT("string is too long", len <= 0xffff);
1477 s->from_len = (u16_t)len;
1478 s->to = to;
1479 len = strlen(to);
1480 LWIP_ASSERT("string is too long", len <= 0xffff);
1481 s->to_len = (u16_t)len;
1482 s->subject = subject;
1483 len = strlen(subject);
1484 LWIP_ASSERT("string is too long", len <= 0xffff);
1485 s->subject_len = (u16_t)len;
1486 s->body = NULL;
1487 LWIP_ASSERT("string is too long", len <= 0xffff);
1488 s->callback_fn = callback_fn;
1489 s->callback_arg = callback_arg;
1490 s->bodydh->callback_fn = bodycback_fn;
1491 s->bodydh->state = BDH_SENDING;
1492 /* call the actual implementation of this function */
1493 return smtp_send_mail_alloced(s);
1494 }
1495  
1496 static void
1497 smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb)
1498 {
1499 struct smtp_bodydh_state *bdh = s->bodydh;
1500 int res = 0, ret;
1501 LWIP_ASSERT("s != NULL", s != NULL);
1502 LWIP_ASSERT("bodydh != NULL", bdh != NULL);
1503  
1504 /* resume any leftovers from prior memory constraints */
1505 if (s->body_len) {
1506 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: resume\n"));
1507 if((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len))
1508 != BDHALLDATASENT) {
1509 s->body_sent = s->body_len - 1;
1510 return;
1511 }
1512 }
1513 ret = res;
1514 /* all data on buffer has been queued, resume execution */
1515 if (bdh->state == BDH_SENDING) {
1516 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: run\n"));
1517 do {
1518 ret |= res; /* remember if we once queued something to send */
1519 bdh->exposed.length = 0;
1520 if (bdh->callback_fn(s->callback_arg, &bdh->exposed) == BDH_DONE) {
1521 bdh->state = BDH_STOP;
1522 }
1523 s->body = bdh->exposed.buffer;
1524 s->body_len = bdh->exposed.length;
1525 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: trying to send %u bytes\n", (unsigned int)s->body_len));
1526 } while (s->body_len &&
1527 ((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) == BDHALLDATASENT)
1528 && (bdh->state != BDH_STOP));
1529 }
1530 if ((bdh->state != BDH_SENDING) && (ret != BDHSOMEDATASENT)) {
1531 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: stop\n"));
1532 s->body_sent = s->body_len;
1533 } else {
1534 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: pause\n"));
1535 s->body_sent = s->body_len - 1;
1536 }
1537 }
1538 #endif /* SMTP_BODYDH */
1539  
1540 #endif /* LWIP_TCP && LWIP_CALLBACK_API */