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