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