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> /* strlen, 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 #if SMTP_CHECK_DATA 296 static err_t smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed); 297 #endif /* SMTP_CHECK_DATA */ 298 static err_t smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err); 299 static void smtp_tcp_err(void *arg, err_t err); 300 static err_t smtp_tcp_poll(void *arg, struct altcp_pcb *pcb); 301 static err_t smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len); 302 static err_t smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err); 303 #if LWIP_DNS 304 static void smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg); 305 #endif /* LWIP_DNS */ 306 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN 307 static size_t smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len); 308 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */ 309 static enum smtp_session_state smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len); 310 static void smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb); 311 static void smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p); 312 #if SMTP_BODYDH 313 static void smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb); 314 #endif /* SMTP_BODYDH */ 315 316 317 #ifdef LWIP_DEBUG 318 /** Convert an smtp result to a string */ 319 const char* 320 smtp_result_str(u8_t smtp_result) 321 { 322 if (smtp_result >= LWIP_ARRAYSIZE(smtp_result_strs)) { 323 return "UNKNOWN"; 324 } 325 return smtp_result_strs[smtp_result]; 326 } 327 328 /** Null-terminates the payload of p for printing out messages. 329 * WARNING: use this only if p is not needed any more as the last byte of 330 * payload is deleted! 331 */ 332 static const char* 333 smtp_pbuf_str(struct pbuf* p) 334 { 335 if ((p == NULL) || (p->len == 0)) { 336 return ""; 337 } 338 ((char*)p->payload)[p->len] = 0; 339 return (const char*)p->payload; 340 } 341 #endif /* LWIP_DEBUG */ 342 343 /** @ingroup smtp 344 * Set IP address or DNS name for next SMTP connection 345 * 346 * @param server IP address (in ASCII representation) or DNS name of the server 347 */ 348 err_t 349 smtp_set_server_addr(const char* server) 350 { 351 size_t len = 0; 352 353 LWIP_ASSERT_CORE_LOCKED(); 354 355 if (server != NULL) { 356 /* strlen: returns length WITHOUT terminating 0 byte */ 357 len = strlen(server); 358 } 359 if (len > SMTP_MAX_SERVERNAME_LEN) { 360 return ERR_MEM; 361 } 362 if (len != 0) { 363 MEMCPY(smtp_server, server, len); 364 } 365 smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */ 366 return ERR_OK; 367 } 368 369 /** @ingroup smtp 370 * Set TCP port for next SMTP connection 371 * 372 * @param port TCP port 373 */ 374 void 375 smtp_set_server_port(u16_t port) 376 { 377 LWIP_ASSERT_CORE_LOCKED(); 378 smtp_server_port = port; 379 } 380 381 #if LWIP_ALTCP && LWIP_ALTCP_TLS 382 /** @ingroup smtp 383 * Set TLS configuration for next SMTP connection 384 * 385 * @param tls_config TLS configuration 386 */ 387 void 388 smtp_set_tls_config(struct altcp_tls_config *tls_config) 389 { 390 LWIP_ASSERT_CORE_LOCKED(); 391 smtp_server_tls_config = tls_config; 392 } 393 #endif 394 395 /** @ingroup smtp 396 * Set authentication parameters for next SMTP connection 397 * 398 * @param username login name as passed to the server 399 * @param pass password passed to the server together with username 400 */ 401 err_t 402 smtp_set_auth(const char* username, const char* pass) 403 { 404 size_t uname_len = 0; 405 size_t pass_len = 0; 406 407 LWIP_ASSERT_CORE_LOCKED(); 408 409 memset(smtp_auth_plain, 0xfa, 64); 410 if (username != NULL) { 411 uname_len = strlen(username); 412 if (uname_len > SMTP_MAX_USERNAME_LEN) { 413 LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n", 414 (int)uname_len, SMTP_MAX_USERNAME_LEN)); 415 return ERR_ARG; 416 } 417 } 418 if (pass != NULL) { 419 #if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN 420 pass_len = strlen(pass); 421 if (pass_len > SMTP_MAX_PASS_LEN) { 422 LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n", 423 (int)uname_len, SMTP_MAX_USERNAME_LEN)); 424 return ERR_ARG; 425 } 426 #else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */ 427 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n")); 428 #endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */ 429 } 430 *smtp_auth_plain = 0; 431 if (username != NULL) { 432 smtp_username = smtp_auth_plain + 1; 433 strcpy(smtp_username, username); 434 } 435 if (pass != NULL) { 436 smtp_pass = smtp_auth_plain + uname_len + 2; 437 strcpy(smtp_pass, pass); 438 } 439 smtp_auth_plain_len = uname_len + pass_len + 2; 440 441 return ERR_OK; 442 } 443 444 #if SMTP_BODYDH 445 static void smtp_free_struct(struct smtp_session *s) 446 { 447 if (s->bodydh != NULL) { 448 SMTP_BODYDH_FREE(s->bodydh); 449 } 450 SMTP_STATE_FREE(s); 451 } 452 #else /* SMTP_BODYDH */ 453 #define smtp_free_struct(x) SMTP_STATE_FREE(x) 454 #endif /* SMTP_BODYDH */ 455 456 static struct altcp_pcb* 457 smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip) 458 { 459 struct altcp_pcb* pcb; 460 LWIP_UNUSED_ARG(remote_ip); 461 462 #if LWIP_ALTCP && LWIP_ALTCP_TLS 463 if (smtp_server_tls_config) { 464 pcb = altcp_tls_new(smtp_server_tls_config, IP_GET_TYPE(remote_ip)); 465 } else 466 #endif 467 { 468 pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip)); 469 } 470 if (pcb != NULL) { 471 altcp_arg(pcb, s); 472 altcp_recv(pcb, smtp_tcp_recv); 473 altcp_err(pcb, smtp_tcp_err); 474 altcp_poll(pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL); 475 altcp_sent(pcb, smtp_tcp_sent); 476 } 477 return pcb; 478 } 479 480 /** The actual mail-sending function, called by smtp_send_mail and 481 * smtp_send_mail_static after setting up the struct smtp_session. 482 */ 483 static err_t 484 smtp_send_mail_alloced(struct smtp_session *s) 485 { 486 err_t err; 487 struct altcp_pcb* pcb = NULL; 488 ip_addr_t addr; 489 490 LWIP_ASSERT("no smtp_session supplied", s != NULL); 491 492 #if SMTP_CHECK_DATA 493 /* check that body conforms to RFC: 494 * - convert all single-CR or -LF in body to CRLF 495 * - only 7-bit ASCII is allowed 496 */ 497 if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) { 498 err = ERR_ARG; 499 goto leave; 500 } 501 if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) { 502 err = ERR_ARG; 503 goto leave; 504 } 505 if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) { 506 err = ERR_ARG; 507 goto leave; 508 } 509 #if SMTP_BODYDH 510 if (s->bodydh == NULL) 511 #endif /* SMTP_BODYDH */ 512 { 513 if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) { 514 err = ERR_ARG; 515 goto leave; 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 = altcp_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 560 deallocate_and_leave: 561 if (pcb != NULL) { 562 altcp_arg(pcb, NULL); 563 altcp_close(pcb); 564 } 565 leave: 566 smtp_free_struct(s); 567 /* no need to call the callback here since we return != ERR_OK */ 568 return err; 569 } 570 571 /** @ingroup smtp 572 * Send an email via the currently selected server, username and password. 573 * 574 * @param from source email address (must be NULL-terminated) 575 * @param to target email address (must be NULL-terminated) 576 * @param subject email subject (must be NULL-terminated) 577 * @param body email body (must be NULL-terminated) 578 * @param callback_fn callback function 579 * @param callback_arg user argument to callback_fn 580 * @returns - ERR_OK if structures were allocated and no error occurred starting the connection 581 * (this does not mean the email has been successfully sent!) 582 * - another err_t on error. 583 */ 584 err_t 585 smtp_send_mail(const char* from, const char* to, const char* subject, const char* body, 586 smtp_result_fn callback_fn, void* callback_arg) 587 { 588 struct smtp_session* s; 589 size_t from_len = strlen(from); 590 size_t to_len = strlen(to); 591 size_t subject_len = strlen(subject); 592 size_t body_len = strlen(body); 593 size_t mem_len = sizeof(struct smtp_session); 594 char *sfrom, *sto, *ssubject, *sbody; 595 596 LWIP_ASSERT_CORE_LOCKED(); 597 598 mem_len += from_len + to_len + subject_len + body_len + 4; 599 if (mem_len > 0xffff) { 600 /* too long! */ 601 return ERR_MEM; 602 } 603 604 /* Allocate memory to keep this email's session state */ 605 s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len); 606 if (s == NULL) { 607 return ERR_MEM; 608 } 609 /* initialize the structure */ 610 memset(s, 0, mem_len); 611 s->from = sfrom = (char*)s + sizeof(struct smtp_session); 612 s->from_len = (u16_t)from_len; 613 s->to = sto = sfrom + from_len + 1; 614 s->to_len = (u16_t)to_len; 615 s->subject = ssubject = sto + to_len + 1; 616 s->subject_len = (u16_t)subject_len; 617 s->body = sbody = ssubject + subject_len + 1; 618 s->body_len = (u16_t)body_len; 619 /* copy source and target email address */ 620 /* cast to size_t is a hack to cast away constness */ 621 MEMCPY(sfrom, from, from_len + 1); 622 MEMCPY(sto, to, to_len + 1); 623 MEMCPY(ssubject, subject, subject_len + 1); 624 MEMCPY(sbody, body, body_len + 1); 625 626 s->callback_fn = callback_fn; 627 s->callback_arg = callback_arg; 628 629 /* call the actual implementation of this function */ 630 return smtp_send_mail_alloced(s); 631 } 632 633 /** @ingroup smtp 634 * Same as smtp_send_mail, but doesn't copy from, to, subject and body into 635 * an internal buffer to save memory. 636 * WARNING: the above data must stay untouched until the callback function is 637 * called (unless the function returns != ERR_OK) 638 */ 639 err_t 640 smtp_send_mail_static(const char *from, const char* to, const char* subject, 641 const char* body, smtp_result_fn callback_fn, void* callback_arg) 642 { 643 struct smtp_session* s; 644 size_t len; 645 646 LWIP_ASSERT_CORE_LOCKED(); 647 648 s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session)); 649 if (s == NULL) { 650 return ERR_MEM; 651 } 652 memset(s, 0, sizeof(struct smtp_session)); 653 /* initialize the structure */ 654 s->from = from; 655 len = strlen(from); 656 LWIP_ASSERT("string is too long", len <= 0xffff); 657 s->from_len = (u16_t)len; 658 s->to = to; 659 len = strlen(to); 660 LWIP_ASSERT("string is too long", len <= 0xffff); 661 s->to_len = (u16_t)len; 662 s->subject = subject; 663 len = strlen(subject); 664 LWIP_ASSERT("string is too long", len <= 0xffff); 665 s->subject_len = (u16_t)len; 666 s->body = body; 667 len = strlen(body); 668 LWIP_ASSERT("string is too long", len <= 0xffff); 669 s->body_len = (u16_t)len; 670 s->callback_fn = callback_fn; 671 s->callback_arg = callback_arg; 672 /* call the actual implementation of this function */ 673 return smtp_send_mail_alloced(s); 674 } 675 676 677 /** @ingroup smtp 678 * Same as smtp_send_mail but takes a struct smtp_send_request as single 679 * parameter which contains all the other parameters. 680 * To be used with tcpip_callback to send mail from interrupt context or from 681 * another thread. 682 * 683 * WARNING: server and authentication must stay untouched until this function has run! 684 * 685 * Usage example: 686 * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context) 687 * - fill the members of the struct as if calling smtp_send_mail 688 * - specify a callback_function 689 * - set callback_arg to the structure itself 690 * - call this function 691 * - wait for the callback function to be called 692 * - in the callback function, deallocate the structure (passed as arg) 693 */ 694 void 695 smtp_send_mail_int(void *arg) 696 { 697 struct smtp_send_request *req = (struct smtp_send_request*)arg; 698 err_t err; 699 700 LWIP_ASSERT_CORE_LOCKED(); 701 LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL); 702 703 if (req->static_data) { 704 err = smtp_send_mail_static(req->from, req->to, req->subject, req->body, 705 req->callback_fn, req->callback_arg); 706 } else { 707 err = smtp_send_mail(req->from, req->to, req->subject, req->body, 708 req->callback_fn, req->callback_arg); 709 } 710 if ((err != ERR_OK) && (req->callback_fn != NULL)) { 711 req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err); 712 } 713 } 714 715 #if SMTP_CHECK_DATA 716 /** Verify that a given string conforms to the SMTP rules 717 * (7-bit only, no single CR or LF, 718 * @todo: no line consisting of a single dot only) 719 */ 720 static err_t 721 smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed) 722 { 723 size_t i; 724 u8_t last_was_cr = 0; 725 for (i = 0; i < data_len; i++) { 726 char current = data[i]; 727 if ((current & 0x80) != 0) { 728 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: no 8-bit data supported: %s\n", data)); 729 return ERR_ARG; 730 } 731 if (current == '\r') { 732 if (!linebreaks_allowed) { 733 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found CR where no linebreaks allowed: %s\n", data)); 734 return ERR_ARG; 735 } 736 if (last_was_cr) { 737 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found double CR: %s\n", data)); 738 return ERR_ARG; 739 } 740 last_was_cr = 1; 741 } else { 742 if (current == '\n') { 743 if (!last_was_cr) { 744 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found LF without CR before: %s\n", data)); 745 return ERR_ARG; 746 } 747 } 748 last_was_cr = 0; 749 } 750 } 751 return ERR_OK; 752 } 753 #endif /* SMTP_CHECK_DATA */ 754 755 /** Frees the smtp_session and calls the callback function */ 756 static void 757 smtp_free(struct smtp_session *s, u8_t result, u16_t srv_err, err_t err) 758 { 759 smtp_result_fn fn = s->callback_fn; 760 void *arg = s->callback_arg; 761 if (s->p != NULL) { 762 pbuf_free(s->p); 763 } 764 smtp_free_struct(s); 765 if (fn != NULL) { 766 fn(arg, result, srv_err, err); 767 } 768 } 769 770 /** Try to close a pcb and free the arg if successful */ 771 static void 772 smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result, 773 u16_t srv_err, err_t err) 774 { 775 if (pcb != NULL) { 776 altcp_arg(pcb, NULL); 777 if (altcp_close(pcb) == ERR_OK) { 778 if (s != NULL) { 779 smtp_free(s, result, srv_err, err); 780 } 781 } else { 782 /* close failed, set back arg */ 783 altcp_arg(pcb, s); 784 } 785 } else { 786 if (s != NULL) { 787 smtp_free(s, result, srv_err, err); 788 } 789 } 790 } 791 792 /** Raw API TCP err callback: pcb is already deallocated */ 793 static void 794 smtp_tcp_err(void *arg, err_t err) 795 { 796 LWIP_UNUSED_ARG(err); 797 if (arg != NULL) { 798 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_err: connection reset by remote host\n")); 799 smtp_free((struct smtp_session*)arg, SMTP_RESULT_ERR_CLOSED, 0, err); 800 } 801 } 802 803 /** Raw API TCP poll callback */ 804 static err_t 805 smtp_tcp_poll(void *arg, struct altcp_pcb *pcb) 806 { 807 if (arg != NULL) { 808 struct smtp_session *s = (struct smtp_session*)arg; 809 if (s->timer != 0) { 810 s->timer--; 811 } 812 } 813 smtp_process(arg, pcb, NULL); 814 return ERR_OK; 815 } 816 817 /** Raw API TCP sent callback */ 818 static err_t 819 smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len) 820 { 821 LWIP_UNUSED_ARG(len); 822 823 smtp_process(arg, pcb, NULL); 824 825 return ERR_OK; 826 } 827 828 /** Raw API TCP recv callback */ 829 static err_t 830 smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) 831 { 832 LWIP_UNUSED_ARG(err); 833 if (p != NULL) { 834 altcp_recved(pcb, p->tot_len); 835 smtp_process(arg, pcb, p); 836 } else { 837 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_recv: connection closed by remote host\n")); 838 smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CLOSED, 0, err); 839 } 840 return ERR_OK; 841 } 842 843 static err_t 844 smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err) 845 { 846 LWIP_UNUSED_ARG(arg); 847 848 if (err == ERR_OK) { 849 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_connected: Waiting for 220\n")); 850 } else { 851 /* shouldn't happen, but we still check 'err', only to be sure */ 852 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_connected: %d\n", (int)err)); 853 smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CONNECT, 0, err); 854 } 855 return ERR_OK; 856 } 857 858 #if LWIP_DNS 859 /** DNS callback 860 * If ipaddr is non-NULL, resolving succeeded, otherwise it failed. 861 */ 862 static void 863 smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg) 864 { 865 struct smtp_session *s = (struct smtp_session*)arg; 866 struct altcp_pcb *pcb; 867 err_t err; 868 u8_t result; 869 870 LWIP_UNUSED_ARG(hostname); 871 872 if (ipaddr != NULL) { 873 pcb = smtp_setup_pcb(s, ipaddr); 874 if (pcb != NULL) { 875 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: hostname resolved, connecting\n")); 876 err = altcp_connect(pcb, ipaddr, smtp_server_port, smtp_tcp_connected); 877 if (err == ERR_OK) { 878 return; 879 } 880 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err)); 881 result = SMTP_RESULT_ERR_CONNECT; 882 } else { 883 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: failed to allocate tcp pcb\n")); 884 result = SMTP_RESULT_ERR_MEM; 885 err = ERR_MEM; 886 } 887 } else { 888 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_dns_found: failed to resolve hostname: %s\n", 889 hostname)); 890 pcb = NULL; 891 result = SMTP_RESULT_ERR_HOSTNAME; 892 err = ERR_ARG; 893 } 894 smtp_close(s, pcb, result, 0, err); 895 } 896 #endif /* LWIP_DNS */ 897 898 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN 899 900 /** Table 6-bit-index-to-ASCII used for base64-encoding */ 901 static const char base64_table[] = { 902 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 903 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 904 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 905 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 906 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 907 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 908 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 909 '+', '/' 910 }; 911 912 /** Base64 encoding */ 913 static size_t 914 smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len) 915 { 916 size_t i; 917 s8_t j; 918 size_t target_idx = 0; 919 size_t longer = (source_len % 3) ? (3 - (source_len % 3)) : 0; 920 size_t source_len_b64 = source_len + longer; 921 size_t len = (((source_len_b64) * 4) / 3); 922 u8_t x = 5; 923 u8_t current = 0; 924 LWIP_UNUSED_ARG(target_len); 925 926 LWIP_ASSERT("target_len is too short", target_len >= len); 927 928 for (i = 0; i < source_len_b64; i++) { 929 u8_t b = (i < source_len ? (u8_t)source[i] : 0); 930 for (j = 7; j >= 0; j--, x--) { 931 if ((b & (1 << j)) != 0) { 932 current = (u8_t)(current | (1U << x)); 933 } 934 if (x == 0) { 935 target[target_idx++] = base64_table[current]; 936 x = 6; 937 current = 0; 938 } 939 } 940 } 941 for (i = len - longer; i < len; i++) { 942 target[i] = '='; 943 } 944 return len; 945 } 946 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */ 947 948 /** Parse pbuf to see if it contains the beginning of an answer. 949 * If so, it returns the contained response code as number between 1 and 999. 950 * If not, zero is returned. 951 * 952 * @param s smtp session struct 953 */ 954 static u16_t 955 smtp_is_response(struct smtp_session *s) 956 { 957 char digits[4]; 958 long num; 959 960 if (s->p == NULL) { 961 return 0; 962 } 963 /* copy three digits and convert them to int */ 964 if (pbuf_copy_partial(s->p, digits, 3, 0) != 3) { 965 /* pbuf was too short */ 966 return 0; 967 } 968 digits[3] = 0; 969 num = strtol(digits, NULL, 10); 970 if ((num <= 0) || (num >= 1000)) { 971 /* failed to find response code at start of line */ 972 return 0; 973 } 974 return (u16_t)num; 975 } 976 977 /** Parse pbuf to see if it contains a fully received answer. 978 * If one is found, ERR_OK is returned. 979 * If none is found, ERR_VAL is returned. 980 * 981 * A fully received answer is a 3-digit number followed by a space, 982 * some string and a CRLF as line ending. 983 * 984 * @param s smtp session struct 985 */ 986 static err_t 987 smtp_is_response_finished(struct smtp_session *s) 988 { 989 u8_t sp; 990 u16_t crlf; 991 u16_t offset; 992 993 if (s->p == NULL) { 994 return ERR_VAL; 995 } 996 offset = 0; 997 again: 998 /* We could check the response number here, but we trust the 999 * protocol definition which says the client can rely on it being 1000 * the same on every line. */ 1001 1002 /* find CRLF */ 1003 if (offset > 0xFFFF - 4) { 1004 /* would overflow */ 1005 return ERR_VAL; 1006 } 1007 crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, (u16_t)(offset + 4)); 1008 if (crlf == 0xFFFF) { 1009 /* no CRLF found */ 1010 return ERR_VAL; 1011 } 1012 sp = pbuf_get_at(s->p, (u16_t)(offset + 3)); 1013 if (sp == '-') { 1014 /* no space after response code -> try next line */ 1015 offset = (u16_t)(crlf + 2); 1016 if (offset < crlf) { 1017 /* overflow */ 1018 return ERR_VAL; 1019 } 1020 goto again; 1021 } else if (sp == ' ') { 1022 /* CRLF found after response code + space -> valid response */ 1023 return ERR_OK; 1024 } 1025 /* sp contains invalid character */ 1026 return ERR_VAL; 1027 } 1028 1029 /** Prepare HELO/EHLO message */ 1030 static enum smtp_session_state 1031 smtp_prepare_helo(struct smtp_session *s, u16_t *tx_buf_len, struct altcp_pcb *pcb) 1032 { 1033 size_t ipa_len; 1034 const char *ipa = ipaddr_ntoa(altcp_get_ip(pcb, 1)); 1035 LWIP_ASSERT("ipaddr_ntoa returned NULL", ipa != NULL); 1036 ipa_len = strlen(ipa); 1037 LWIP_ASSERT("string too long", ipa_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_EHLO_1_LEN-SMTP_CMD_EHLO_2_LEN)); 1038 1039 *tx_buf_len = (u16_t)(SMTP_CMD_EHLO_1_LEN + (u16_t)ipa_len + SMTP_CMD_EHLO_2_LEN); 1040 LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN); 1041 1042 SMEMCPY(s->tx_buf, SMTP_CMD_EHLO_1, SMTP_CMD_EHLO_1_LEN); 1043 MEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN], ipa, ipa_len); 1044 SMEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN + ipa_len], SMTP_CMD_EHLO_2, SMTP_CMD_EHLO_2_LEN); 1045 return SMTP_HELO; 1046 } 1047 1048 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN 1049 /** Parse last server response (in rx_buf) for supported authentication method, 1050 * create data to send out (to tx_buf), set tx_data_len correctly 1051 * and return the next state. 1052 */ 1053 static enum smtp_session_state 1054 smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len) 1055 { 1056 /* check response for supported authentication method */ 1057 u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP); 1058 if (auth == 0xFFFF) { 1059 auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ); 1060 } 1061 if (auth != 0xFFFF) { 1062 u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth); 1063 if ((crlf != 0xFFFF) && (crlf > auth)) { 1064 /* use tx_buf temporarily */ 1065 u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, (u16_t)(crlf - auth), auth); 1066 if (copied != 0) { 1067 char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN; 1068 s->tx_buf[copied] = 0; 1069 #if SMTP_SUPPORT_AUTH_PLAIN 1070 /* favour PLAIN over LOGIN since it involves less requests */ 1071 if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) { 1072 size_t auth_len; 1073 /* server supports AUTH PLAIN */ 1074 SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN); 1075 1076 /* add base64-encoded string "\0username\0password" */ 1077 auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN], 1078 SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s), 1079 SMTP_AUTH_PLAIN_LEN(s)); 1080 LWIP_ASSERT("string too long", auth_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_AUTHPLAIN_1_LEN-SMTP_CMD_AUTHPLAIN_2_LEN)); 1081 *tx_buf_len = (u16_t)(SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len); 1082 SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2, 1083 SMTP_CMD_AUTHPLAIN_2_LEN); 1084 return SMTP_AUTH_PLAIN; 1085 } else 1086 #endif /* SMTP_SUPPORT_AUTH_PLAIN */ 1087 { 1088 #if SMTP_SUPPORT_AUTH_LOGIN 1089 if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) { 1090 /* server supports AUTH LOGIN */ 1091 *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN; 1092 SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN); 1093 return SMTP_AUTH_LOGIN_UNAME; 1094 } 1095 #endif /* SMTP_SUPPORT_AUTH_LOGIN */ 1096 } 1097 } 1098 } 1099 } 1100 /* server didnt's send correct keywords for AUTH, try sending directly */ 1101 return smtp_prepare_mail(s, tx_buf_len); 1102 } 1103 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */ 1104 1105 #if SMTP_SUPPORT_AUTH_LOGIN 1106 /** Send base64-encoded username */ 1107 static enum smtp_session_state 1108 smtp_prepare_auth_login_uname(struct smtp_session *s, u16_t *tx_buf_len) 1109 { 1110 size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN, 1111 SMTP_USERNAME(s), strlen(SMTP_USERNAME(s))); 1112 /* @todo: support base64-encoded longer than 64k */ 1113 LWIP_ASSERT("string too long", base64_len <= 0xffff); 1114 LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN); 1115 *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN); 1116 1117 SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN); 1118 s->tx_buf[*tx_buf_len] = 0; 1119 return SMTP_AUTH_LOGIN_PASS; 1120 } 1121 1122 /** Send base64-encoded password */ 1123 static enum smtp_session_state 1124 smtp_prepare_auth_login_pass(struct smtp_session *s, u16_t *tx_buf_len) 1125 { 1126 size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN, 1127 SMTP_PASS(s), strlen(SMTP_PASS(s))); 1128 /* @todo: support base64-encoded longer than 64k */ 1129 LWIP_ASSERT("string too long", base64_len <= 0xffff); 1130 LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN); 1131 *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN); 1132 1133 SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN); 1134 s->tx_buf[*tx_buf_len] = 0; 1135 return SMTP_AUTH_LOGIN; 1136 } 1137 #endif /* SMTP_SUPPORT_AUTH_LOGIN */ 1138 1139 /** Prepare MAIL message */ 1140 static enum smtp_session_state 1141 smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len) 1142 { 1143 char *target = s->tx_buf; 1144 LWIP_ASSERT("tx_buf overflow detected", s->from_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_MAIL_1_LEN - SMTP_CMD_MAIL_2_LEN)); 1145 *tx_buf_len = (u16_t)(SMTP_CMD_MAIL_1_LEN + SMTP_CMD_MAIL_2_LEN + s->from_len); 1146 target[*tx_buf_len] = 0; 1147 1148 SMEMCPY(target, SMTP_CMD_MAIL_1, SMTP_CMD_MAIL_1_LEN); 1149 target += SMTP_CMD_MAIL_1_LEN; 1150 MEMCPY(target, s->from, s->from_len); 1151 target += s->from_len; 1152 SMEMCPY(target, SMTP_CMD_MAIL_2, SMTP_CMD_MAIL_2_LEN); 1153 return SMTP_MAIL; 1154 } 1155 1156 /** Prepare RCPT message */ 1157 static enum smtp_session_state 1158 smtp_prepare_rcpt(struct smtp_session *s, u16_t *tx_buf_len) 1159 { 1160 char *target = s->tx_buf; 1161 LWIP_ASSERT("tx_buf overflow detected", s->to_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_RCPT_1_LEN - SMTP_CMD_RCPT_2_LEN)); 1162 *tx_buf_len = (u16_t)(SMTP_CMD_RCPT_1_LEN + SMTP_CMD_RCPT_2_LEN + s->to_len); 1163 target[*tx_buf_len] = 0; 1164 1165 SMEMCPY(target, SMTP_CMD_RCPT_1, SMTP_CMD_RCPT_1_LEN); 1166 target += SMTP_CMD_RCPT_1_LEN; 1167 MEMCPY(target, s->to, s->to_len); 1168 target += s->to_len; 1169 SMEMCPY(target, SMTP_CMD_RCPT_2, SMTP_CMD_RCPT_2_LEN); 1170 return SMTP_RCPT; 1171 } 1172 1173 /** Prepare header of body */ 1174 static enum smtp_session_state 1175 smtp_prepare_header(struct smtp_session *s, u16_t *tx_buf_len) 1176 { 1177 char *target = s->tx_buf; 1178 int len = SMTP_CMD_HEADER_1_LEN + SMTP_CMD_HEADER_2_LEN + 1179 SMTP_CMD_HEADER_3_LEN + SMTP_CMD_HEADER_4_LEN + s->from_len + s->to_len + 1180 s->subject_len; 1181 LWIP_ASSERT("tx_buf overflow detected", len > 0 && len <= SMTP_TX_BUF_LEN); 1182 *tx_buf_len = (u16_t)len; 1183 target[*tx_buf_len] = 0; 1184 1185 SMEMCPY(target, SMTP_CMD_HEADER_1, SMTP_CMD_HEADER_1_LEN); 1186 target += SMTP_CMD_HEADER_1_LEN; 1187 MEMCPY(target, s->from, s->from_len); 1188 target += s->from_len; 1189 SMEMCPY(target, SMTP_CMD_HEADER_2, SMTP_CMD_HEADER_2_LEN); 1190 target += SMTP_CMD_HEADER_2_LEN; 1191 MEMCPY(target, s->to, s->to_len); 1192 target += s->to_len; 1193 SMEMCPY(target, SMTP_CMD_HEADER_3, SMTP_CMD_HEADER_3_LEN); 1194 target += SMTP_CMD_HEADER_3_LEN; 1195 MEMCPY(target, s->subject, s->subject_len); 1196 target += s->subject_len; 1197 SMEMCPY(target, SMTP_CMD_HEADER_4, SMTP_CMD_HEADER_4_LEN); 1198 1199 return SMTP_BODY; 1200 } 1201 1202 /** Prepare QUIT message */ 1203 static enum smtp_session_state 1204 smtp_prepare_quit(struct smtp_session *s, u16_t *tx_buf_len) 1205 { 1206 *tx_buf_len = SMTP_CMD_QUIT_LEN; 1207 s->tx_buf[*tx_buf_len] = 0; 1208 SMEMCPY(s->tx_buf, SMTP_CMD_QUIT, SMTP_CMD_QUIT_LEN); 1209 LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN); 1210 return SMTP_CLOSED; 1211 } 1212 1213 /** If in state SMTP_BODY, try to send more body data */ 1214 static void 1215 smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb) 1216 { 1217 err_t err; 1218 1219 if (s->state == SMTP_BODY) { 1220 #if SMTP_BODYDH 1221 if (s->bodydh) { 1222 smtp_send_body_data_handler(s, pcb); 1223 } else 1224 #endif /* SMTP_BODYDH */ 1225 { 1226 u16_t send_len = (u16_t)(s->body_len - s->body_sent); 1227 if (send_len > 0) { 1228 u16_t snd_buf = altcp_sndbuf(pcb); 1229 if (send_len > snd_buf) { 1230 send_len = snd_buf; 1231 } 1232 if (send_len > 0) { 1233 /* try to send something out */ 1234 err = altcp_write(pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY); 1235 if (err == ERR_OK) { 1236 s->timer = SMTP_TIMEOUT_DATABLOCK; 1237 s->body_sent = (u16_t)(s->body_sent + send_len); 1238 if (s->body_sent < s->body_len) { 1239 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n", 1240 s->body_sent, s->body_len)); 1241 } 1242 } 1243 } 1244 } 1245 } 1246 if (s->body_sent == s->body_len) { 1247 /* the whole body has been written, write last line */ 1248 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n", 1249 s->body_len)); 1250 err = altcp_write(pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0); 1251 if (err == ERR_OK) { 1252 s->timer = SMTP_TIMEOUT_DATATERM; 1253 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n", 1254 smtp_state_str[SMTP_QUIT])); 1255 /* last line written, change state, wait for confirmation */ 1256 s->state = SMTP_QUIT; 1257 } 1258 } 1259 } 1260 } 1261 1262 /** State machine-like implementation of an SMTP client. 1263 */ 1264 static void 1265 smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p) 1266 { 1267 struct smtp_session* s = (struct smtp_session*)arg; 1268 u16_t response_code = 0; 1269 u16_t tx_buf_len = 0; 1270 enum smtp_session_state next_state; 1271 1272 if (arg == NULL) { 1273 /* already closed SMTP connection */ 1274 if (p != NULL) { 1275 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n", 1276 p->tot_len, smtp_pbuf_str(p))); 1277 pbuf_free(p); 1278 } 1279 return; 1280 } 1281 1282 next_state = s->state; 1283 1284 if (p != NULL) { 1285 /* received data */ 1286 if (s->p == NULL) { 1287 s->p = p; 1288 } else { 1289 pbuf_cat(s->p, p); 1290 } 1291 } else { 1292 /* idle timer, close connection if timed out */ 1293 if (s->timer == 0) { 1294 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n")); 1295 smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT); 1296 return; 1297 } 1298 if (s->state == SMTP_BODY) { 1299 smtp_send_body(s, pcb); 1300 return; 1301 } 1302 } 1303 response_code = smtp_is_response(s); 1304 if (response_code) { 1305 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code)); 1306 if (smtp_is_response_finished(s) != ERR_OK) { 1307 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code)); 1308 /* wait for next packet to complete the respone */ 1309 return; 1310 } 1311 } else { 1312 if (s->p != NULL) { 1313 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n", 1314 smtp_pbuf_str(s->p))); 1315 pbuf_free(s->p); 1316 s->p = NULL; 1317 } 1318 return; 1319 } 1320 1321 switch(s->state) 1322 { 1323 case(SMTP_NULL): 1324 /* wait for 220 */ 1325 if (response_code == 220) { 1326 /* then send EHLO */ 1327 next_state = smtp_prepare_helo(s, &tx_buf_len, pcb); 1328 } 1329 break; 1330 case(SMTP_HELO): 1331 /* wait for 250 */ 1332 if (response_code == 250) { 1333 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN 1334 /* then send AUTH or MAIL */ 1335 next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len); 1336 } 1337 break; 1338 case(SMTP_AUTH_LOGIN): 1339 case(SMTP_AUTH_PLAIN): 1340 /* wait for 235 */ 1341 if (response_code == 235) { 1342 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */ 1343 /* send MAIL */ 1344 next_state = smtp_prepare_mail(s, &tx_buf_len); 1345 } 1346 break; 1347 #if SMTP_SUPPORT_AUTH_LOGIN 1348 case(SMTP_AUTH_LOGIN_UNAME): 1349 /* wait for 334 Username */ 1350 if (response_code == 334) { 1351 if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) { 1352 /* send username */ 1353 next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len); 1354 } 1355 } 1356 break; 1357 case(SMTP_AUTH_LOGIN_PASS): 1358 /* wait for 334 Password */ 1359 if (response_code == 334) { 1360 if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) { 1361 /* send username */ 1362 next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len); 1363 } 1364 } 1365 break; 1366 #endif /* SMTP_SUPPORT_AUTH_LOGIN */ 1367 case(SMTP_MAIL): 1368 /* wait for 250 */ 1369 if (response_code == 250) { 1370 /* send RCPT */ 1371 next_state = smtp_prepare_rcpt(s, &tx_buf_len); 1372 } 1373 break; 1374 case(SMTP_RCPT): 1375 /* wait for 250 */ 1376 if (response_code == 250) { 1377 /* send DATA */ 1378 SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN); 1379 tx_buf_len = SMTP_CMD_DATA_LEN; 1380 next_state = SMTP_DATA; 1381 } 1382 break; 1383 case(SMTP_DATA): 1384 /* wait for 354 */ 1385 if (response_code == 354) { 1386 /* send email header */ 1387 next_state = smtp_prepare_header(s, &tx_buf_len); 1388 } 1389 break; 1390 case(SMTP_BODY): 1391 /* nothing to be done here, handled somewhere else */ 1392 break; 1393 case(SMTP_QUIT): 1394 /* wait for 250 */ 1395 if (response_code == 250) { 1396 /* send QUIT */ 1397 next_state = smtp_prepare_quit(s, &tx_buf_len); 1398 } 1399 break; 1400 case(SMTP_CLOSED): 1401 /* nothing to do, wait for connection closed from server */ 1402 return; 1403 default: 1404 LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state, 1405 smtp_state_str[s->state])); 1406 break; 1407 } 1408 if (s->state == next_state) { 1409 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n", 1410 smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p))); 1411 /* close connection */ 1412 smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK); 1413 return; 1414 } 1415 if (tx_buf_len > 0) { 1416 SMTP_TX_BUF_MAX(tx_buf_len); 1417 if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) { 1418 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n", 1419 smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p))); 1420 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n", 1421 smtp_state_str[s->state], tx_buf_len, s->tx_buf)); 1422 s->timer = SMTP_TIMEOUT; 1423 pbuf_free(s->p); 1424 s->p = NULL; 1425 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n", 1426 smtp_state_str[s->state], smtp_state_str[next_state])); 1427 s->state = next_state; 1428 if (next_state == SMTP_BODY) { 1429 /* try to stream-send body data right now */ 1430 smtp_send_body(s, pcb); 1431 } else if (next_state == SMTP_CLOSED) { 1432 /* sent out all data, delete structure */ 1433 altcp_arg(pcb, NULL); 1434 smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK); 1435 } 1436 } 1437 } 1438 } 1439 1440 #if SMTP_BODYDH 1441 /** Elementary sub-function to send data 1442 * 1443 * @returns: BDHALLDATASENT all data has been written 1444 * BDHSOMEDATASENT some data has been written 1445 * 0 no data has been written 1446 */ 1447 static int 1448 smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany) 1449 { 1450 err_t err; 1451 u16_t len = *howmany; 1452 1453 len = (u16_t)LWIP_MIN(len, altcp_sndbuf(pcb)); 1454 err = altcp_write(pcb, *from, len, TCP_WRITE_FLAG_COPY); 1455 if (err == ERR_OK) { 1456 *from += len; 1457 if ((*howmany -= len) > 0) { 1458 return BDHSOMEDATASENT; 1459 } 1460 return BDHALLDATASENT; 1461 } 1462 return 0; 1463 } 1464 1465 /** Same as smtp_send_mail_static, but uses a callback function to send body data 1466 */ 1467 err_t 1468 smtp_send_mail_bodycback(const char *from, const char* to, const char* subject, 1469 smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg) 1470 { 1471 struct smtp_session* s; 1472 size_t len; 1473 1474 LWIP_ASSERT_CORE_LOCKED(); 1475 1476 s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session)); 1477 if (s == NULL) { 1478 return ERR_MEM; 1479 } 1480 memset(s, 0, sizeof(struct smtp_session)); 1481 s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state)); 1482 if (s->bodydh == NULL) { 1483 SMTP_STATE_FREE(s); 1484 return ERR_MEM; 1485 } 1486 memset(s->bodydh, 0, sizeof(struct smtp_bodydh_state)); 1487 /* initialize the structure */ 1488 s->from = from; 1489 len = strlen(from); 1490 LWIP_ASSERT("string is too long", len <= 0xffff); 1491 s->from_len = (u16_t)len; 1492 s->to = to; 1493 len = strlen(to); 1494 LWIP_ASSERT("string is too long", len <= 0xffff); 1495 s->to_len = (u16_t)len; 1496 s->subject = subject; 1497 len = strlen(subject); 1498 LWIP_ASSERT("string is too long", len <= 0xffff); 1499 s->subject_len = (u16_t)len; 1500 s->body = NULL; 1501 s->callback_fn = callback_fn; 1502 s->callback_arg = callback_arg; 1503 s->bodydh->callback_fn = bodycback_fn; 1504 s->bodydh->state = BDH_SENDING; 1505 /* call the actual implementation of this function */ 1506 return smtp_send_mail_alloced(s); 1507 } 1508 1509 static void 1510 smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb) 1511 { 1512 struct smtp_bodydh_state *bdh; 1513 int res = 0, ret; 1514 LWIP_ASSERT("s != NULL", s != NULL); 1515 bdh = s->bodydh; 1516 LWIP_ASSERT("bodydh != NULL", bdh != NULL); 1517 1518 /* resume any leftovers from prior memory constraints */ 1519 if (s->body_len) { 1520 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: resume\n")); 1521 if((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) 1522 != BDHALLDATASENT) { 1523 s->body_sent = s->body_len - 1; 1524 return; 1525 } 1526 } 1527 ret = res; 1528 /* all data on buffer has been queued, resume execution */ 1529 if (bdh->state == BDH_SENDING) { 1530 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: run\n")); 1531 do { 1532 ret |= res; /* remember if we once queued something to send */ 1533 bdh->exposed.length = 0; 1534 if (bdh->callback_fn(s->callback_arg, &bdh->exposed) == BDH_DONE) { 1535 bdh->state = BDH_STOP; 1536 } 1537 s->body = bdh->exposed.buffer; 1538 s->body_len = bdh->exposed.length; 1539 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: trying to send %u bytes\n", (unsigned int)s->body_len)); 1540 } while (s->body_len && 1541 ((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) == BDHALLDATASENT) 1542 && (bdh->state != BDH_STOP)); 1543 } 1544 if ((bdh->state != BDH_SENDING) && (ret != BDHSOMEDATASENT)) { 1545 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: stop\n")); 1546 s->body_sent = s->body_len; 1547 } else { 1548 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: pause\n")); 1549 s->body_sent = s->body_len - 1; 1550 } 1551 } 1552 #endif /* SMTP_BODYDH */ 1553 1554 #endif /* LWIP_TCP && LWIP_CALLBACK_API */ 1555