Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
lwip_smtp.c
00001 /** 00002 * @file 00003 * SMTP client module 00004 * 00005 * Author: Simon Goldschmidt 00006 * 00007 * @defgroup smtp SMTP client 00008 * @ingroup apps 00009 * 00010 * This is simple SMTP client for raw API. 00011 * It is a minimal implementation of SMTP as specified in RFC 5321. 00012 * 00013 * Example usage: 00014 @code{.c} 00015 void my_smtp_result_fn(void *arg, u8_t smtp_result, u16_t srv_err, err_t err) 00016 { 00017 printf("mail (%p) sent with results: 0x%02x, 0x%04x, 0x%08x\n", arg, 00018 smtp_result, srv_err, err); 00019 } 00020 static void my_smtp_test(void) 00021 { 00022 smtp_set_server_addr("mymailserver.org"); 00023 -> set both username and password as NULL if no auth needed 00024 smtp_set_auth("username", "password"); 00025 smtp_send_mail("sender", "recipient", "subject", "body", my_smtp_result_fn, 00026 some_argument); 00027 } 00028 @endcode 00029 00030 * When using from any other thread than the tcpip_thread (for NO_SYS==0), use 00031 * smtp_send_mail_int()! 00032 * 00033 * SMTP_BODYDH usage: 00034 @code{.c} 00035 int my_smtp_bodydh_fn(void *arg, struct smtp_bodydh *bdh) 00036 { 00037 if(bdh->state >= 10) { 00038 return BDH_DONE; 00039 } 00040 sprintf(bdh->buffer,"Line #%2d\r\n",bdh->state); 00041 bdh->length = strlen(bdh->buffer); 00042 ++bdh->state; 00043 return BDH_WORKING; 00044 } 00045 00046 smtp_send_mail_bodycback("sender", "recipient", "subject", 00047 my_smtp_bodydh_fn, my_smtp_result_fn, some_argument); 00048 @endcode 00049 * 00050 * @todo: 00051 * - attachments (the main difficulty here is streaming base64-encoding to 00052 * prevent having to allocate a buffer for the whole encoded file at once) 00053 * - test with more mail servers... 00054 * 00055 */ 00056 00057 #include "lwip/apps/smtp.h" 00058 00059 #if LWIP_TCP && LWIP_CALLBACK_API 00060 #include "lwip/sys.h" 00061 #include "lwip/sockets.h" 00062 #include "lwip/altcp.h" 00063 #include "lwip/dns.h" 00064 #include "lwip/mem.h" 00065 #include "lwip/altcp_tcp.h" 00066 #include "lwip/altcp_tls.h" 00067 00068 #include <string.h> /* strlen, memcpy */ 00069 #include <stdlib.h> 00070 00071 /** TCP poll interval. Unit is 0.5 sec. */ 00072 #define SMTP_POLL_INTERVAL 4 00073 /** TCP poll timeout while sending message body, reset after every 00074 * successful write. 3 minutes */ 00075 #define SMTP_TIMEOUT_DATABLOCK ( 3 * 60 * SMTP_POLL_INTERVAL / 2) 00076 /** TCP poll timeout while waiting for confirmation after sending the body. 00077 * 10 minutes */ 00078 #define SMTP_TIMEOUT_DATATERM (10 * 60 * SMTP_POLL_INTERVAL / 2) 00079 /** TCP poll timeout while not sending the body. 00080 * This is somewhat lower than the RFC states (5 minutes for initial, MAIL 00081 * and RCPT) but still OK for us here. 00082 * 2 minutes */ 00083 #define SMTP_TIMEOUT ( 2 * 60 * SMTP_POLL_INTERVAL / 2) 00084 00085 /* the various debug levels for this file */ 00086 #define SMTP_DEBUG_TRACE (SMTP_DEBUG | LWIP_DBG_TRACE) 00087 #define SMTP_DEBUG_STATE (SMTP_DEBUG | LWIP_DBG_STATE) 00088 #define SMTP_DEBUG_WARN (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING) 00089 #define SMTP_DEBUG_WARN_STATE (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) 00090 #define SMTP_DEBUG_SERIOUS (SMTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS) 00091 00092 00093 #define SMTP_RX_BUF_LEN 255 00094 #define SMTP_TX_BUF_LEN 255 00095 #define SMTP_CRLF "\r\n" 00096 #define SMTP_CRLF_LEN 2 00097 00098 #define SMTP_RESP_220 "220" 00099 #define SMTP_RESP_235 "235" 00100 #define SMTP_RESP_250 "250" 00101 #define SMTP_RESP_334 "334" 00102 #define SMTP_RESP_354 "354" 00103 #define SMTP_RESP_LOGIN_UNAME "VXNlcm5hbWU6" 00104 #define SMTP_RESP_LOGIN_PASS "UGFzc3dvcmQ6" 00105 00106 #define SMTP_KEYWORD_AUTH_SP "AUTH " 00107 #define SMTP_KEYWORD_AUTH_EQ "AUTH=" 00108 #define SMTP_KEYWORD_AUTH_LEN 5 00109 #define SMTP_AUTH_PARAM_PLAIN "PLAIN" 00110 #define SMTP_AUTH_PARAM_LOGIN "LOGIN" 00111 00112 #define SMTP_CMD_EHLO_1 "EHLO [" 00113 #define SMTP_CMD_EHLO_1_LEN 6 00114 #define SMTP_CMD_EHLO_2 "]\r\n" 00115 #define SMTP_CMD_EHLO_2_LEN 3 00116 #define SMTP_CMD_AUTHPLAIN_1 "AUTH PLAIN " 00117 #define SMTP_CMD_AUTHPLAIN_1_LEN 11 00118 #define SMTP_CMD_AUTHPLAIN_2 "\r\n" 00119 #define SMTP_CMD_AUTHPLAIN_2_LEN 2 00120 #define SMTP_CMD_AUTHLOGIN "AUTH LOGIN\r\n" 00121 #define SMTP_CMD_AUTHLOGIN_LEN 12 00122 #define SMTP_CMD_MAIL_1 "MAIL FROM: <" 00123 #define SMTP_CMD_MAIL_1_LEN 12 00124 #define SMTP_CMD_MAIL_2 ">\r\n" 00125 #define SMTP_CMD_MAIL_2_LEN 3 00126 #define SMTP_CMD_RCPT_1 "RCPT TO: <" 00127 #define SMTP_CMD_RCPT_1_LEN 10 00128 #define SMTP_CMD_RCPT_2 ">\r\n" 00129 #define SMTP_CMD_RCPT_2_LEN 3 00130 #define SMTP_CMD_DATA "DATA\r\n" 00131 #define SMTP_CMD_DATA_LEN 6 00132 #define SMTP_CMD_HEADER_1 "From: <" 00133 #define SMTP_CMD_HEADER_1_LEN 7 00134 #define SMTP_CMD_HEADER_2 ">\r\nTo: <" 00135 #define SMTP_CMD_HEADER_2_LEN 8 00136 #define SMTP_CMD_HEADER_3 ">\r\nSubject: " 00137 #define SMTP_CMD_HEADER_3_LEN 12 00138 #define SMTP_CMD_HEADER_4 "\r\n\r\n" 00139 #define SMTP_CMD_HEADER_4_LEN 4 00140 #define SMTP_CMD_BODY_FINISHED "\r\n.\r\n" 00141 #define SMTP_CMD_BODY_FINISHED_LEN 5 00142 #define SMTP_CMD_QUIT "QUIT\r\n" 00143 #define SMTP_CMD_QUIT_LEN 6 00144 00145 #if defined(SMTP_STAT_TX_BUF_MAX) && SMTP_STAT_TX_BUF_MAX 00146 #define SMTP_TX_BUF_MAX(len) LWIP_MACRO(if((len) > smtp_tx_buf_len_max) smtp_tx_buf_len_max = (len);) 00147 #else /* SMTP_STAT_TX_BUF_MAX */ 00148 #define SMTP_TX_BUF_MAX(len) 00149 #endif /* SMTP_STAT_TX_BUF_MAX */ 00150 00151 #if SMTP_COPY_AUTHDATA 00152 #define SMTP_USERNAME(session) (session)->username 00153 #define SMTP_PASS(session) (session)->pass 00154 #define SMTP_AUTH_PLAIN_DATA(session) (session)->auth_plain 00155 #define SMTP_AUTH_PLAIN_LEN(session) (session)->auth_plain_len 00156 #else /* SMTP_COPY_AUTHDATA */ 00157 #define SMTP_USERNAME(session) smtp_username 00158 #define SMTP_PASS(session) smtp_pass 00159 #define SMTP_AUTH_PLAIN_DATA(session) smtp_auth_plain 00160 #define SMTP_AUTH_PLAIN_LEN(session) smtp_auth_plain_len 00161 #endif /* SMTP_COPY_AUTHDATA */ 00162 00163 #if SMTP_BODYDH 00164 #ifndef SMTP_BODYDH_MALLOC 00165 #define SMTP_BODYDH_MALLOC(size) mem_malloc(size) 00166 #define SMTP_BODYDH_FREE(ptr) mem_free(ptr) 00167 #endif 00168 00169 /* Some internal state return values */ 00170 #define BDHALLDATASENT 2 00171 #define BDHSOMEDATASENT 1 00172 00173 enum bdh_handler_state { 00174 BDH_SENDING, /* Serving the user function generating body content */ 00175 BDH_STOP /* User function stopped, closing */ 00176 }; 00177 #endif 00178 00179 /** State for SMTP client state machine */ 00180 enum smtp_session_state { 00181 SMTP_NULL, 00182 SMTP_HELO, 00183 SMTP_AUTH_PLAIN, 00184 SMTP_AUTH_LOGIN_UNAME, 00185 SMTP_AUTH_LOGIN_PASS, 00186 SMTP_AUTH_LOGIN, 00187 SMTP_MAIL, 00188 SMTP_RCPT, 00189 SMTP_DATA, 00190 SMTP_BODY, 00191 SMTP_QUIT, 00192 SMTP_CLOSED 00193 }; 00194 00195 #ifdef LWIP_DEBUG 00196 /** State-to-string table for debugging */ 00197 static const char *smtp_state_str[] = { 00198 "SMTP_NULL", 00199 "SMTP_HELO", 00200 "SMTP_AUTH_PLAIN", 00201 "SMTP_AUTH_LOGIN_UNAME", 00202 "SMTP_AUTH_LOGIN_PASS", 00203 "SMTP_AUTH_LOGIN", 00204 "SMTP_MAIL", 00205 "SMTP_RCPT", 00206 "SMTP_DATA", 00207 "SMTP_BODY", 00208 "SMTP_QUIT", 00209 "SMTP_CLOSED", 00210 }; 00211 00212 static const char *smtp_result_strs[] = { 00213 "SMTP_RESULT_OK", 00214 "SMTP_RESULT_ERR_UNKNOWN", 00215 "SMTP_RESULT_ERR_CONNECT", 00216 "SMTP_RESULT_ERR_HOSTNAME", 00217 "SMTP_RESULT_ERR_CLOSED", 00218 "SMTP_RESULT_ERR_TIMEOUT", 00219 "SMTP_RESULT_ERR_SVR_RESP", 00220 "SMTP_RESULT_ERR_MEM" 00221 }; 00222 #endif /* LWIP_DEBUG */ 00223 00224 #if SMTP_BODYDH 00225 struct smtp_bodydh_state { 00226 smtp_bodycback_fn callback_fn; /* The function to call (again) */ 00227 u16_t state; 00228 struct smtp_bodydh exposed; /* the user function structure */ 00229 }; 00230 #endif /* SMTP_BODYDH */ 00231 00232 /** struct keeping the body and state of an smtp session */ 00233 struct smtp_session { 00234 /** keeping the state of the smtp session */ 00235 enum smtp_session_state state; 00236 /** timeout handling, if this reaches 0, the connection is closed */ 00237 u16_t timer; 00238 /** helper buffer for transmit, not used for sending body */ 00239 char tx_buf[SMTP_TX_BUF_LEN + 1]; 00240 struct pbuf* p; 00241 /** source email address */ 00242 const char* from; 00243 /** size of the sourceemail address */ 00244 u16_t from_len; 00245 /** target email address */ 00246 const char* to; 00247 /** size of the target email address */ 00248 u16_t to_len; 00249 /** subject of the email */ 00250 const char *subject; 00251 /** length of the subject string */ 00252 u16_t subject_len; 00253 /** this is the body of the mail to be sent */ 00254 const char* body; 00255 /** this is the length of the body to be sent */ 00256 u16_t body_len; 00257 /** amount of data from body already sent */ 00258 u16_t body_sent; 00259 /** callback function to call when closed */ 00260 smtp_result_fn callback_fn; 00261 /** argument for callback function */ 00262 void *callback_arg; 00263 #if SMTP_COPY_AUTHDATA 00264 /** Username to use for this request */ 00265 char *username; 00266 /** Password to use for this request */ 00267 char *pass; 00268 /** Username and password combined as necessary for PLAIN authentication */ 00269 char auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3]; 00270 /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */ 00271 size_t auth_plain_len; 00272 #endif /* SMTP_COPY_AUTHDATA */ 00273 #if SMTP_BODYDH 00274 struct smtp_bodydh_state *bodydh; 00275 #endif /* SMTP_BODYDH */ 00276 }; 00277 00278 /** IP address or DNS name of the server to use for next SMTP request */ 00279 static char smtp_server[SMTP_MAX_SERVERNAME_LEN + 1]; 00280 /** TCP port of the server to use for next SMTP request */ 00281 static u16_t smtp_server_port = SMTP_DEFAULT_PORT; 00282 #if LWIP_ALTCP && LWIP_ALTCP_TLS 00283 /** If this is set, mail is sent using SMTPS */ 00284 static struct altcp_tls_config *smtp_server_tls_config; 00285 #endif 00286 /** Username to use for the next SMTP request */ 00287 static char *smtp_username; 00288 /** Password to use for the next SMTP request */ 00289 static char *smtp_pass; 00290 /** Username and password combined as necessary for PLAIN authentication */ 00291 static char smtp_auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3]; 00292 /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */ 00293 static size_t smtp_auth_plain_len; 00294 00295 #if SMTP_CHECK_DATA 00296 static err_t smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed); 00297 #endif /* SMTP_CHECK_DATA */ 00298 static err_t smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err); 00299 static void smtp_tcp_err(void *arg, err_t err); 00300 static err_t smtp_tcp_poll(void *arg, struct altcp_pcb *pcb); 00301 static err_t smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len); 00302 static err_t smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err); 00303 #if LWIP_DNS 00304 static void smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg); 00305 #endif /* LWIP_DNS */ 00306 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN 00307 static size_t smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len); 00308 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */ 00309 static enum smtp_session_state smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len); 00310 static void smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb); 00311 static void smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p); 00312 #if SMTP_BODYDH 00313 static void smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb); 00314 #endif /* SMTP_BODYDH */ 00315 00316 00317 #ifdef LWIP_DEBUG 00318 /** Convert an smtp result to a string */ 00319 const char* 00320 smtp_result_str(u8_t smtp_result) 00321 { 00322 if (smtp_result >= LWIP_ARRAYSIZE(smtp_result_strs)) { 00323 return "UNKNOWN"; 00324 } 00325 return smtp_result_strs[smtp_result]; 00326 } 00327 00328 /** Null-terminates the payload of p for printing out messages. 00329 * WARNING: use this only if p is not needed any more as the last byte of 00330 * payload is deleted! 00331 */ 00332 static const char* 00333 smtp_pbuf_str(struct pbuf* p) 00334 { 00335 if ((p == NULL) || (p->len == 0)) { 00336 return ""; 00337 } 00338 ((char*)p->payload)[p->len] = 0; 00339 return (const char*)p->payload; 00340 } 00341 #endif /* LWIP_DEBUG */ 00342 00343 /** @ingroup smtp 00344 * Set IP address or DNS name for next SMTP connection 00345 * 00346 * @param server IP address (in ASCII representation) or DNS name of the server 00347 */ 00348 err_t 00349 smtp_set_server_addr(const char* server) 00350 { 00351 size_t len = 0; 00352 00353 LWIP_ASSERT_CORE_LOCKED(); 00354 00355 if (server != NULL) { 00356 /* strlen: returns length WITHOUT terminating 0 byte */ 00357 len = strlen(server); 00358 } 00359 if (len > SMTP_MAX_SERVERNAME_LEN) { 00360 return ERR_MEM; 00361 } 00362 if (len != 0) { 00363 MEMCPY(smtp_server, server, len); 00364 } 00365 smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */ 00366 return ERR_OK; 00367 } 00368 00369 /** @ingroup smtp 00370 * Set TCP port for next SMTP connection 00371 * 00372 * @param port TCP port 00373 */ 00374 void 00375 smtp_set_server_port(u16_t port) 00376 { 00377 LWIP_ASSERT_CORE_LOCKED(); 00378 smtp_server_port = port; 00379 } 00380 00381 #if LWIP_ALTCP && LWIP_ALTCP_TLS 00382 /** @ingroup smtp 00383 * Set TLS configuration for next SMTP connection 00384 * 00385 * @param tls_config TLS configuration 00386 */ 00387 void 00388 smtp_set_tls_config(struct altcp_tls_config *tls_config) 00389 { 00390 LWIP_ASSERT_CORE_LOCKED(); 00391 smtp_server_tls_config = tls_config; 00392 } 00393 #endif 00394 00395 /** @ingroup smtp 00396 * Set authentication parameters for next SMTP connection 00397 * 00398 * @param username login name as passed to the server 00399 * @param pass password passed to the server together with username 00400 */ 00401 err_t 00402 smtp_set_auth(const char* username, const char* pass) 00403 { 00404 size_t uname_len = 0; 00405 size_t pass_len = 0; 00406 00407 LWIP_ASSERT_CORE_LOCKED(); 00408 00409 memset(smtp_auth_plain, 0xfa, 64); 00410 if (username != NULL) { 00411 uname_len = strlen(username); 00412 if (uname_len > SMTP_MAX_USERNAME_LEN) { 00413 LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n", 00414 (int)uname_len, SMTP_MAX_USERNAME_LEN)); 00415 return ERR_ARG; 00416 } 00417 } 00418 if (pass != NULL) { 00419 #if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN 00420 pass_len = strlen(pass); 00421 if (pass_len > SMTP_MAX_PASS_LEN) { 00422 LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n", 00423 (int)uname_len, SMTP_MAX_USERNAME_LEN)); 00424 return ERR_ARG; 00425 } 00426 #else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */ 00427 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n")); 00428 #endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */ 00429 } 00430 *smtp_auth_plain = 0; 00431 if (username != NULL) { 00432 smtp_username = smtp_auth_plain + 1; 00433 strcpy(smtp_username, username); 00434 } 00435 if (pass != NULL) { 00436 smtp_pass = smtp_auth_plain + uname_len + 2; 00437 strcpy(smtp_pass, pass); 00438 } 00439 smtp_auth_plain_len = uname_len + pass_len + 2; 00440 00441 return ERR_OK; 00442 } 00443 00444 #if SMTP_BODYDH 00445 static void smtp_free_struct(struct smtp_session *s) 00446 { 00447 if (s->bodydh != NULL) { 00448 SMTP_BODYDH_FREE(s->bodydh); 00449 } 00450 SMTP_STATE_FREE(s); 00451 } 00452 #else /* SMTP_BODYDH */ 00453 #define smtp_free_struct(x) SMTP_STATE_FREE(x) 00454 #endif /* SMTP_BODYDH */ 00455 00456 static struct altcp_pcb* 00457 smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip) 00458 { 00459 struct altcp_pcb* pcb; 00460 LWIP_UNUSED_ARG(remote_ip); 00461 00462 #if LWIP_ALTCP && LWIP_ALTCP_TLS 00463 if (smtp_server_tls_config) { 00464 pcb = altcp_tls_new(smtp_server_tls_config, IP_GET_TYPE(remote_ip)); 00465 } else 00466 #endif 00467 { 00468 pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip)); 00469 } 00470 if (pcb != NULL) { 00471 altcp_arg (pcb, s); 00472 altcp_recv (pcb, smtp_tcp_recv); 00473 altcp_err (pcb, smtp_tcp_err); 00474 altcp_poll (pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL); 00475 altcp_sent (pcb, smtp_tcp_sent); 00476 } 00477 return pcb; 00478 } 00479 00480 /** The actual mail-sending function, called by smtp_send_mail and 00481 * smtp_send_mail_static after setting up the struct smtp_session. 00482 */ 00483 static err_t 00484 smtp_send_mail_alloced(struct smtp_session *s) 00485 { 00486 err_t err; 00487 struct altcp_pcb* pcb = NULL; 00488 ip_addr_t addr; 00489 00490 LWIP_ASSERT("no smtp_session supplied", s != NULL); 00491 00492 #if SMTP_CHECK_DATA 00493 /* check that body conforms to RFC: 00494 * - convert all single-CR or -LF in body to CRLF 00495 * - only 7-bit ASCII is allowed 00496 */ 00497 if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) { 00498 err = ERR_ARG; 00499 goto leave; 00500 } 00501 if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) { 00502 err = ERR_ARG; 00503 goto leave; 00504 } 00505 if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) { 00506 err = ERR_ARG; 00507 goto leave; 00508 } 00509 #if SMTP_BODYDH 00510 if (s->bodydh == NULL) 00511 #endif /* SMTP_BODYDH */ 00512 { 00513 if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) { 00514 err = ERR_ARG; 00515 goto leave; 00516 } 00517 } 00518 #endif /* SMTP_CHECK_DATA */ 00519 00520 #if SMTP_COPY_AUTHDATA 00521 /* copy auth data, ensuring the first byte is always zero */ 00522 MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1); 00523 s->auth_plain_len = smtp_auth_plain_len; 00524 /* default username and pass is empty string */ 00525 s->username = s->auth_plain; 00526 s->pass = s->auth_plain; 00527 if (smtp_username != NULL) { 00528 s->username += smtp_username - smtp_auth_plain; 00529 } 00530 if (smtp_pass != NULL) { 00531 s->pass += smtp_pass - smtp_auth_plain; 00532 } 00533 #endif /* SMTP_COPY_AUTHDATA */ 00534 00535 s->state = SMTP_NULL; 00536 s->timer = SMTP_TIMEOUT; 00537 00538 #if LWIP_DNS 00539 err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s); 00540 #else /* LWIP_DNS */ 00541 err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG; 00542 #endif /* LWIP_DNS */ 00543 if (err == ERR_OK) { 00544 pcb = smtp_setup_pcb(s, &addr); 00545 if (pcb == NULL) { 00546 err = ERR_MEM; 00547 goto leave; 00548 } 00549 err = altcp_connect (pcb, &addr, smtp_server_port, smtp_tcp_connected); 00550 if (err != ERR_OK) { 00551 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err)); 00552 goto deallocate_and_leave; 00553 } 00554 } else if (err != ERR_INPROGRESS) { 00555 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err)); 00556 goto deallocate_and_leave; 00557 } 00558 return ERR_OK; 00559 00560 deallocate_and_leave: 00561 if (pcb != NULL) { 00562 altcp_arg (pcb, NULL); 00563 altcp_close (pcb); 00564 } 00565 leave: 00566 smtp_free_struct(s); 00567 /* no need to call the callback here since we return != ERR_OK */ 00568 return err; 00569 } 00570 00571 /** @ingroup smtp 00572 * Send an email via the currently selected server, username and password. 00573 * 00574 * @param from source email address (must be NULL-terminated) 00575 * @param to target email address (must be NULL-terminated) 00576 * @param subject email subject (must be NULL-terminated) 00577 * @param body email body (must be NULL-terminated) 00578 * @param callback_fn callback function 00579 * @param callback_arg user argument to callback_fn 00580 * @returns - ERR_OK if structures were allocated and no error occured starting the connection 00581 * (this does not mean the email has been successfully sent!) 00582 * - another err_t on error. 00583 */ 00584 err_t 00585 smtp_send_mail(const char* from, const char* to, const char* subject, const char* body, 00586 smtp_result_fn callback_fn, void* callback_arg) 00587 { 00588 struct smtp_session* s; 00589 size_t from_len = strlen(from); 00590 size_t to_len = strlen(to); 00591 size_t subject_len = strlen(subject); 00592 size_t body_len = strlen(body); 00593 size_t mem_len = sizeof(struct smtp_session); 00594 char *sfrom, *sto, *ssubject, *sbody; 00595 00596 LWIP_ASSERT_CORE_LOCKED(); 00597 00598 mem_len += from_len + to_len + subject_len + body_len + 4; 00599 if (mem_len > 0xffff) { 00600 /* too long! */ 00601 return ERR_MEM; 00602 } 00603 00604 /* Allocate memory to keep this email's session state */ 00605 s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len); 00606 if (s == NULL) { 00607 return ERR_MEM; 00608 } 00609 /* initialize the structure */ 00610 memset(s, 0, mem_len); 00611 s->from = sfrom = (char*)s + sizeof(struct smtp_session); 00612 s->from_len = (u16_t)from_len; 00613 s->to = sto = sfrom + from_len + 1; 00614 s->to_len = (u16_t)to_len; 00615 s->subject = ssubject = sto + to_len + 1; 00616 s->subject_len = (u16_t)subject_len; 00617 s->body = sbody = ssubject + subject_len + 1; 00618 s->body_len = (u16_t)body_len; 00619 /* copy source and target email address */ 00620 /* cast to size_t is a hack to cast away constness */ 00621 MEMCPY(sfrom, from, from_len + 1); 00622 MEMCPY(sto, to, to_len + 1); 00623 MEMCPY(ssubject, subject, subject_len + 1); 00624 MEMCPY(sbody, body, body_len + 1); 00625 00626 s->callback_fn = callback_fn; 00627 s->callback_arg = callback_arg; 00628 00629 /* call the actual implementation of this function */ 00630 return smtp_send_mail_alloced(s); 00631 } 00632 00633 /** @ingroup smtp 00634 * Same as smtp_send_mail, but doesn't copy from, to, subject and body into 00635 * an internal buffer to save memory. 00636 * WARNING: the above data must stay untouched until the callback function is 00637 * called (unless the function returns != ERR_OK) 00638 */ 00639 err_t 00640 smtp_send_mail_static(const char *from, const char* to, const char* subject, 00641 const char* body, smtp_result_fn callback_fn, void* callback_arg) 00642 { 00643 struct smtp_session* s; 00644 size_t len; 00645 00646 LWIP_ASSERT_CORE_LOCKED(); 00647 00648 s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session)); 00649 if (s == NULL) { 00650 return ERR_MEM; 00651 } 00652 memset(s, 0, sizeof(struct smtp_session)); 00653 /* initialize the structure */ 00654 s->from = from; 00655 len = strlen(from); 00656 LWIP_ASSERT("string is too long", len <= 0xffff); 00657 s->from_len = (u16_t)len; 00658 s->to = to; 00659 len = strlen(to); 00660 LWIP_ASSERT("string is too long", len <= 0xffff); 00661 s->to_len = (u16_t)len; 00662 s->subject = subject; 00663 len = strlen(subject); 00664 LWIP_ASSERT("string is too long", len <= 0xffff); 00665 s->subject_len = (u16_t)len; 00666 s->body = body; 00667 len = strlen(body); 00668 LWIP_ASSERT("string is too long", len <= 0xffff); 00669 s->body_len = (u16_t)len; 00670 s->callback_fn = callback_fn; 00671 s->callback_arg = callback_arg; 00672 /* call the actual implementation of this function */ 00673 return smtp_send_mail_alloced(s); 00674 } 00675 00676 00677 /** @ingroup smtp 00678 * Same as smtp_send_mail but takes a struct smtp_send_request as single 00679 * parameter which contains all the other parameters. 00680 * To be used with tcpip_callback to send mail from interrupt context or from 00681 * another thread. 00682 * 00683 * WARNING: server and authentication must stay untouched until this function has run! 00684 * 00685 * Usage example: 00686 * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context) 00687 * - fill the members of the struct as if calling smtp_send_mail 00688 * - specify a callback_function 00689 * - set callback_arg to the structure itself 00690 * - call this function 00691 * - wait for the callback function to be called 00692 * - in the callback function, deallocate the structure (passed as arg) 00693 */ 00694 void 00695 smtp_send_mail_int(void *arg) 00696 { 00697 struct smtp_send_request *req = (struct smtp_send_request*)arg; 00698 err_t err; 00699 00700 LWIP_ASSERT_CORE_LOCKED(); 00701 LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL); 00702 00703 if (req->static_data) { 00704 err = smtp_send_mail_static(req->from, req->to, req->subject, req->body, 00705 req->callback_fn, req->callback_arg); 00706 } else { 00707 err = smtp_send_mail(req->from, req->to, req->subject, req->body, 00708 req->callback_fn, req->callback_arg); 00709 } 00710 if ((err != ERR_OK) && (req->callback_fn != NULL)) { 00711 req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err); 00712 } 00713 } 00714 00715 #if SMTP_CHECK_DATA 00716 /** Verify that a given string conforms to the SMTP rules 00717 * (7-bit only, no single CR or LF, 00718 * @todo: no line consisting of a single dot only) 00719 */ 00720 static err_t 00721 smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed) 00722 { 00723 size_t i; 00724 u8_t last_was_cr = 0; 00725 for (i = 0; i < data_len; i++) { 00726 char current = data[i]; 00727 if ((current & 0x80) != 0) { 00728 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: no 8-bit data supported: %s\n", data)); 00729 return ERR_ARG; 00730 } 00731 if (current == '\r') { 00732 if (!linebreaks_allowed) { 00733 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found CR where no linebreaks allowed: %s\n", data)); 00734 return ERR_ARG; 00735 } 00736 if (last_was_cr) { 00737 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found double CR: %s\n", data)); 00738 return ERR_ARG; 00739 } 00740 last_was_cr = 1; 00741 } else { 00742 if (current == '\n') { 00743 if (!last_was_cr) { 00744 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found LF without CR before: %s\n", data)); 00745 return ERR_ARG; 00746 } 00747 } 00748 last_was_cr = 0; 00749 } 00750 } 00751 return ERR_OK; 00752 } 00753 #endif /* SMTP_CHECK_DATA */ 00754 00755 /** Frees the smtp_session and calls the callback function */ 00756 static void 00757 smtp_free(struct smtp_session *s, u8_t result, u16_t srv_err, err_t err) 00758 { 00759 smtp_result_fn fn = s->callback_fn; 00760 void *arg = s->callback_arg; 00761 if (s->p != NULL) { 00762 pbuf_free(s->p); 00763 } 00764 smtp_free_struct(s); 00765 if (fn != NULL) { 00766 fn(arg, result, srv_err, err); 00767 } 00768 } 00769 00770 /** Try to close a pcb and free the arg if successful */ 00771 static void 00772 smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result, 00773 u16_t srv_err, err_t err) 00774 { 00775 if (pcb != NULL) { 00776 altcp_arg (pcb, NULL); 00777 if (altcp_close (pcb) == ERR_OK) { 00778 if (s != NULL) { 00779 smtp_free(s, result, srv_err, err); 00780 } 00781 } else { 00782 /* close failed, set back arg */ 00783 altcp_arg (pcb, s); 00784 } 00785 } else { 00786 if (s != NULL) { 00787 smtp_free(s, result, srv_err, err); 00788 } 00789 } 00790 } 00791 00792 /** Raw API TCP err callback: pcb is already deallocated */ 00793 static void 00794 smtp_tcp_err(void *arg, err_t err) 00795 { 00796 LWIP_UNUSED_ARG(err); 00797 if (arg != NULL) { 00798 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_err: connection reset by remote host\n")); 00799 smtp_free((struct smtp_session*)arg, SMTP_RESULT_ERR_CLOSED, 0, err); 00800 } 00801 } 00802 00803 /** Raw API TCP poll callback */ 00804 static err_t 00805 smtp_tcp_poll(void *arg, struct altcp_pcb *pcb) 00806 { 00807 if (arg != NULL) { 00808 struct smtp_session *s = (struct smtp_session*)arg; 00809 if (s->timer != 0) { 00810 s->timer--; 00811 } 00812 } 00813 smtp_process(arg, pcb, NULL); 00814 return ERR_OK; 00815 } 00816 00817 /** Raw API TCP sent callback */ 00818 static err_t 00819 smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len) 00820 { 00821 LWIP_UNUSED_ARG(len); 00822 00823 smtp_process(arg, pcb, NULL); 00824 00825 return ERR_OK; 00826 } 00827 00828 /** Raw API TCP recv callback */ 00829 static err_t 00830 smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) 00831 { 00832 LWIP_UNUSED_ARG(err); 00833 if (p != NULL) { 00834 altcp_recved (pcb, p->tot_len); 00835 smtp_process(arg, pcb, p); 00836 } else { 00837 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_recv: connection closed by remote host\n")); 00838 smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CLOSED, 0, err); 00839 } 00840 return ERR_OK; 00841 } 00842 00843 static err_t 00844 smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err) 00845 { 00846 LWIP_UNUSED_ARG(arg); 00847 00848 if (err == ERR_OK) { 00849 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_connected: Waiting for 220\n")); 00850 } else { 00851 /* shouldn't happen, but we still check 'err', only to be sure */ 00852 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_connected: %d\n", (int)err)); 00853 smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CONNECT, 0, err); 00854 } 00855 return ERR_OK; 00856 } 00857 00858 #if LWIP_DNS 00859 /** DNS callback 00860 * If ipaddr is non-NULL, resolving succeeded, otherwise it failed. 00861 */ 00862 static void 00863 smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg) 00864 { 00865 struct smtp_session *s = (struct smtp_session*)arg; 00866 struct altcp_pcb *pcb; 00867 err_t err; 00868 u8_t result; 00869 00870 LWIP_UNUSED_ARG(hostname); 00871 00872 if (ipaddr != NULL) { 00873 pcb = smtp_setup_pcb(s, ipaddr); 00874 if (pcb != NULL) { 00875 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: hostname resolved, connecting\n")); 00876 err = altcp_connect (pcb, ipaddr, smtp_server_port, smtp_tcp_connected); 00877 if (err == ERR_OK) { 00878 return; 00879 } 00880 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err)); 00881 result = SMTP_RESULT_ERR_CONNECT; 00882 } else { 00883 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: failed to allocate tcp pcb\n")); 00884 result = SMTP_RESULT_ERR_MEM; 00885 err = ERR_MEM; 00886 } 00887 } else { 00888 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_dns_found: failed to resolve hostname: %s\n", 00889 hostname)); 00890 pcb = NULL; 00891 result = SMTP_RESULT_ERR_HOSTNAME; 00892 err = ERR_ARG; 00893 } 00894 smtp_close(s, pcb, result, 0, err); 00895 } 00896 #endif /* LWIP_DNS */ 00897 00898 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN 00899 00900 /** Table 6-bit-index-to-ASCII used for base64-encoding */ 00901 static const char base64_table[] = { 00902 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 00903 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 00904 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 00905 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 00906 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 00907 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 00908 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 00909 '+', '/' 00910 }; 00911 00912 /** Base64 encoding */ 00913 static size_t 00914 smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len) 00915 { 00916 size_t i; 00917 s8_t j; 00918 size_t target_idx = 0; 00919 size_t longer = (source_len % 3) ? (3 - (source_len % 3)) : 0; 00920 size_t source_len_b64 = source_len + longer; 00921 size_t len = (((source_len_b64) * 4) / 3); 00922 u8_t x = 5; 00923 u8_t current = 0; 00924 LWIP_UNUSED_ARG(target_len); 00925 00926 LWIP_ASSERT("target_len is too short", target_len >= len); 00927 00928 for (i = 0; i < source_len_b64; i++) { 00929 u8_t b = (i < source_len ? (u8_t)source[i] : 0); 00930 for (j = 7; j >= 0; j--, x--) { 00931 if ((b & (1 << j)) != 0) { 00932 current = (u8_t)(current | (1U << x)); 00933 } 00934 if (x == 0) { 00935 target[target_idx++] = base64_table[current]; 00936 x = 6; 00937 current = 0; 00938 } 00939 } 00940 } 00941 for (i = len - longer; i < len; i++) { 00942 target[i] = '='; 00943 } 00944 return len; 00945 } 00946 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */ 00947 00948 /** Parse pbuf to see if it contains the beginning of an answer. 00949 * If so, it returns the contained response code as number between 1 and 999. 00950 * If not, zero is returned. 00951 * 00952 * @param s smtp session struct 00953 */ 00954 static u16_t 00955 smtp_is_response(struct smtp_session *s) 00956 { 00957 char digits[4]; 00958 long num; 00959 00960 if (s->p == NULL) { 00961 return 0; 00962 } 00963 /* copy three digits and convert them to int */ 00964 if (pbuf_copy_partial(s->p, digits, 3, 0) != 3) { 00965 /* pbuf was too short */ 00966 return 0; 00967 } 00968 digits[3] = 0; 00969 num = strtol(digits, NULL, 10); 00970 if ((num <= 0) || (num >= 1000)) { 00971 /* failed to find response code at start of line */ 00972 return 0; 00973 } 00974 return (u16_t)num; 00975 } 00976 00977 /** Parse pbuf to see if it contains a fully received answer. 00978 * If one is found, ERR_OK is returned. 00979 * If none is found, ERR_VAL is returned. 00980 * 00981 * A fully received answer is a 3-digit number followed by a space, 00982 * some string and a CRLF as line ending. 00983 * 00984 * @param s smtp session struct 00985 */ 00986 static err_t 00987 smtp_is_response_finished(struct smtp_session *s) 00988 { 00989 u8_t sp; 00990 u16_t crlf; 00991 u16_t offset; 00992 00993 if (s->p == NULL) { 00994 return ERR_VAL; 00995 } 00996 offset = 0; 00997 again: 00998 /* We could check the response number here, but we trust the 00999 * protocol definition which says the client can rely on it being 01000 * the same on every line. */ 01001 01002 /* find CRLF */ 01003 if (offset > 0xFFFF - 4) { 01004 /* would overflow */ 01005 return ERR_VAL; 01006 } 01007 crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, (u16_t)(offset + 4)); 01008 if (crlf == 0xFFFF) { 01009 /* no CRLF found */ 01010 return ERR_VAL; 01011 } 01012 sp = pbuf_get_at(s->p, (u16_t)(offset + 3)); 01013 if (sp == '-') { 01014 /* no space after response code -> try next line */ 01015 offset = (u16_t)(crlf + 2); 01016 if (offset < crlf) { 01017 /* overflow */ 01018 return ERR_VAL; 01019 } 01020 goto again; 01021 } else if (sp == ' ') { 01022 /* CRLF found after response code + space -> valid response */ 01023 return ERR_OK; 01024 } 01025 /* sp contains invalid character */ 01026 return ERR_VAL; 01027 } 01028 01029 /** Prepare HELO/EHLO message */ 01030 static enum smtp_session_state 01031 smtp_prepare_helo(struct smtp_session *s, u16_t *tx_buf_len, struct altcp_pcb *pcb) 01032 { 01033 size_t ipa_len; 01034 const char *ipa = ipaddr_ntoa(altcp_get_ip(pcb, 1)); 01035 LWIP_ASSERT("ipaddr_ntoa returned NULL", ipa != NULL); 01036 ipa_len = strlen(ipa); 01037 LWIP_ASSERT("string too long", ipa_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_EHLO_1_LEN-SMTP_CMD_EHLO_2_LEN)); 01038 01039 *tx_buf_len = (u16_t)(SMTP_CMD_EHLO_1_LEN + (u16_t)ipa_len + SMTP_CMD_EHLO_2_LEN); 01040 LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN); 01041 01042 SMEMCPY(s->tx_buf, SMTP_CMD_EHLO_1, SMTP_CMD_EHLO_1_LEN); 01043 MEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN], ipa, ipa_len); 01044 SMEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN + ipa_len], SMTP_CMD_EHLO_2, SMTP_CMD_EHLO_2_LEN); 01045 return SMTP_HELO; 01046 } 01047 01048 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN 01049 /** Parse last server response (in rx_buf) for supported authentication method, 01050 * create data to send out (to tx_buf), set tx_data_len correctly 01051 * and return the next state. 01052 */ 01053 static enum smtp_session_state 01054 smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len) 01055 { 01056 /* check response for supported authentication method */ 01057 u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP); 01058 if (auth == 0xFFFF) { 01059 auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ); 01060 } 01061 if (auth != 0xFFFF) { 01062 u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth); 01063 if ((crlf != 0xFFFF) && (crlf > auth)) { 01064 /* use tx_buf temporarily */ 01065 u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, (u16_t)(crlf - auth), auth); 01066 if (copied != 0) { 01067 char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN; 01068 s->tx_buf[copied] = 0; 01069 #if SMTP_SUPPORT_AUTH_PLAIN 01070 /* favour PLAIN over LOGIN since it involves less requests */ 01071 if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) { 01072 size_t auth_len; 01073 /* server supports AUTH PLAIN */ 01074 SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN); 01075 01076 /* add base64-encoded string "\0username\0password" */ 01077 auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN], 01078 SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s), 01079 SMTP_AUTH_PLAIN_LEN(s)); 01080 LWIP_ASSERT("string too long", auth_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_AUTHPLAIN_1_LEN-SMTP_CMD_AUTHPLAIN_2_LEN)); 01081 *tx_buf_len = (u16_t)(SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len); 01082 SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2, 01083 SMTP_CMD_AUTHPLAIN_2_LEN); 01084 return SMTP_AUTH_PLAIN; 01085 } else 01086 #endif /* SMTP_SUPPORT_AUTH_PLAIN */ 01087 { 01088 #if SMTP_SUPPORT_AUTH_LOGIN 01089 if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) { 01090 /* server supports AUTH LOGIN */ 01091 *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN; 01092 SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN); 01093 return SMTP_AUTH_LOGIN_UNAME; 01094 } 01095 #endif /* SMTP_SUPPORT_AUTH_LOGIN */ 01096 } 01097 } 01098 } 01099 } 01100 /* server didnt's send correct keywords for AUTH, try sending directly */ 01101 return smtp_prepare_mail(s, tx_buf_len); 01102 } 01103 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */ 01104 01105 #if SMTP_SUPPORT_AUTH_LOGIN 01106 /** Send base64-encoded username */ 01107 static enum smtp_session_state 01108 smtp_prepare_auth_login_uname(struct smtp_session *s, u16_t *tx_buf_len) 01109 { 01110 size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN, 01111 SMTP_USERNAME(s), strlen(SMTP_USERNAME(s))); 01112 /* @todo: support base64-encoded longer than 64k */ 01113 LWIP_ASSERT("string too long", base64_len <= 0xffff); 01114 LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN); 01115 *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN); 01116 01117 SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN); 01118 s->tx_buf[*tx_buf_len] = 0; 01119 return SMTP_AUTH_LOGIN_PASS; 01120 } 01121 01122 /** Send base64-encoded password */ 01123 static enum smtp_session_state 01124 smtp_prepare_auth_login_pass(struct smtp_session *s, u16_t *tx_buf_len) 01125 { 01126 size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN, 01127 SMTP_PASS(s), strlen(SMTP_PASS(s))); 01128 /* @todo: support base64-encoded longer than 64k */ 01129 LWIP_ASSERT("string too long", base64_len <= 0xffff); 01130 LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN); 01131 *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN); 01132 01133 SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN); 01134 s->tx_buf[*tx_buf_len] = 0; 01135 return SMTP_AUTH_LOGIN; 01136 } 01137 #endif /* SMTP_SUPPORT_AUTH_LOGIN */ 01138 01139 /** Prepare MAIL message */ 01140 static enum smtp_session_state 01141 smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len) 01142 { 01143 char *target = s->tx_buf; 01144 LWIP_ASSERT("tx_buf overflow detected", s->from_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_MAIL_1_LEN - SMTP_CMD_MAIL_2_LEN)); 01145 *tx_buf_len = (u16_t)(SMTP_CMD_MAIL_1_LEN + SMTP_CMD_MAIL_2_LEN + s->from_len); 01146 target[*tx_buf_len] = 0; 01147 01148 SMEMCPY(target, SMTP_CMD_MAIL_1, SMTP_CMD_MAIL_1_LEN); 01149 target += SMTP_CMD_MAIL_1_LEN; 01150 MEMCPY(target, s->from, s->from_len); 01151 target += s->from_len; 01152 SMEMCPY(target, SMTP_CMD_MAIL_2, SMTP_CMD_MAIL_2_LEN); 01153 return SMTP_MAIL; 01154 } 01155 01156 /** Prepare RCPT message */ 01157 static enum smtp_session_state 01158 smtp_prepare_rcpt(struct smtp_session *s, u16_t *tx_buf_len) 01159 { 01160 char *target = s->tx_buf; 01161 LWIP_ASSERT("tx_buf overflow detected", s->to_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_RCPT_1_LEN - SMTP_CMD_RCPT_2_LEN)); 01162 *tx_buf_len = (u16_t)(SMTP_CMD_RCPT_1_LEN + SMTP_CMD_RCPT_2_LEN + s->to_len); 01163 target[*tx_buf_len] = 0; 01164 01165 SMEMCPY(target, SMTP_CMD_RCPT_1, SMTP_CMD_RCPT_1_LEN); 01166 target += SMTP_CMD_RCPT_1_LEN; 01167 MEMCPY(target, s->to, s->to_len); 01168 target += s->to_len; 01169 SMEMCPY(target, SMTP_CMD_RCPT_2, SMTP_CMD_RCPT_2_LEN); 01170 return SMTP_RCPT; 01171 } 01172 01173 /** Prepare header of body */ 01174 static enum smtp_session_state 01175 smtp_prepare_header(struct smtp_session *s, u16_t *tx_buf_len) 01176 { 01177 char *target = s->tx_buf; 01178 int len = SMTP_CMD_HEADER_1_LEN + SMTP_CMD_HEADER_2_LEN + 01179 SMTP_CMD_HEADER_3_LEN + SMTP_CMD_HEADER_4_LEN + s->from_len + s->to_len + 01180 s->subject_len; 01181 LWIP_ASSERT("tx_buf overflow detected", len > 0 && len <= SMTP_TX_BUF_LEN); 01182 *tx_buf_len = (u16_t)len; 01183 target[*tx_buf_len] = 0; 01184 01185 SMEMCPY(target, SMTP_CMD_HEADER_1, SMTP_CMD_HEADER_1_LEN); 01186 target += SMTP_CMD_HEADER_1_LEN; 01187 MEMCPY(target, s->from, s->from_len); 01188 target += s->from_len; 01189 SMEMCPY(target, SMTP_CMD_HEADER_2, SMTP_CMD_HEADER_2_LEN); 01190 target += SMTP_CMD_HEADER_2_LEN; 01191 MEMCPY(target, s->to, s->to_len); 01192 target += s->to_len; 01193 SMEMCPY(target, SMTP_CMD_HEADER_3, SMTP_CMD_HEADER_3_LEN); 01194 target += SMTP_CMD_HEADER_3_LEN; 01195 MEMCPY(target, s->subject, s->subject_len); 01196 target += s->subject_len; 01197 SMEMCPY(target, SMTP_CMD_HEADER_4, SMTP_CMD_HEADER_4_LEN); 01198 01199 return SMTP_BODY; 01200 } 01201 01202 /** Prepare QUIT message */ 01203 static enum smtp_session_state 01204 smtp_prepare_quit(struct smtp_session *s, u16_t *tx_buf_len) 01205 { 01206 *tx_buf_len = SMTP_CMD_QUIT_LEN; 01207 s->tx_buf[*tx_buf_len] = 0; 01208 SMEMCPY(s->tx_buf, SMTP_CMD_QUIT, SMTP_CMD_QUIT_LEN); 01209 LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN); 01210 return SMTP_CLOSED; 01211 } 01212 01213 /** If in state SMTP_BODY, try to send more body data */ 01214 static void 01215 smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb) 01216 { 01217 err_t err; 01218 01219 if (s->state == SMTP_BODY) { 01220 #if SMTP_BODYDH 01221 if (s->bodydh) { 01222 smtp_send_body_data_handler(s, pcb); 01223 } else 01224 #endif /* SMTP_BODYDH */ 01225 { 01226 u16_t send_len = (u16_t)(s->body_len - s->body_sent); 01227 if (send_len > 0) { 01228 u16_t snd_buf = altcp_sndbuf (pcb); 01229 if (send_len > snd_buf) { 01230 send_len = snd_buf; 01231 } 01232 if (send_len > 0) { 01233 /* try to send something out */ 01234 err = altcp_write (pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY); 01235 if (err == ERR_OK) { 01236 s->timer = SMTP_TIMEOUT_DATABLOCK; 01237 s->body_sent = (u16_t)(s->body_sent + send_len); 01238 if (s->body_sent < s->body_len) { 01239 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n", 01240 s->body_sent, s->body_len)); 01241 } 01242 } 01243 } 01244 } 01245 } 01246 if (s->body_sent == s->body_len) { 01247 /* the whole body has been written, write last line */ 01248 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n", 01249 s->body_len)); 01250 err = altcp_write (pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0); 01251 if (err == ERR_OK) { 01252 s->timer = SMTP_TIMEOUT_DATATERM; 01253 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n", 01254 smtp_state_str[SMTP_QUIT])); 01255 /* last line written, change state, wait for confirmation */ 01256 s->state = SMTP_QUIT; 01257 } 01258 } 01259 } 01260 } 01261 01262 /** State machine-like implementation of an SMTP client. 01263 */ 01264 static void 01265 smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p) 01266 { 01267 struct smtp_session* s = (struct smtp_session*)arg; 01268 u16_t response_code = 0; 01269 u16_t tx_buf_len = 0; 01270 enum smtp_session_state next_state; 01271 01272 if (arg == NULL) { 01273 /* already closed SMTP connection */ 01274 if (p != NULL) { 01275 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n", 01276 p->tot_len, smtp_pbuf_str(p))); 01277 pbuf_free(p); 01278 } 01279 return; 01280 } 01281 01282 next_state = s->state; 01283 01284 if (p != NULL) { 01285 /* received data */ 01286 if (s->p == NULL) { 01287 s->p = p; 01288 } else { 01289 pbuf_cat(s->p, p); 01290 } 01291 } else { 01292 /* idle timer, close connection if timed out */ 01293 if (s->timer == 0) { 01294 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n")); 01295 smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT); 01296 return; 01297 } 01298 if (s->state == SMTP_BODY) { 01299 smtp_send_body(s, pcb); 01300 return; 01301 } 01302 } 01303 response_code = smtp_is_response(s); 01304 if (response_code) { 01305 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code)); 01306 if (smtp_is_response_finished(s) != ERR_OK) { 01307 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code)); 01308 /* wait for next packet to complete the respone */ 01309 return; 01310 } 01311 } else { 01312 if (s->p != NULL) { 01313 LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n", 01314 smtp_pbuf_str(s->p))); 01315 pbuf_free(s->p); 01316 s->p = NULL; 01317 } 01318 return; 01319 } 01320 01321 switch(s->state) 01322 { 01323 case(SMTP_NULL): 01324 /* wait for 220 */ 01325 if (response_code == 220) { 01326 /* then send EHLO */ 01327 next_state = smtp_prepare_helo(s, &tx_buf_len, pcb); 01328 } 01329 break; 01330 case(SMTP_HELO): 01331 /* wait for 250 */ 01332 if (response_code == 250) { 01333 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN 01334 /* then send AUTH or MAIL */ 01335 next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len); 01336 } 01337 break; 01338 case(SMTP_AUTH_LOGIN): 01339 case(SMTP_AUTH_PLAIN): 01340 /* wait for 235 */ 01341 if (response_code == 235) { 01342 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */ 01343 /* send MAIL */ 01344 next_state = smtp_prepare_mail(s, &tx_buf_len); 01345 } 01346 break; 01347 #if SMTP_SUPPORT_AUTH_LOGIN 01348 case(SMTP_AUTH_LOGIN_UNAME): 01349 /* wait for 334 Username */ 01350 if (response_code == 334) { 01351 if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) { 01352 /* send username */ 01353 next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len); 01354 } 01355 } 01356 break; 01357 case(SMTP_AUTH_LOGIN_PASS): 01358 /* wait for 334 Password */ 01359 if (response_code == 334) { 01360 if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) { 01361 /* send username */ 01362 next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len); 01363 } 01364 } 01365 break; 01366 #endif /* SMTP_SUPPORT_AUTH_LOGIN */ 01367 case(SMTP_MAIL): 01368 /* wait for 250 */ 01369 if (response_code == 250) { 01370 /* send RCPT */ 01371 next_state = smtp_prepare_rcpt(s, &tx_buf_len); 01372 } 01373 break; 01374 case(SMTP_RCPT): 01375 /* wait for 250 */ 01376 if (response_code == 250) { 01377 /* send DATA */ 01378 SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN); 01379 tx_buf_len = SMTP_CMD_DATA_LEN; 01380 next_state = SMTP_DATA; 01381 } 01382 break; 01383 case(SMTP_DATA): 01384 /* wait for 354 */ 01385 if (response_code == 354) { 01386 /* send email header */ 01387 next_state = smtp_prepare_header(s, &tx_buf_len); 01388 } 01389 break; 01390 case(SMTP_BODY): 01391 /* nothing to be done here, handled somewhere else */ 01392 break; 01393 case(SMTP_QUIT): 01394 /* wait for 250 */ 01395 if (response_code == 250) { 01396 /* send QUIT */ 01397 next_state = smtp_prepare_quit(s, &tx_buf_len); 01398 } 01399 break; 01400 case(SMTP_CLOSED): 01401 /* nothing to do, wait for connection closed from server */ 01402 return; 01403 default: 01404 LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state, 01405 smtp_state_str[s->state])); 01406 break; 01407 } 01408 if (s->state == next_state) { 01409 LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n", 01410 smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p))); 01411 /* close connection */ 01412 smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK); 01413 return; 01414 } 01415 if (tx_buf_len > 0) { 01416 SMTP_TX_BUF_MAX(tx_buf_len); 01417 if (altcp_write (pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) { 01418 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n", 01419 smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p))); 01420 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n", 01421 smtp_state_str[s->state], tx_buf_len, s->tx_buf)); 01422 s->timer = SMTP_TIMEOUT; 01423 pbuf_free(s->p); 01424 s->p = NULL; 01425 LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n", 01426 smtp_state_str[s->state], smtp_state_str[next_state])); 01427 s->state = next_state; 01428 if (next_state == SMTP_BODY) { 01429 /* try to stream-send body data right now */ 01430 smtp_send_body(s, pcb); 01431 } else if (next_state == SMTP_CLOSED) { 01432 /* sent out all data, delete structure */ 01433 altcp_arg (pcb, NULL); 01434 smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK); 01435 } 01436 } 01437 } 01438 } 01439 01440 #if SMTP_BODYDH 01441 /** Elementary sub-function to send data 01442 * 01443 * @returns: BDHALLDATASENT all data has been written 01444 * BDHSOMEDATASENT some data has been written 01445 * 0 no data has been written 01446 */ 01447 static int 01448 smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany) 01449 { 01450 err_t err; 01451 u16_t len = *howmany; 01452 01453 len = (u16_t)LWIP_MIN(len, altcp_sndbuf (pcb)); 01454 err = altcp_write (pcb, *from, len, TCP_WRITE_FLAG_COPY); 01455 if (err == ERR_OK) { 01456 *from += len; 01457 if ((*howmany -= len) > 0) { 01458 return BDHSOMEDATASENT; 01459 } 01460 return BDHALLDATASENT; 01461 } 01462 return 0; 01463 } 01464 01465 /** Same as smtp_send_mail_static, but uses a callback function to send body data 01466 */ 01467 err_t 01468 smtp_send_mail_bodycback(const char *from, const char* to, const char* subject, 01469 smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg) 01470 { 01471 struct smtp_session* s; 01472 size_t len; 01473 01474 LWIP_ASSERT_CORE_LOCKED(); 01475 01476 s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session)); 01477 if (s == NULL) { 01478 return ERR_MEM; 01479 } 01480 memset(s, 0, sizeof(struct smtp_session)); 01481 s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state)); 01482 if (s->bodydh == NULL) { 01483 SMTP_STATE_FREE(s); 01484 return ERR_MEM; 01485 } 01486 memset(s->bodydh, 0, sizeof(struct smtp_bodydh_state)); 01487 /* initialize the structure */ 01488 s->from = from; 01489 len = strlen(from); 01490 LWIP_ASSERT("string is too long", len <= 0xffff); 01491 s->from_len = (u16_t)len; 01492 s->to = to; 01493 len = strlen(to); 01494 LWIP_ASSERT("string is too long", len <= 0xffff); 01495 s->to_len = (u16_t)len; 01496 s->subject = subject; 01497 len = strlen(subject); 01498 LWIP_ASSERT("string is too long", len <= 0xffff); 01499 s->subject_len = (u16_t)len; 01500 s->body = NULL; 01501 LWIP_ASSERT("string is too long", len <= 0xffff); 01502 s->callback_fn = callback_fn; 01503 s->callback_arg = callback_arg; 01504 s->bodydh->callback_fn = bodycback_fn; 01505 s->bodydh->state = BDH_SENDING; 01506 /* call the actual implementation of this function */ 01507 return smtp_send_mail_alloced(s); 01508 } 01509 01510 static void 01511 smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb) 01512 { 01513 struct smtp_bodydh_state *bdh; 01514 int res = 0, ret; 01515 LWIP_ASSERT("s != NULL", s != NULL); 01516 bdh = s->bodydh; 01517 LWIP_ASSERT("bodydh != NULL", bdh != NULL); 01518 01519 /* resume any leftovers from prior memory constraints */ 01520 if (s->body_len) { 01521 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: resume\n")); 01522 if((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) 01523 != BDHALLDATASENT) { 01524 s->body_sent = s->body_len - 1; 01525 return; 01526 } 01527 } 01528 ret = res; 01529 /* all data on buffer has been queued, resume execution */ 01530 if (bdh->state == BDH_SENDING) { 01531 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: run\n")); 01532 do { 01533 ret |= res; /* remember if we once queued something to send */ 01534 bdh->exposed.length = 0; 01535 if (bdh->callback_fn(s->callback_arg, &bdh->exposed) == BDH_DONE) { 01536 bdh->state = BDH_STOP; 01537 } 01538 s->body = bdh->exposed.buffer; 01539 s->body_len = bdh->exposed.length; 01540 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: trying to send %u bytes\n", (unsigned int)s->body_len)); 01541 } while (s->body_len && 01542 ((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) == BDHALLDATASENT) 01543 && (bdh->state != BDH_STOP)); 01544 } 01545 if ((bdh->state != BDH_SENDING) && (ret != BDHSOMEDATASENT)) { 01546 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: stop\n")); 01547 s->body_sent = s->body_len; 01548 } else { 01549 LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: pause\n")); 01550 s->body_sent = s->body_len - 1; 01551 } 01552 } 01553 #endif /* SMTP_BODYDH */ 01554 01555 #endif /* LWIP_TCP && LWIP_CALLBACK_API */
Generated on Tue Jul 12 2022 13:54:29 by
