valgrind: avoid complaints from plugin loading
[platform/upstream/libwebsockets.git] / plugins / protocol_esp32_lws_scan.c
1 /*
2  * ESP32 Scan / Factory protocol handler
3  *
4  * Copyright (C) 2017 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA*
20  *
21  */
22 #include <string.h>
23 #include <nvs.h>
24 #include <esp_ota_ops.h>
25
26 typedef enum {
27         SCAN_STATE_NONE,
28         SCAN_STATE_INITIAL,
29         SCAN_STATE_INITIAL_MANIFEST,
30         SCAN_STATE_KNOWN,
31         SCAN_STATE_LIST,
32         SCAN_STATE_FINAL
33 } scan_state;
34
35 struct store_json {
36         const char *j;
37         const char *nvs;
38 };
39
40 struct per_session_data__esplws_scan {
41         struct per_session_data__esplws_scan *next;
42         scan_state scan_state;
43         struct timeval last_send;
44
45         struct lws_spa *spa;
46         char filename[32];
47         char result[LWS_PRE + 512];
48         unsigned char buffer[4096];
49         int result_len;
50         int filename_length;
51         long file_length;
52         nvs_handle nvh;
53
54         char ap_record;
55         unsigned char subsequent:1;
56         unsigned char changed_partway:1;
57 };
58
59 struct per_vhost_data__esplws_scan {
60         wifi_ap_record_t ap_records[10];
61         TimerHandle_t timer, reboot_timer;
62         struct per_session_data__esplws_scan *live_pss_list;
63         struct lws_context *context;
64         struct lws_vhost *vhost;
65         const struct lws_protocols *protocol;
66
67         const esp_partition_t *part;
68         esp_ota_handle_t otahandle;
69         long file_length;
70         long content_length;
71
72         struct lws *cwsi;
73         char json[1024];
74         int json_len;
75
76         uint16_t count_ap_records;
77         char count_live_pss;
78         unsigned char scan_ongoing:1;
79         unsigned char completed_any_scan:1;
80         unsigned char reboot:1;
81         unsigned char changed_settings:1;
82         unsigned char checked_updates:1;
83         unsigned char autonomous_update:1;
84         unsigned char autonomous_update_sampled:1;
85 };
86
87 static const struct store_json store_json[] = {
88         { "\"ssid0\":\"", "0ssid" },
89         { ",\"pw0\":\"", "0password" },
90         { "\"ssid1\":\"", "1ssid" },
91         { ",\"pw1\":\"", "1password" },
92         { "\"ssid2\":\"", "2ssid" },
93         { ",\"pw2\":\"", "2password" },
94         { "\"ssid3\":\"", "3ssid" },
95         { ",\"pw3\":\"", "3password" },
96         { ",\"access_pw\":\"", "access_pw" },
97         { "{\"group\":\"", "group" },
98         { "{\"role\":\"", "role" },
99         { ",\"region\":\"", "region" },
100 };
101
102 static wifi_scan_config_t scan_config = {
103         .ssid = 0,
104         .bssid = 0,
105         .channel = 0,
106         .show_hidden = true
107 };
108
109 const esp_partition_t *
110 ota_choose_part(void);
111
112 static const char * const param_names[] = {
113         "text",
114         "pub",
115         "pri",
116         "serial",
117         "opts",
118 };
119
120 enum enum_param_names {
121         EPN_TEXT,
122         EPN_PUB,
123         EPN_PRI,
124         EPN_SERIAL,
125         EPN_OPTS,
126 };
127
128
129 static void
130 scan_finished(uint16_t count, wifi_ap_record_t *recs, void *v);
131
132 static int
133 esplws_simple_arg(char *dest, int len, const char *in, const char *match)
134 {
135        const char *p = strstr(in, match);
136        int n = 0;
137
138        if (!p)
139                return 1;
140
141        p += strlen(match);
142        while (*p && *p != '\"' && n < len - 1)
143                dest[n++] = *p++;
144        dest[n] = '\0';
145
146        return 0;
147 }
148
149 static void
150 scan_start(struct per_vhost_data__esplws_scan *vhd)
151 {
152         int n;
153
154         if (vhd->reboot)
155                 esp_restart();
156
157         if (vhd->scan_ongoing)
158                 return;
159
160         vhd->scan_ongoing = 1;
161         lws_esp32.scan_consumer = scan_finished;
162         lws_esp32.scan_consumer_arg = vhd;
163         n = esp_wifi_scan_start(&scan_config, false);
164         if (n != ESP_OK)
165                 lwsl_err("scan start failed %d\n", n);
166 }
167
168 static void timer_cb(TimerHandle_t t)
169 {
170         struct per_vhost_data__esplws_scan *vhd = pvTimerGetTimerID(t);
171
172         scan_start(vhd);
173 }
174
175 static void reboot_timer_cb(TimerHandle_t t)
176 {
177         esp_restart();
178 }
179
180 static int
181 client_connection(struct per_vhost_data__esplws_scan *vhd, const char *file)
182 {
183 #if CONFIG_LWS_IS_FACTORY_APPLICATION == 'y' && defined(CONFIG_LWS_OTA_SERVER_BASE_URL) && \
184     defined(CONFIG_LWS_OTA_SERVER_FQDN)
185         static struct lws_client_connect_info i;
186         char path[256];
187
188         memset(&i, 0, sizeof i);
189
190         snprintf(path, sizeof(path) - 1, CONFIG_LWS_OTA_SERVER_BASE_URL "/" CONFIG_LWS_MODEL_NAME "/%s", file);
191
192         lwsl_notice("Fetching %s\n", path);
193
194         i.port = 443;
195         i.context = vhd->context;
196         i.address = CONFIG_LWS_OTA_SERVER_FQDN;
197         i.ssl_connection = 1;
198         i.host = i.address;
199         i.origin = i.host;
200         i.vhost = vhd->vhost;
201         i.method = "GET";
202         i.path = path;
203         i.protocol = "esplws-scan";
204         i.pwsi = &vhd->cwsi;
205
206         vhd->cwsi = lws_client_connect_via_info(&i);
207         if (!vhd->cwsi) {
208                 lwsl_notice("NULL return\n");
209                 return 1; /* fail */
210         }
211 #endif
212         return 0; /* ongoing */
213 }
214
215 static void
216 scan_finished(uint16_t count, wifi_ap_record_t *recs, void *v)
217 {
218         struct per_vhost_data__esplws_scan *vhd = v;
219         struct per_session_data__esplws_scan *p = vhd->live_pss_list;
220
221         lwsl_notice("%s: count %d\n", __func__, count);
222
223         vhd->scan_ongoing = 0;
224
225         if (count < ARRAY_SIZE(vhd->ap_records))
226                 vhd->count_ap_records = count;
227         else
228                 vhd->count_ap_records = ARRAY_SIZE(vhd->ap_records);
229
230         memcpy(vhd->ap_records, recs, vhd->count_ap_records * sizeof(*recs));
231         
232         while (p) {
233                 if (p->scan_state != SCAN_STATE_INITIAL && p->scan_state != SCAN_STATE_NONE)
234                         p->changed_partway = 1;
235                 else
236                         p->scan_state = SCAN_STATE_INITIAL;
237                 p = p->next;
238         }
239
240         lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol);
241
242         if (lws_esp32.inet && !vhd->cwsi && !vhd->checked_updates)
243                 client_connection(vhd, "manifest.json");
244
245         if (vhd->changed_settings) {
246                 lws_esp32_wlan_nvs_get(1);
247                 vhd->changed_settings = 0;
248         } else
249                esp_wifi_connect();
250 }
251
252 static const char *ssl_names[] = { "ssl-pub.pem", "ssl-pri.pem" };
253
254 static int
255 file_upload_cb(void *data, const char *name, const char *filename,
256                char *buf, int len, enum lws_spa_fileupload_states state)
257 {
258         struct per_session_data__esplws_scan *pss =
259                         (struct per_session_data__esplws_scan *)data;
260         int n;
261
262         switch (state) {
263         case LWS_UFS_OPEN:
264                 if (lws_esp32_get_reboot_type() != LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON)
265                         return -1;
266
267                 lwsl_notice("LWS_UFS_OPEN Filename %s\n", filename);
268                 strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
269                 if (!strcmp(name, "pub") || !strcmp(name, "pri")) {
270                         if (nvs_open("lws-station", NVS_READWRITE, &pss->nvh))
271                                 return 1;
272                 } else
273                         return 1;
274                 pss->file_length = 0;
275                 break;
276
277         case LWS_UFS_FINAL_CONTENT:
278         case LWS_UFS_CONTENT:
279                 if (len) {
280                         /* if the file length is too big, drop it */
281                         if (pss->file_length + len > sizeof(pss->buffer))
282                                 return 1;
283
284                         memcpy(pss->buffer + pss->file_length, buf, len);
285                 }
286                 pss->file_length += len;
287
288                 if (state == LWS_UFS_CONTENT)
289                         break;
290
291                 lwsl_notice("LWS_UFS_FINAL_CONTENT\n");
292                 n = 0;
293                 if (!strcmp(name, "pri"))
294                         n = 1;
295                 n = nvs_set_blob(pss->nvh, ssl_names[n], pss->buffer, pss->file_length);
296                 if (n == ESP_OK)
297                         nvs_commit(pss->nvh);
298                 nvs_close(pss->nvh);
299                 if (n != ESP_OK)
300                         return 1;
301                 break;
302         }
303
304         return 0;
305 }
306
307 static int
308 callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason,
309                     void *user, void *in, size_t len)
310 {
311         struct per_session_data__esplws_scan *pss =
312                         (struct per_session_data__esplws_scan *)user;
313         struct per_vhost_data__esplws_scan *vhd =
314                         (struct per_vhost_data__esplws_scan *)
315                         lws_protocol_vh_priv_get(lws_get_vhost(wsi),
316                                         lws_get_protocol(wsi));
317         unsigned char *start = pss->buffer + LWS_PRE - 1, *p = start,
318                       *end = pss->buffer + sizeof(pss->buffer) - 1;
319         wifi_ap_record_t *r;
320         int n, m;
321         nvs_handle nvh;
322         size_t s;
323
324
325         switch (reason) {
326
327         case LWS_CALLBACK_PROTOCOL_INIT:
328                 vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
329                                 lws_get_protocol(wsi),
330                                 sizeof(struct per_vhost_data__esplws_scan));
331                 vhd->context = lws_get_context(wsi);
332                 vhd->protocol = lws_get_protocol(wsi);
333                 vhd->vhost = lws_get_vhost(wsi);
334                 vhd->timer = xTimerCreate("x", pdMS_TO_TICKS(10000), 1, vhd,
335                           (TimerCallbackFunction_t)timer_cb);
336                 vhd->scan_ongoing = 0;
337                 strcpy(vhd->json, " { }");
338                 scan_start(vhd);
339                 break;
340
341         case LWS_CALLBACK_PROTOCOL_DESTROY:
342                 if (!vhd)
343                         break;
344                 xTimerStop(vhd->timer, 0);
345                 xTimerDelete(vhd->timer, 0);
346                 break;
347
348         case LWS_CALLBACK_ESTABLISHED:
349                 lwsl_notice("%s: ESTABLISHED\n", __func__);
350                 if (!vhd->live_pss_list) {
351                         scan_start(vhd);
352                         xTimerStart(vhd->timer, 0);
353                 }
354                 vhd->count_live_pss++;
355                 pss->next = vhd->live_pss_list;
356                 vhd->live_pss_list = pss;
357                 /* if we have scan results, update them.  Otherwise wait */
358                 if (vhd->count_ap_records) {
359                         pss->scan_state = SCAN_STATE_INITIAL;
360                         lws_callback_on_writable(wsi);
361                 }
362                 break;
363
364         case LWS_CALLBACK_SERVER_WRITEABLE:
365
366                 if (vhd->autonomous_update_sampled) {
367                         p += snprintf((char *)p, end - p,
368                                       " {\n \"auton\":\"1\",\n \"pos\": \"%ld\",\n"
369                                       " \"len\":\"%ld\"\n}\n",
370                                         vhd->file_length,
371                                         vhd->content_length);
372
373                         n = LWS_WRITE_TEXT;
374                         goto issue;
375                 }
376
377                 switch (pss->scan_state) {
378                         struct timeval t;
379                         uint8_t mac[6];
380                         struct lws_esp32_image i;
381                         char img_factory[512], img_ota[512], group[16], role[16];
382                         int grt;
383
384                 case SCAN_STATE_INITIAL:
385
386                         gettimeofday(&t, NULL);
387                         if (t.tv_sec - pss->last_send.tv_sec < 10)
388                                 return 0;
389
390                         pss->last_send = t;
391
392                         if (nvs_open("lws-station", NVS_READWRITE, &nvh)) {
393                                 lwsl_err("unable to open nvs\n");
394                                 return -1;
395                         }
396                         n = 0;
397                         if (nvs_get_blob(nvh, "ssl-pub.pem", NULL, &s) == ESP_OK)
398                                 n = 1;
399                         if (nvs_get_blob(nvh, "ssl-pri.pem", NULL, &s) == ESP_OK)
400                                 n |= 2;
401                         s = sizeof(group) - 1;
402                         group[0] = '\0';
403                         role[0] = '\0';
404                         nvs_get_str(nvh, "group", group, &s);
405                         nvs_get_str(nvh, "role", role, &s);
406
407                         nvs_close(nvh);
408
409                         /*
410                          * this value in the JSON is just
411                          * used for UI indication.  Each conditional feature confirms
412                          * it itself before it allows itself to be used.
413                          */
414
415                         grt = lws_esp32_get_reboot_type();
416
417                         esp_efuse_mac_get_default(mac);
418                         strcpy(img_factory, " { \"date\": \"Empty\" }");
419                         strcpy(img_ota, " { \"date\": \"Empty\" }");
420
421                         lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP,
422                                 ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL), &i,
423                                 img_factory, sizeof(img_factory));
424                         lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP,
425                                 ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL), &i,
426                                 img_ota, sizeof(img_ota));
427
428                         p += snprintf((char *)p, end - p,
429                                       "{ \"model\":\"%s\",\n"
430                                       " \"forced_button\":\"%d\",\n"
431                                       " \"serial\":\"%s\",\n"
432                                       " \"opts\":\"%s\",\n"
433                                       " \"host\":\"%s-%s\",\n"
434                                       " \"region\":\"%d\",\n"
435                                       " \"ssl_pub\":\"%d\",\n"
436                                       " \"ssl_pri\":\"%d\",\n"
437                                       " \"mac\":\"%02X%02X%02X%02X%02X%02X\",\n"
438                                       " \"ssid\":\"%s\",\n"
439                                       " \"conn_ip\":\"%s\",\n"
440                                       " \"conn_mask\":\"%s\",\n"
441                                       " \"conn_gw\":\"%s\",\n"
442                                       " \"group\":\"%s\",\n"
443                                       " \"role\":\"%s\",\n"
444                                       " \"img_factory\": %s,\n"
445                                       " \"img_ota\": %s,\n",
446                                       lws_esp32.model,
447                                       grt == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON, 
448                                       lws_esp32.serial,
449                                       lws_esp32.opts,
450                                       lws_esp32.model, lws_esp32.serial,
451                                       lws_esp32.region,
452                                       n & 1, (n >> 1) & 1,
453                                       mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] | 1,
454                                       lws_esp32.active_ssid,
455                                       lws_esp32.sta_ip,
456                                       lws_esp32.sta_mask,
457                                       lws_esp32.sta_gw,
458                                       group, role,
459                                         img_factory,
460                                         img_ota
461                                       );
462
463                         n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;
464                         pss->scan_state = SCAN_STATE_INITIAL_MANIFEST;
465                         pss->ap_record = 0;
466                         pss->subsequent = 0;
467                         break;
468
469                 case SCAN_STATE_INITIAL_MANIFEST:
470                         p += snprintf((char *)p, end - p,
471                                       " \"latest\": %s,\n"
472                                       " \"inet\":\"%d\",\n",
473                                         vhd->json,
474                                       lws_esp32.inet
475                                       );
476
477                         p += snprintf((char *)p, end - p,
478                                       " \"known\":[\n");
479
480                         n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
481                         pss->scan_state = SCAN_STATE_KNOWN;
482                         break;
483
484                 case SCAN_STATE_KNOWN:
485                         if (nvs_open("lws-station", NVS_READONLY, &nvh)) {
486                                 lwsl_notice("unable to open nvh\n");
487                                 return -1;
488                         }
489
490                         for (m = 0; m < 4; m++) {
491                                 char name[10], ssid[32];
492                                 unsigned int pp = 0, use = 0;
493
494                                 if (m)
495                                         *p++ = ',';
496
497                                 s = sizeof(ssid) - 1;
498                                 ssid[0] = '\0';
499                                 lws_snprintf(name, sizeof(name) - 1, "%dssid", m);
500                                 nvs_get_str(nvh, name, ssid, &s);
501                                 lws_snprintf(name, sizeof(name) - 1, "%dpassword", m);
502                                 s = 10;
503                                 nvs_get_str(nvh, name, NULL, &s);
504                                 pp = !!s;
505                                 lws_snprintf(name, sizeof(name) - 1, "%duse", m);
506                                 nvs_get_u32(nvh, name, &use);
507
508                                 p += snprintf((char *)p, end - p,
509                                         "{\"ssid\":\"%s\",\n"
510                                         " \"pp\":\"%u\",\n"
511                                         "\"use\":\"%u\"}\n",
512                                         ssid, pp, use);
513                         }
514                         nvs_close(nvh);
515
516                         p += snprintf((char *)p, end - p,
517                                       "], \"aps\":[\n");
518
519                         n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
520                         pss->scan_state = SCAN_STATE_LIST;
521                         break;
522
523                 case SCAN_STATE_LIST:
524                         for (m = 0; m < 6; m++) {
525                                 n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
526                                 if (pss->ap_record >= vhd->count_ap_records)
527                                         goto scan_state_final;
528
529                                 if (pss->subsequent)
530                                         *p++ = ',';
531                                 pss->subsequent = 1;
532
533                                 r = &vhd->ap_records[(int)pss->ap_record++];
534                                 p += snprintf((char *)p, end - p,
535                                               "{\"ssid\":\"%s\",\n"
536                                                "\"bssid\":\"%02X:%02X:%02X:%02X:%02X:%02X\",\n"
537                                                "\"rssi\":\"%d\",\n"
538                                                "\"chan\":\"%d\",\n"
539                                                "\"auth\":\"%d\"}\n",
540                                                 r->ssid,
541                                                 r->bssid[0], r->bssid[1], r->bssid[2],
542                                                 r->bssid[3], r->bssid[4], r->bssid[5],
543                                                 r->rssi, r->primary, r->authmode);
544                                 if (pss->ap_record >= vhd->count_ap_records)
545                                         pss->scan_state = SCAN_STATE_FINAL;
546                         }
547                         break;
548
549                 case SCAN_STATE_FINAL:
550 scan_state_final:
551                         n = LWS_WRITE_CONTINUATION;
552                         p += sprintf((char *)p, "]\n}\n");
553                         if (pss->changed_partway) {
554                                 pss->subsequent = 0;
555                                 pss->scan_state = SCAN_STATE_INITIAL;
556                         } else {
557                                 pss->scan_state = SCAN_STATE_NONE;
558                                 vhd->autonomous_update_sampled = vhd->autonomous_update;
559                         }
560                         break;
561                 default:
562                         return 0;
563                 }
564 issue:
565 //              lwsl_notice("issue: %d (%d)\n", p - start, n);
566                 m = lws_write(wsi, (unsigned char *)start, p - start, n);
567                 if (m < 0) {
568                         lwsl_err("ERROR %d writing to di socket\n", m);
569                         return -1;
570                 }
571
572                 if (pss->scan_state != SCAN_STATE_NONE)
573                         lws_callback_on_writable(wsi);
574
575                 break;
576
577         case LWS_CALLBACK_RECEIVE:
578                 {
579                         const char *sect = "\"app\": {", *b;
580                         nvs_handle nvh;
581                         char p[64], use[6];
582                         int n, si = -1;
583
584                         if (strstr((const char *)in, "identify")) {
585                                 lws_esp32_identify_physical_device();
586                                 break;
587                         }
588
589                         if (vhd->json_len && strstr((const char *)in, "update-factory")) {
590                                 sect = "\"factory\": {";
591                                 goto auton;
592                         }
593                         if (vhd->json_len && strstr((const char *)in, "update-ota"))
594                                 goto auton;
595
596                         if (strstr((const char *)in, "\"reset\""))
597                                 goto sched_reset;
598
599                         if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) {
600                                 lwsl_err("Unable to open nvs\n");
601                                 break;
602                         }
603
604                         if (!esplws_simple_arg(p, sizeof(p), in, ",\"slot\":\""))
605                                 si = atoi(p);
606
607                         lwsl_notice("si %d\n", si);
608
609                         for (n = 0; n < ARRAY_SIZE(store_json); n++) {
610                                 if (esplws_simple_arg(p, sizeof(p), in, store_json[n].j))
611                                         continue;
612
613                                 /* only change access password if he has physical access to device */
614                                 if (n == 8 && lws_esp32_get_reboot_type() != LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON)
615                                         continue;
616
617                                 //lwsl_notice("%s: %s '%s'\n", __func__, store_json[n].nvs, p);
618                                 if (n == 9) {
619                                         strncpy(lws_esp32.group, p, sizeof(lws_esp32.group) - 1);
620                                         lws_esp32.group[sizeof(lws_esp32.group) - 1] = '\0';
621                                 }
622                                 if (n == 10) {
623                                         strncpy(lws_esp32.role, p, sizeof(lws_esp32.role) - 1);
624                                         lws_esp32.role[sizeof(lws_esp32.role) - 1] = '\0';
625                                 }
626
627                                 if (lws_nvs_set_str(nvh, store_json[n].nvs, p) != ESP_OK) {
628                                         lwsl_err("Unable to store %s in nvm\n", store_json[n].nvs);
629                                         goto bail_nvs;
630                                 }
631
632                                 if (si != -1 && n < 8) {
633                                         if (!(n & 1)) {
634                                                 strncpy(lws_esp32.ssid[(n >> 1) & 3], p,
635                                                                 sizeof(lws_esp32.ssid[0]));
636                                                 lws_esp32.ssid[(n >> 1) & 3]
637                                                         [sizeof(lws_esp32.ssid[0]) - 1] = '\0';
638                                                 lws_snprintf(use, sizeof(use) - 1, "%duse", si);
639                                                 lwsl_notice("resetting %s to 0\n", use);
640                                                 nvs_set_u32(nvh, use, 0);
641
642                                         } else {
643                                                 strncpy(lws_esp32.password[(n >> 1) & 3], p,
644                                                                 sizeof(lws_esp32.password[0]));
645                                                 lws_esp32.password[(n >> 1) & 3]
646                                                         [sizeof(lws_esp32.password[0]) - 1] = '\0';
647                                         }
648                                 }
649
650                         }
651
652                         nvs_commit(nvh);
653                         nvs_close(nvh);
654
655                         if (strstr((const char *)in, "\"factory-reset\"")) {
656                                 if (lws_esp32_get_reboot_type() ==
657                                         LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) {
658
659                                         lwsl_notice("Doing factory reset\n");
660                                         ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
661                                         n = nvs_erase_all(nvh);
662                                         if (n)
663                                                 lwsl_notice("erase_all failed %d\n", n);
664                                         nvs_commit(nvh);
665                                         nvs_close(nvh);
666
667                                         goto sched_reset;
668                                 } else
669                                         lwsl_notice("failed on factory button boot\n");
670                         }
671
672                         if (vhd->scan_ongoing)
673                                 vhd->changed_settings = 1;
674                         else
675                                 lws_esp32_wlan_nvs_get(1);
676
677                         lwsl_notice("set Join AP info\n");
678                         break;
679
680 bail_nvs:
681                         nvs_close(nvh);
682
683                         return 1;
684
685 sched_reset:
686                         vhd->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(250), 0, vhd,
687                                                 (TimerCallbackFunction_t)reboot_timer_cb);
688                         xTimerStart(vhd->reboot_timer, 0);
689
690                         return 1;
691
692 auton:
693                         lwsl_notice("Autonomous upload\n");
694                         b = strstr(vhd->json, sect);
695                         if (!b) {
696                                 lwsl_notice("Can't find %s in JSON\n", sect);
697                                 return 1;
698                         }
699                         b = strstr(b, "\"file\": \"");
700                         if (!b) {
701                                 lwsl_notice("Can't find \"file\": JSON\n");
702                                 return 1;
703                         }
704                         vhd->autonomous_update = 1;
705                         if (pss->scan_state == SCAN_STATE_NONE)
706                                 vhd->autonomous_update_sampled = 1;
707                         b += 9;
708                         n = 0;
709                         while ((*b != '\"') && n < sizeof(p) - 1)
710                                 p[n++] = *b++;
711
712                         p[n] = '\0';
713
714                         vhd->part = ota_choose_part();
715                         if (!vhd->part)
716                                 return 1;
717
718                         if (client_connection(vhd, p))
719                                 vhd->autonomous_update = 0;
720
721                         break;
722                 }
723
724         case LWS_CALLBACK_CLOSED:
725                 {
726                         struct per_session_data__esplws_scan **p = &vhd->live_pss_list;
727
728                         while (*p) {
729                                 if ((*p) == pss) {
730                                         *p = pss->next;
731                                         continue;
732                                 }
733
734                                 p = &((*p)->next);
735                         }
736
737                         vhd->count_live_pss--;
738                 }
739                 if (!vhd->live_pss_list)
740                         xTimerStop(vhd->timer, 0);
741                 break;
742
743         /* "factory" POST handling */
744
745         case LWS_CALLBACK_HTTP_BODY:
746                 /* create the POST argument parser if not already existing */
747                 lwsl_notice("LWS_CALLBACK_HTTP_BODY (scan)\n");
748                 if (!pss->spa) {
749                         pss->spa = lws_spa_create(wsi, param_names,
750                                         ARRAY_SIZE(param_names), 1024,
751                                         file_upload_cb, pss);
752                         if (!pss->spa)
753                                 return -1;
754
755                         pss->filename[0] = '\0';
756                         pss->file_length = 0;
757                 }
758
759                 /* let it parse the POST data */
760                 if (lws_spa_process(pss->spa, in, len))
761                         return -1;
762                 break;
763
764         case LWS_CALLBACK_HTTP_BODY_COMPLETION:
765                 lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION (scan)\n");
766                 /* call to inform no more payload data coming */
767                 lws_spa_finalize(pss->spa);
768
769                 if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) {
770                         lwsl_err("Unable to open nvs\n");
771                         break;
772                 }
773
774                 if (lws_esp32_get_reboot_type() == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) {
775
776                         if (lws_spa_get_string(pss->spa, EPN_SERIAL)) {
777                                 if (lws_nvs_set_str(nvh, "serial", lws_spa_get_string(pss->spa, EPN_SERIAL)) != ESP_OK) {
778                                         lwsl_err("Unable to store serial in nvm\n");
779                                         goto bail_nvs;
780                                 }
781                 
782                                 nvs_commit(nvh);
783                         }
784
785                         if (lws_spa_get_string(pss->spa, EPN_OPTS)) {
786                                 if (lws_nvs_set_str(nvh, "opts", lws_spa_get_string(pss->spa, EPN_OPTS)) != ESP_OK) {
787                                         lwsl_err("Unable to store options in nvm\n");
788                                         goto bail_nvs;
789                                 }
790                 
791                                 nvs_commit(nvh);
792                         }
793                 }
794                 nvs_close(nvh);
795
796                 pss->result_len = snprintf(pss->result + LWS_PRE, sizeof(pss->result) - LWS_PRE - 1,
797                                 "<html>Rebooting after storing certs...<br>connect to AP '<b>config-%s-%s</b>' and continue here: "
798                                 "<a href=\"https://192.168.4.1\">https://192.168.4.1</a></html>",
799                                 lws_esp32.model, lws_spa_get_string(pss->spa, EPN_SERIAL));
800
801                 if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
802                         goto bail;
803
804                 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
805                                 (unsigned char *)"text/html", 9, &p, end))
806                         goto bail;
807                 if (lws_add_http_header_content_length(wsi, pss->result_len, &p, end))
808                         goto bail;
809                 if (lws_finalize_http_header(wsi, &p, end))
810                         goto bail;
811
812                 n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
813                 if (n < 0)
814                         goto bail;
815
816                 lws_callback_on_writable(wsi);
817                 break;
818
819         case LWS_CALLBACK_HTTP_WRITEABLE:
820                 lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n",
821                            pss->result_len);
822                 n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
823                               pss->result_len, LWS_WRITE_HTTP);
824                 if (n < 0)
825                         return 1;
826
827                 vhd->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(3000), 0, vhd,
828                           (TimerCallbackFunction_t)reboot_timer_cb);
829                 xTimerStart(vhd->reboot_timer, 0);
830
831                 return 1; // hang up since we will reset
832
833         /* ----- client handling ----- */
834
835         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
836                 lwsl_notice("Client connection error %s\n", (char *)in);
837                 vhd->cwsi = NULL;
838                 break;
839
840         case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
841                 if (!vhd->autonomous_update)
842                         break;
843
844                 {
845                         char pp[20];
846
847                         if (lws_hdr_copy(wsi, pp, sizeof(pp) - 1, WSI_TOKEN_HTTP_CONTENT_LENGTH) < 0)
848                                 return -1;
849         
850                         vhd->content_length = atoi(pp);
851                         if (vhd->content_length <= 0 ||
852                             vhd->content_length > vhd->part->size)
853                                 return -1;
854
855                         if (esp_ota_begin(vhd->part, (long)-1, &vhd->otahandle) != ESP_OK) {
856                                 lwsl_err("OTA: Failed to begin\n");
857                                 return 1;
858                         }
859
860                         vhd->file_length = 0;
861                         break;
862                 }
863                 break;
864
865         case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
866                 //lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: %ld\n",
867                 //          (long)len);
868
869                 if (!vhd->autonomous_update) {
870                         if (sizeof(vhd->json) - vhd->json_len - 1 < len)
871                                 len = sizeof(vhd->json) - vhd->json_len - 1;
872                         memcpy(vhd->json + vhd->json_len, in, len);
873                         vhd->json_len += len;
874                         vhd->json[vhd->json_len] = '\0';
875                         break;
876                 }
877
878                 /* autonomous download */
879
880
881                 if (vhd->file_length + len > vhd->part->size) {
882                         lwsl_err("OTA: incoming file too large\n");
883                         goto abort_ota;
884                 }
885
886                 lwsl_debug("writing 0x%lx... 0x%lx\n",
887                            vhd->part->address + vhd->file_length,
888                            vhd->part->address + vhd->file_length + len);
889                 if (esp_ota_write(vhd->otahandle, in, len) != ESP_OK) {
890                         lwsl_err("OTA: Failed to write\n");
891                         goto abort_ota;
892                 }
893                 vhd->file_length += len;
894
895                 lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol);
896                 break;
897
898 abort_ota:
899                 esp_ota_end(vhd->otahandle);
900                 vhd->otahandle = 0;
901                 vhd->autonomous_update = 0;
902
903                 return 1;
904
905         case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
906                 {
907                         char *px = (char *)pss->buffer + LWS_PRE;
908                         int lenx = sizeof(pss->buffer) - LWS_PRE - 1;
909
910                         //lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP: %d\n", len);
911
912                         if (lws_http_client_read(wsi, &px, &lenx) < 0)
913                                 return -1;
914                 }
915                 break;
916
917         case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
918                 lwsl_notice("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
919                 vhd->cwsi = NULL;
920                 if (!vhd->autonomous_update) {
921
922                         vhd->checked_updates = 1;
923                         puts(vhd->json);
924                         return -1;
925                 }
926
927                 /* autonomous download */
928
929                 lwsl_notice("auton complete\n");
930
931                 if (esp_ota_end(vhd->otahandle) != ESP_OK) {
932                         lwsl_err("OTA: end failed\n");
933                         return 1;
934                 }
935
936                 if (esp_ota_set_boot_partition(vhd->part) != ESP_OK) {
937                         lwsl_err("OTA: set boot part failed\n");
938                         return 1;
939                 }
940                 vhd->otahandle = 0;
941                 vhd->autonomous_update = 0;
942
943                 vhd->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(250), 0, vhd,
944                           (TimerCallbackFunction_t)reboot_timer_cb);
945                         xTimerStart(vhd->reboot_timer, 0);
946                 return -1;
947
948         case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
949                 lwsl_notice("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n");
950                 break;
951
952         case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
953                 /* called when our wsi user_space is going to be destroyed */
954                 if (pss->spa) {
955                         lws_spa_destroy(pss->spa);
956                         pss->spa = NULL;
957                 }
958                 break;
959
960         default:
961                 break;
962         }
963
964         return 0;
965
966 bail:
967         return 1;
968 }
969
970 #define LWS_PLUGIN_PROTOCOL_ESPLWS_SCAN \
971         { \
972                 "esplws-scan", \
973                 callback_esplws_scan, \
974                 sizeof(struct per_session_data__esplws_scan), \
975                 1024, 0, NULL, 900 \
976         }
977