Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_smtp.c Source File

lwip_smtp.c

Go to the documentation of this file.
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 */