@@ -93,6 +93,7 @@ static const char http_html_filename[] = "enduser_setup.html";
9393static const char http_header_200 [] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html\r\n" ; /* Note single \r\n here! */
9494static const char http_header_204 [] = "HTTP/1.1 204 No Content\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
9595static const char http_header_302 [] = "HTTP/1.1 302 Moved\r\nLocation: /\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
96+ static const char http_header_302_trying [] = "HTTP/1.1 302 Moved\r\nLocation: /?trying=true\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
9697static const char http_header_400 [] = "HTTP/1.1 400 Bad request\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
9798static const char http_header_404 [] = "HTTP/1.1 404 Not found\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
9899static const char http_header_405 [] = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
@@ -101,8 +102,8 @@ static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\nContent-Le
101102static const char http_header_content_len_fmt [] = "Content-length:%5d\r\n\r\n" ;
102103static const char http_html_gzip_contentencoding [] = "Content-Encoding: gzip\r\n" ;
103104
104- /* Externally defined: static const char http_html_backup [] = ... */
105- #include "eus/http_html_backup. def"
105+ /* Externally defined: static const char enduser_setup_html_default [] = ... */
106+ #include "enduser_setup/enduser_setup.html.gz. def.h "
106107
107108typedef struct scan_listener
108109{
@@ -398,9 +399,9 @@ static err_t close_once_sent (void *arg, struct tcp_pcb *pcb, u16_t len)
398399/**
399400 * Search String
400401 *
401- * Search string for first occurance of any char in srch_str.
402+ * Search string for first occurence of any char in srch_str.
402403 *
403- * @return -1 iff no occurance of char was found.
404+ * @return -1 if no occurence of char was found.
404405 */
405406static int enduser_setup_srch_str (const char * str , const char * srch_str )
406407{
@@ -418,9 +419,9 @@ static int enduser_setup_srch_str(const char *str, const char *srch_str)
418419/**
419420 * Load HTTP Payload
420421 *
421- * @return - 0 iff payload loaded successfully
422- * 1 iff backup html was loaded
423- * 2 iff out of memory
422+ * @return - 0 if payload loaded successfully
423+ * 1 if default html was loaded
424+ * 2 if out of memory
424425 */
425426static int enduser_setup_http_load_payload (void )
426427{
@@ -466,16 +467,16 @@ static int enduser_setup_http_load_payload(void)
466467
467468 if (!f || err == VFS_RES_ERR || err2 == VFS_RES_ERR )
468469 {
469- ENDUSER_SETUP_DEBUG ("Unable to load file enduser_setup.html, loading backup HTML..." );
470+ ENDUSER_SETUP_DEBUG ("Unable to load file enduser_setup.html, loading default HTML..." );
470471
471- c_sprintf (cl_hdr , http_header_content_len_fmt , sizeof (http_html_backup ));
472+ c_sprintf (cl_hdr , http_header_content_len_fmt , sizeof (enduser_setup_html_default ));
472473 cl_len = c_strlen (cl_hdr );
473- int html_len = LITLEN (http_html_backup );
474+ int html_len = LITLEN (enduser_setup_html_default );
474475
475- if (http_html_backup [0 ] == 0x1f && http_html_backup [1 ] == 0x8b )
476+ if (enduser_setup_html_default [0 ] == 0x1f && enduser_setup_html_default [1 ] == 0x8b )
476477 {
477478 ce_len = c_strlen (http_html_gzip_contentencoding );
478- html_len = http_html_backup_len ; /* Defined in eus/http_html_backup. def by xxd -i */
479+ html_len = enduser_setup_html_default_len ; /* Defined in enduser_setup/enduser_setup.html.gz. def.h by xxd -i */
479480 ENDUSER_SETUP_DEBUG ("Content is gzipped" );
480481 }
481482
@@ -499,7 +500,7 @@ static int enduser_setup_http_load_payload(void)
499500
500501 c_memcpy (& (state -> http_payload_data [offset ]), & (cl_hdr ), cl_len );
501502 offset += cl_len ;
502- c_memcpy (& (state -> http_payload_data [offset ]), & (http_html_backup ), sizeof (http_html_backup ));
503+ c_memcpy (& (state -> http_payload_data [offset ]), & (enduser_setup_html_default ), sizeof (enduser_setup_html_default ));
503504
504505 return 1 ;
505506 }
@@ -548,7 +549,7 @@ static int enduser_setup_http_load_payload(void)
548549 *
549550 * Parse escaped and form encoded data of request.
550551 *
551- * @return - return 0 iff the HTTP parameter is decoded into a valid string.
552+ * @return - return 0 if the HTTP parameter is decoded into a valid string.
552553 */
553554static int enduser_setup_http_urldecode (char * dst , const char * src , int src_len , int dst_len )
554555{
@@ -631,13 +632,167 @@ static void do_station_cfg (task_param_t param, uint8_t prio)
631632 luaM_free (lua_getstate (), cnf );
632633}
633634
635+ /**
636+ * Count the number of occurences of a character in a string
637+ *
638+ * return the number of times the character was encountered in the string
639+ */
640+ static int count_char_occurence (const char * input , const char char_to_count ) {
641+ const char * current = input ;
642+ int occur = 0 ;
643+ while (* current != 0 ) {
644+ if (* current == char_to_count ) occur ++ ;
645+ current ++ ;
646+ }
647+ return occur ;
648+ }
649+
650+ /* structure used to store the key/value pairs that we find in a HTTP POST body */
651+ struct keypairs_t {
652+ char * * keypairs ;
653+ int keypairs_nb ;
654+ };
655+
656+ static void enduser_setup_free_keypairs (struct keypairs_t * kp ) {
657+ if (kp == NULL ) return ;
658+
659+ if (kp -> keypairs != NULL ) {
660+ for (int i = 0 ; i < kp -> keypairs_nb * 2 ; i ++ ) {
661+ os_free (kp -> keypairs [i ]);
662+ }
663+ }
664+ os_free (kp -> keypairs );
665+ os_free (kp );
666+ }
667+
668+ static struct keypairs_t * enduser_setup_alloc_keypairs (int kp_number ){
669+ struct keypairs_t * kp = os_malloc (sizeof (struct keypairs_t ));
670+ os_memset (kp , 0 , sizeof (struct keypairs_t ));
671+
672+ kp -> keypairs = os_malloc (kp_number * 2 * sizeof (char * ));
673+ kp -> keypairs_nb = kp_number ;
674+ return kp ;
675+ }
676+
677+ /**
678+ * Parses a form-urlencoded body into a struct keypairs_t, which contains an array of key,values strings and the size of the array.
679+ */
680+ static struct keypairs_t * enduser_setup_get_keypairs_from_form (char * form_body , int form_length ) {
681+ int keypair_nb = count_char_occurence (form_body , '&' ) + 1 ;
682+ int equal_nb = count_char_occurence (form_body , '=' );
683+
684+ if (keypair_nb == 1 && equal_nb == 0 ) {
685+ ENDUSER_SETUP_DEBUG ("No keypair in form body" );
686+ return NULL ;
687+ }
688+
689+ struct keypairs_t * kp = enduser_setup_alloc_keypairs (keypair_nb );
690+
691+ int current_idx = 0 ;
692+ int err ;
693+
694+ char * body_copy = os_malloc (form_length + 1 );
695+ os_bzero (body_copy , form_length + 1 );
696+ os_memcpy (body_copy , form_body , form_length );
697+ char * tok = strtok (body_copy , "=" );
698+
699+ char last_tok = '=' ;
700+ while (tok ) {
701+ size_t len = strlen (tok );
702+ kp -> keypairs [current_idx ] = os_malloc (len + 1 );
703+ err = enduser_setup_http_urldecode (kp -> keypairs [current_idx ], tok , len , len + 1 );
704+ if (err ) {
705+ ENDUSER_SETUP_DEBUG ("Unable to decode parameter" );
706+ enduser_setup_free_keypairs (kp );
707+ os_free (body_copy );
708+ return NULL ;
709+ }
710+
711+ current_idx ++ ;
712+ if (current_idx > keypair_nb * 2 ) {
713+ ENDUSER_SETUP_DEBUG ("Too many keypairs!" );
714+ enduser_setup_free_keypairs (kp );
715+ os_free (body_copy );
716+ return NULL ;
717+ }
718+
719+ if (last_tok == '=' ) {
720+ tok = strtok (NULL , "&" ); // now search for the '&'
721+ last_tok = '&' ;
722+ } else {
723+ tok = strtok (NULL , "=" ); // search for the next '='
724+ last_tok = '=' ;
725+ }
726+ }
727+ os_free (body_copy );
728+ return kp ;
729+ }
730+
731+
732+ /**
733+ * This function saves the form data received when the configuration is sent to the ESP into a eus_params.lua file
734+ */
735+ static int enduser_setup_write_file_with_extra_configuration_data (char * form_body , int form_length ) {
736+ ENDUSER_SETUP_DEBUG ("enduser: write data from posted form" );
737+ ENDUSER_SETUP_DEBUG (form_body );
738+
739+ // We will save the form data into a file in the LUA format: KEY="VALUE", so that configuration data is available for load in the lua code.
740+ // As input, we have a string as such: "key1=value1&key2=value2&key3=value%203" (urlencoded), the number of '&' tells us how many keypairs there are (the count + 1)
741+
742+ struct keypairs_t * kp = enduser_setup_get_keypairs_from_form (form_body , form_length );
743+ if (kp == NULL || kp -> keypairs_nb == 0 ) {
744+ ENDUSER_SETUP_DEBUG ("enduser: No extra configuration." );
745+ if (kp != NULL ) enduser_setup_free_keypairs (kp );
746+ return 1 ;
747+ }
748+
749+ // Now that we have the keys and the values, let's save them in a lua file
750+ int p_file = vfs_open ("eus_params.lua" , "w" );
751+ if (p_file == 0 )
752+ {
753+ ENDUSER_SETUP_DEBUG ("Can't open file in write mode!" );
754+ enduser_setup_free_keypairs (kp );
755+ return 1 ;
756+ }
757+
758+ // write all key pairs as KEY="VALUE"\n into a Lua table, example:
759+ // local p = {}
760+ // p.wifi_ssid="ssid"
761+ // p.wifi_password="password"
762+ // p.device_name="foo-node"
763+ // return p
764+ vfs_write (p_file , "local p={}\n" , 11 );
765+ int idx = 0 ;
766+ for ( idx = 0 ; idx < kp -> keypairs_nb * 2 ; idx = idx + 2 ){
767+ char * to_write = kp -> keypairs [idx ];
768+ size_t length = c_strlen (to_write );
769+
770+ vfs_write (p_file , "p." , 2 );
771+
772+ vfs_write (p_file , to_write , length );
773+
774+ vfs_write (p_file , "=\"" , 2 );
775+
776+ to_write = kp -> keypairs [idx + 1 ];
777+ length = c_strlen (to_write );
778+ vfs_write (p_file , to_write , length );
779+
780+ vfs_write (p_file , "\"\n" , 2 );
781+ }
782+ vfs_write (p_file , "return p\n" , 9 );
783+
784+ vfs_close (p_file );
785+ enduser_setup_free_keypairs (kp );
786+ // TODO: we could call back in the LUA with an associative table setup, but this is MVP2...
787+ return 0 ;
788+ }
634789
635790/**
636791 * Handle HTTP Credentials
637792 *
638- * @return - return 0 iff credentials are found and handled successfully
639- * return 1 iff credentials aren't found
640- * return 2 iff an error occured
793+ * @return - return 0 if credentials are found and handled successfully
794+ * return 1 if credentials aren't found
795+ * return 2 if an error occured
641796 */
642797static int enduser_setup_http_handle_credentials (char * data , unsigned short data_len )
643798{
@@ -682,7 +837,6 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data
682837 return 1 ;
683838 }
684839
685-
686840 ENDUSER_SETUP_DEBUG ("" );
687841 ENDUSER_SETUP_DEBUG ("WiFi Credentials Stored" );
688842 ENDUSER_SETUP_DEBUG ("-----------------------" );
@@ -702,7 +856,7 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data
702856/**
703857 * Serve HTML
704858 *
705- * @return - return 0 iff html was served successfully
859+ * @return - return 0 if html was served successfully
706860 */
707861static int enduser_setup_http_serve_header (struct tcp_pcb * http_client , const char * header , uint32_t header_len )
708862{
@@ -763,7 +917,7 @@ static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len)
763917/**
764918 * Serve HTML
765919 *
766- * @return - return 0 iff html was served successfully
920+ * @return - return 0 if html was served successfully
767921 */
768922static int enduser_setup_http_serve_html (struct tcp_pcb * http_client )
769923{
@@ -957,6 +1111,37 @@ static void enduser_setup_handle_OPTIONS (struct tcp_pcb *http_client, char *dat
9571111}
9581112
9591113
1114+ static err_t enduser_setup_handle_POST (struct tcp_pcb * http_client , char * data , size_t data_len )
1115+ {
1116+ ENDUSER_SETUP_DEBUG ("Handling POST" );
1117+ if (c_strncmp (data + 5 , "/setwifi " , 9 ) == 0 ) // User clicked the submit button
1118+ {
1119+ switch (enduser_setup_http_handle_credentials (data , data_len ))
1120+ {
1121+ case 0 : {
1122+ // all went fine, extract all the form data into a file
1123+ char * body = strstr (data , "\r\n\r\n" );
1124+ char * content_length_str = strstr (data , "Content-Length: " );
1125+ if ( body != NULL && content_length_str != NULL ){
1126+ int bodylength = c_atoi (content_length_str + 16 );
1127+ body += 4 ; // length of the double CRLF found above
1128+ enduser_setup_write_file_with_extra_configuration_data (body , bodylength );
1129+ }
1130+ // redirect user to the base page with the trying flag
1131+ enduser_setup_http_serve_header (http_client , http_header_302_trying , LITLEN (http_header_302_trying ));
1132+ break ;
1133+ }
1134+ case 1 :
1135+ enduser_setup_http_serve_header (http_client , http_header_400 , LITLEN (http_header_400 ));
1136+ break ;
1137+ default :
1138+ ENDUSER_SETUP_ERROR ("http_recvcb failed. Failed to handle wifi credentials." , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_NONFATAL );
1139+ break ;
1140+ }
1141+ }
1142+ }
1143+
1144+
9601145/* --- WiFi AP scanning support -------------------------------------------- */
9611146
9621147static void free_scan_listeners (void )
@@ -1165,7 +1350,7 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
11651350
11661351 if (c_strncmp (data , "GET " , 4 ) == 0 )
11671352 {
1168- if (c_strncmp (data + 4 , "/ " , 2 ) == 0 )
1353+ if (c_strncmp (data + 4 , "/ " , 2 ) == 0 || c_strncmp ( data + 4 , "/?" , 2 ) == 0 )
11691354 {
11701355 if (enduser_setup_http_serve_html (http_client ) != 0 )
11711356 {
@@ -1237,21 +1422,6 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
12371422 break ;
12381423 }
12391424 }
1240- else if (c_strncmp (data + 4 , "/setwifi?" , 9 ) == 0 )
1241- {
1242- switch (enduser_setup_http_handle_credentials (data , data_len ))
1243- {
1244- case 0 :
1245- enduser_setup_serve_status_as_json (http_client );
1246- break ;
1247- case 1 :
1248- enduser_setup_http_serve_header (http_client , http_header_400 , LITLEN (http_header_400 ));
1249- break ;
1250- default :
1251- ENDUSER_SETUP_ERROR ("http_recvcb failed. Failed to handle wifi credentials." , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_NONFATAL );
1252- break ;
1253- }
1254- }
12551425 else if (c_strncmp (data + 4 , "/generate_204" , 13 ) == 0 )
12561426 {
12571427 /* Convince Android devices that they have internet access to avoid pesky dialogues. */
@@ -1267,6 +1437,10 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
12671437 {
12681438 enduser_setup_handle_OPTIONS (http_client , data , data_len );
12691439 }
1440+ else if (c_strncmp (data , "POST " , 5 ) == 0 )
1441+ {
1442+ enduser_setup_handle_POST (http_client , data , data_len );
1443+ }
12701444 else /* not GET or OPTIONS */
12711445 {
12721446 enduser_setup_http_serve_header (http_client , http_header_405 , LITLEN (http_header_405 ));
@@ -1279,6 +1453,7 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
12791453 return ret ;
12801454}
12811455
1456+
12821457static err_t enduser_setup_http_connectcb (void * arg , struct tcp_pcb * pcb , err_t err )
12831458{
12841459 ENDUSER_SETUP_DEBUG ("enduser_setup_http_connectcb" );
@@ -1332,7 +1507,7 @@ static int enduser_setup_http_start(void)
13321507 int err = enduser_setup_http_load_payload ();
13331508 if (err == 1 )
13341509 {
1335- ENDUSER_SETUP_DEBUG ("enduser_setup_http_start info. Loaded backup HTML." );
1510+ ENDUSER_SETUP_DEBUG ("enduser_setup_http_start info. Loaded default HTML." );
13361511 }
13371512 else if (err == 2 )
13381513 {
0 commit comments