Device management with online dashboard See https://freshen.cc for full description

Committer:
lsm
Date:
Tue Apr 10 06:50:09 2018 +0000
Revision:
0:b21e585b3aa1
Version 1.3

Who changed what in which revision?

UserRevisionLine numberNew contents of line
lsm 0:b21e585b3aa1 1 /*
lsm 0:b21e585b3aa1 2 * Copyright (c) 2014-2018 Cesanta Software Limited
lsm 0:b21e585b3aa1 3 * All rights reserved
lsm 0:b21e585b3aa1 4 *
lsm 0:b21e585b3aa1 5 * Licensed to the Apache Software Foundation (ASF) under one
lsm 0:b21e585b3aa1 6 * or more contributor license agreements. See the NOTICE file
lsm 0:b21e585b3aa1 7 * distributed with this work for additional information
lsm 0:b21e585b3aa1 8 * regarding copyright ownership. The ASF licenses this file
lsm 0:b21e585b3aa1 9 * to you under the Apache License, Version 2.0 (the
lsm 0:b21e585b3aa1 10 * "License"); you may not use this file except in compliance
lsm 0:b21e585b3aa1 11 * with the License. You may obtain a copy of the License at
lsm 0:b21e585b3aa1 12 *
lsm 0:b21e585b3aa1 13 * http://www.apache.org/licenses/LICENSE-2.0
lsm 0:b21e585b3aa1 14 *
lsm 0:b21e585b3aa1 15 * Unless required by applicable law or agreed to in writing,
lsm 0:b21e585b3aa1 16 * software distributed under the License is distributed on an
lsm 0:b21e585b3aa1 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
lsm 0:b21e585b3aa1 18 * KIND, either express or implied. See the License for the
lsm 0:b21e585b3aa1 19 * specific language governing permissions and limitations
lsm 0:b21e585b3aa1 20 * under the License.
lsm 0:b21e585b3aa1 21 */
lsm 0:b21e585b3aa1 22
lsm 0:b21e585b3aa1 23 #define FRESHEN_VERSION "1.3"
lsm 0:b21e585b3aa1 24
lsm 0:b21e585b3aa1 25 #ifndef FRESHEN_H
lsm 0:b21e585b3aa1 26 #define FRESHEN_H
lsm 0:b21e585b3aa1 27
lsm 0:b21e585b3aa1 28 #ifdef __cplusplus
lsm 0:b21e585b3aa1 29 extern "C" {
lsm 0:b21e585b3aa1 30 #endif /* __cplusplus */
lsm 0:b21e585b3aa1 31
lsm 0:b21e585b3aa1 32 #include <stddef.h>
lsm 0:b21e585b3aa1 33
lsm 0:b21e585b3aa1 34 #define FRESHEN_API extern
lsm 0:b21e585b3aa1 35
lsm 0:b21e585b3aa1 36 /* Common JSON-RPC error codes */
lsm 0:b21e585b3aa1 37 #define FRESHEN_ERROR_INVALID -32700 /* Invalid JSON was received */
lsm 0:b21e585b3aa1 38 #define FRESHEN_ERROR_NOT_FOUND -32601 /* The method does not exist */
lsm 0:b21e585b3aa1 39 #define FRESHEN_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
lsm 0:b21e585b3aa1 40
lsm 0:b21e585b3aa1 41 #if __STDC_VERSION__ >= 201112L
lsm 0:b21e585b3aa1 42 #include <stdatomic.h>
lsm 0:b21e585b3aa1 43 typedef atomic_flag freshen_spinlock_t;
lsm 0:b21e585b3aa1 44 #define FRESHEN_SPINLOCK_INIT ATOMIC_FLAG_INIT
lsm 0:b21e585b3aa1 45 static void freshen_spinlock_lock(freshen_spinlock_t *lock) {
lsm 0:b21e585b3aa1 46 while (atomic_flag_test_and_set(lock))
lsm 0:b21e585b3aa1 47 ;
lsm 0:b21e585b3aa1 48 }
lsm 0:b21e585b3aa1 49 static void freshen_spinlock_unlock(freshen_spinlock_t *lock) {
lsm 0:b21e585b3aa1 50 atomic_flag_clear(lock);
lsm 0:b21e585b3aa1 51 }
lsm 0:b21e585b3aa1 52 #else
lsm 0:b21e585b3aa1 53 typedef int freshen_spinlock_t;
lsm 0:b21e585b3aa1 54 #define FRESHEN_SPINLOCK_INIT 0
lsm 0:b21e585b3aa1 55 static void freshen_spinlock_lock(freshen_spinlock_t *lock) {
lsm 0:b21e585b3aa1 56 }
lsm 0:b21e585b3aa1 57 static void freshen_spinlock_unlock(freshen_spinlock_t *lock) {
lsm 0:b21e585b3aa1 58 }
lsm 0:b21e585b3aa1 59 #endif /* __STDC_VERSION__ */
lsm 0:b21e585b3aa1 60
lsm 0:b21e585b3aa1 61 /* Append-only buffer of limited capacity */
lsm 0:b21e585b3aa1 62 struct freshen_buf {
lsm 0:b21e585b3aa1 63 void *ptr;
lsm 0:b21e585b3aa1 64 size_t len;
lsm 0:b21e585b3aa1 65 size_t cap;
lsm 0:b21e585b3aa1 66 int overflow;
lsm 0:b21e585b3aa1 67 };
lsm 0:b21e585b3aa1 68
lsm 0:b21e585b3aa1 69 struct freshen_queue {
lsm 0:b21e585b3aa1 70 void *buf;
lsm 0:b21e585b3aa1 71 size_t buf_sz;
lsm 0:b21e585b3aa1 72 size_t tail;
lsm 0:b21e585b3aa1 73 freshen_spinlock_t lock;
lsm 0:b21e585b3aa1 74 };
lsm 0:b21e585b3aa1 75
lsm 0:b21e585b3aa1 76 struct freshen_method {
lsm 0:b21e585b3aa1 77 const char *method;
lsm 0:b21e585b3aa1 78 size_t method_sz;
lsm 0:b21e585b3aa1 79 int (*func)(const char *, char *, size_t, void *);
lsm 0:b21e585b3aa1 80 void *userdata;
lsm 0:b21e585b3aa1 81 struct freshen_method *next;
lsm 0:b21e585b3aa1 82 };
lsm 0:b21e585b3aa1 83
lsm 0:b21e585b3aa1 84 /*
lsm 0:b21e585b3aa1 85 * Main Freshen context, stores current request information and a list of
lsm 0:b21e585b3aa1 86 * exported RPC methods.
lsm 0:b21e585b3aa1 87 */
lsm 0:b21e585b3aa1 88 struct freshen_ctx {
lsm 0:b21e585b3aa1 89 struct freshen_queue queue;
lsm 0:b21e585b3aa1 90 struct freshen_method *methods;
lsm 0:b21e585b3aa1 91 void *privdata;
lsm 0:b21e585b3aa1 92 };
lsm 0:b21e585b3aa1 93
lsm 0:b21e585b3aa1 94 #define FRESHEN_CTX_INTIALIZER \
lsm 0:b21e585b3aa1 95 { {NULL, 0, 0, FRESHEN_SPINLOCK_INIT}, NULL, NULL }
lsm 0:b21e585b3aa1 96
lsm 0:b21e585b3aa1 97 FRESHEN_API struct freshen_ctx freshen_default_context;
lsm 0:b21e585b3aa1 98
lsm 0:b21e585b3aa1 99 /* Registers function fn under the given name within the given RPC context */
lsm 0:b21e585b3aa1 100 #define freshen_core_export(freshen, name, fn, ud) \
lsm 0:b21e585b3aa1 101 do { \
lsm 0:b21e585b3aa1 102 static struct freshen_method m = {(name), sizeof(name) - 1, (fn), NULL, \
lsm 0:b21e585b3aa1 103 NULL}; \
lsm 0:b21e585b3aa1 104 m.userdata = (ud); \
lsm 0:b21e585b3aa1 105 m.next = (freshen)->methods; \
lsm 0:b21e585b3aa1 106 (freshen)->methods = &m; \
lsm 0:b21e585b3aa1 107 } while (0)
lsm 0:b21e585b3aa1 108
lsm 0:b21e585b3aa1 109 FRESHEN_API int freshen_core_publish(struct freshen_ctx *freshen,
lsm 0:b21e585b3aa1 110 const char *event, const char *data);
lsm 0:b21e585b3aa1 111
lsm 0:b21e585b3aa1 112 FRESHEN_API int freshen_core_process(struct freshen_ctx *freshen, void *input,
lsm 0:b21e585b3aa1 113 size_t input_sz, void *output,
lsm 0:b21e585b3aa1 114 size_t output_sz);
lsm 0:b21e585b3aa1 115
lsm 0:b21e585b3aa1 116 #if !defined(FRESHEN_DECLARATIONS_ONLY)
lsm 0:b21e585b3aa1 117 #include <stdio.h>
lsm 0:b21e585b3aa1 118 #include <stdlib.h>
lsm 0:b21e585b3aa1 119 #include <string.h>
lsm 0:b21e585b3aa1 120
lsm 0:b21e585b3aa1 121 #if defined(ESP_PLATFORM)
lsm 0:b21e585b3aa1 122 #include "esp_log.h"
lsm 0:b21e585b3aa1 123 #define FLOGI(...) ESP_LOGI("freshen", __VA_ARGS__)
lsm 0:b21e585b3aa1 124 #else
lsm 0:b21e585b3aa1 125 #define FLOGI(...) \
lsm 0:b21e585b3aa1 126 do { \
lsm 0:b21e585b3aa1 127 printf(__VA_ARGS__); \
lsm 0:b21e585b3aa1 128 putchar('\n'); \
lsm 0:b21e585b3aa1 129 } while (0)
lsm 0:b21e585b3aa1 130 #endif
lsm 0:b21e585b3aa1 131 #define FLOGE FLOGI
lsm 0:b21e585b3aa1 132
lsm 0:b21e585b3aa1 133 static int freshen_json(const char *s, size_t sz, const char *key, size_t keysz,
lsm 0:b21e585b3aa1 134 const char **value, size_t *valuesz) {
lsm 0:b21e585b3aa1 135 enum {
lsm 0:b21e585b3aa1 136 JSON_STATE_VALUE,
lsm 0:b21e585b3aa1 137 JSON_STATE_LITERAL,
lsm 0:b21e585b3aa1 138 JSON_STATE_STRING,
lsm 0:b21e585b3aa1 139 JSON_STATE_ESCAPE,
lsm 0:b21e585b3aa1 140 JSON_STATE_UTF8
lsm 0:b21e585b3aa1 141 } state = JSON_STATE_VALUE;
lsm 0:b21e585b3aa1 142 const char *k = NULL;
lsm 0:b21e585b3aa1 143 int index = 1;
lsm 0:b21e585b3aa1 144 int depth = 0;
lsm 0:b21e585b3aa1 145 int utf8_bytes = 0;
lsm 0:b21e585b3aa1 146
lsm 0:b21e585b3aa1 147 if (key == NULL) {
lsm 0:b21e585b3aa1 148 index = keysz;
lsm 0:b21e585b3aa1 149 keysz = 0;
lsm 0:b21e585b3aa1 150 }
lsm 0:b21e585b3aa1 151
lsm 0:b21e585b3aa1 152 *value = NULL;
lsm 0:b21e585b3aa1 153 *valuesz = 0;
lsm 0:b21e585b3aa1 154
lsm 0:b21e585b3aa1 155 for (; sz > 0; s++, sz--) {
lsm 0:b21e585b3aa1 156 enum {
lsm 0:b21e585b3aa1 157 JSON_ACTION_NONE,
lsm 0:b21e585b3aa1 158 JSON_ACTION_START,
lsm 0:b21e585b3aa1 159 JSON_ACTION_END,
lsm 0:b21e585b3aa1 160 JSON_ACTION_START_STRUCT,
lsm 0:b21e585b3aa1 161 JSON_ACTION_END_STRUCT
lsm 0:b21e585b3aa1 162 } action = JSON_ACTION_NONE;
lsm 0:b21e585b3aa1 163 unsigned char c = *s;
lsm 0:b21e585b3aa1 164 switch (state) {
lsm 0:b21e585b3aa1 165 case JSON_STATE_VALUE:
lsm 0:b21e585b3aa1 166 if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' ||
lsm 0:b21e585b3aa1 167 c == ':') {
lsm 0:b21e585b3aa1 168 continue;
lsm 0:b21e585b3aa1 169 } else if (c == '"') {
lsm 0:b21e585b3aa1 170 action = JSON_ACTION_START;
lsm 0:b21e585b3aa1 171 state = JSON_STATE_STRING;
lsm 0:b21e585b3aa1 172 } else if (c == '{' || c == '[') {
lsm 0:b21e585b3aa1 173 action = JSON_ACTION_START_STRUCT;
lsm 0:b21e585b3aa1 174 } else if (c == '}' || c == ']') {
lsm 0:b21e585b3aa1 175 action = JSON_ACTION_END_STRUCT;
lsm 0:b21e585b3aa1 176 } else if (c == 't' || c == 'f' || c == 'n' || c == '-' ||
lsm 0:b21e585b3aa1 177 (c >= '0' && c <= '9')) {
lsm 0:b21e585b3aa1 178 action = JSON_ACTION_START;
lsm 0:b21e585b3aa1 179 state = JSON_STATE_LITERAL;
lsm 0:b21e585b3aa1 180 } else {
lsm 0:b21e585b3aa1 181 return -1;
lsm 0:b21e585b3aa1 182 }
lsm 0:b21e585b3aa1 183 break;
lsm 0:b21e585b3aa1 184 case JSON_STATE_LITERAL:
lsm 0:b21e585b3aa1 185 if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' ||
lsm 0:b21e585b3aa1 186 c == ']' || c == '}' || c == ':') {
lsm 0:b21e585b3aa1 187 state = JSON_STATE_VALUE;
lsm 0:b21e585b3aa1 188 s--;
lsm 0:b21e585b3aa1 189 sz++;
lsm 0:b21e585b3aa1 190 action = JSON_ACTION_END;
lsm 0:b21e585b3aa1 191 } else if (c < 32 || c > 126) {
lsm 0:b21e585b3aa1 192 return -1;
lsm 0:b21e585b3aa1 193 }
lsm 0:b21e585b3aa1 194 case JSON_STATE_STRING:
lsm 0:b21e585b3aa1 195 if (c < 32 || (c > 126 && c < 192)) {
lsm 0:b21e585b3aa1 196 return -1;
lsm 0:b21e585b3aa1 197 } else if (c == '"') {
lsm 0:b21e585b3aa1 198 action = JSON_ACTION_END;
lsm 0:b21e585b3aa1 199 state = JSON_STATE_VALUE;
lsm 0:b21e585b3aa1 200 } else if (c == '\\') {
lsm 0:b21e585b3aa1 201 state = JSON_STATE_ESCAPE;
lsm 0:b21e585b3aa1 202 } else if (c >= 192 && c < 224) {
lsm 0:b21e585b3aa1 203 utf8_bytes = 1;
lsm 0:b21e585b3aa1 204 state = JSON_STATE_UTF8;
lsm 0:b21e585b3aa1 205 } else if (c >= 224 && c < 240) {
lsm 0:b21e585b3aa1 206 utf8_bytes = 2;
lsm 0:b21e585b3aa1 207 state = JSON_STATE_UTF8;
lsm 0:b21e585b3aa1 208 } else if (c >= 240 && c < 247) {
lsm 0:b21e585b3aa1 209 utf8_bytes = 3;
lsm 0:b21e585b3aa1 210 state = JSON_STATE_UTF8;
lsm 0:b21e585b3aa1 211 } else if (c >= 128 && c < 192) {
lsm 0:b21e585b3aa1 212 return -1;
lsm 0:b21e585b3aa1 213 }
lsm 0:b21e585b3aa1 214 break;
lsm 0:b21e585b3aa1 215 case JSON_STATE_ESCAPE:
lsm 0:b21e585b3aa1 216 if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' ||
lsm 0:b21e585b3aa1 217 c == 'n' || c == 'r' || c == 't' || c == 'u') {
lsm 0:b21e585b3aa1 218 state = JSON_STATE_STRING;
lsm 0:b21e585b3aa1 219 } else {
lsm 0:b21e585b3aa1 220 return -1;
lsm 0:b21e585b3aa1 221 }
lsm 0:b21e585b3aa1 222 break;
lsm 0:b21e585b3aa1 223 case JSON_STATE_UTF8:
lsm 0:b21e585b3aa1 224 if (c < 128 || c > 191) {
lsm 0:b21e585b3aa1 225 return -1;
lsm 0:b21e585b3aa1 226 }
lsm 0:b21e585b3aa1 227 utf8_bytes--;
lsm 0:b21e585b3aa1 228 if (utf8_bytes == 0) {
lsm 0:b21e585b3aa1 229 state = JSON_STATE_STRING;
lsm 0:b21e585b3aa1 230 }
lsm 0:b21e585b3aa1 231 break;
lsm 0:b21e585b3aa1 232 default:
lsm 0:b21e585b3aa1 233 return -1;
lsm 0:b21e585b3aa1 234 }
lsm 0:b21e585b3aa1 235
lsm 0:b21e585b3aa1 236 if (action == JSON_ACTION_END_STRUCT) {
lsm 0:b21e585b3aa1 237 depth--;
lsm 0:b21e585b3aa1 238 }
lsm 0:b21e585b3aa1 239
lsm 0:b21e585b3aa1 240 if (depth == 1) {
lsm 0:b21e585b3aa1 241 if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) {
lsm 0:b21e585b3aa1 242 if (index == 0) {
lsm 0:b21e585b3aa1 243 *value = s;
lsm 0:b21e585b3aa1 244 } else if (keysz > 0 && index == 1) {
lsm 0:b21e585b3aa1 245 k = s;
lsm 0:b21e585b3aa1 246 } else {
lsm 0:b21e585b3aa1 247 index--;
lsm 0:b21e585b3aa1 248 }
lsm 0:b21e585b3aa1 249 } else if (action == JSON_ACTION_END ||
lsm 0:b21e585b3aa1 250 action == JSON_ACTION_END_STRUCT) {
lsm 0:b21e585b3aa1 251 if (*value != NULL && index == 0) {
lsm 0:b21e585b3aa1 252 *valuesz = (size_t)(s + 1 - *value);
lsm 0:b21e585b3aa1 253 return 0;
lsm 0:b21e585b3aa1 254 } else if (keysz > 0 && k != NULL) {
lsm 0:b21e585b3aa1 255 if (keysz == (size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0) {
lsm 0:b21e585b3aa1 256 index = 0;
lsm 0:b21e585b3aa1 257 } else {
lsm 0:b21e585b3aa1 258 index = 2;
lsm 0:b21e585b3aa1 259 }
lsm 0:b21e585b3aa1 260 k = NULL;
lsm 0:b21e585b3aa1 261 }
lsm 0:b21e585b3aa1 262 }
lsm 0:b21e585b3aa1 263 }
lsm 0:b21e585b3aa1 264
lsm 0:b21e585b3aa1 265 if (action == JSON_ACTION_START_STRUCT) {
lsm 0:b21e585b3aa1 266 depth++;
lsm 0:b21e585b3aa1 267 }
lsm 0:b21e585b3aa1 268 }
lsm 0:b21e585b3aa1 269 return -1;
lsm 0:b21e585b3aa1 270 }
lsm 0:b21e585b3aa1 271
lsm 0:b21e585b3aa1 272 static int freshen_json_unescape(const char *s, size_t n, char *out) {
lsm 0:b21e585b3aa1 273 int r = 0;
lsm 0:b21e585b3aa1 274 if (*s++ != '"') {
lsm 0:b21e585b3aa1 275 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 276 }
lsm 0:b21e585b3aa1 277 while (n > 2) {
lsm 0:b21e585b3aa1 278 char c = *s;
lsm 0:b21e585b3aa1 279 if (c == '\\') {
lsm 0:b21e585b3aa1 280 s++;
lsm 0:b21e585b3aa1 281 n--;
lsm 0:b21e585b3aa1 282 switch (*s) {
lsm 0:b21e585b3aa1 283 case 'b':
lsm 0:b21e585b3aa1 284 c = '\b';
lsm 0:b21e585b3aa1 285 break;
lsm 0:b21e585b3aa1 286 case 'f':
lsm 0:b21e585b3aa1 287 c = '\f';
lsm 0:b21e585b3aa1 288 break;
lsm 0:b21e585b3aa1 289 case 'n':
lsm 0:b21e585b3aa1 290 c = '\n';
lsm 0:b21e585b3aa1 291 break;
lsm 0:b21e585b3aa1 292 case 'r':
lsm 0:b21e585b3aa1 293 c = '\r';
lsm 0:b21e585b3aa1 294 break;
lsm 0:b21e585b3aa1 295 case 't':
lsm 0:b21e585b3aa1 296 c = '\t';
lsm 0:b21e585b3aa1 297 break;
lsm 0:b21e585b3aa1 298 case '\\':
lsm 0:b21e585b3aa1 299 c = '\\';
lsm 0:b21e585b3aa1 300 break;
lsm 0:b21e585b3aa1 301 case '/':
lsm 0:b21e585b3aa1 302 c = '/';
lsm 0:b21e585b3aa1 303 break;
lsm 0:b21e585b3aa1 304 case '\"':
lsm 0:b21e585b3aa1 305 c = '\"';
lsm 0:b21e585b3aa1 306 break;
lsm 0:b21e585b3aa1 307 default: /* XXX: we don't support unicode \uXXXX yet */
lsm 0:b21e585b3aa1 308 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 309 }
lsm 0:b21e585b3aa1 310 }
lsm 0:b21e585b3aa1 311 if (out != NULL) {
lsm 0:b21e585b3aa1 312 *out++ = c;
lsm 0:b21e585b3aa1 313 }
lsm 0:b21e585b3aa1 314 s++;
lsm 0:b21e585b3aa1 315 n--;
lsm 0:b21e585b3aa1 316 r++;
lsm 0:b21e585b3aa1 317 }
lsm 0:b21e585b3aa1 318 if (*s != '"') {
lsm 0:b21e585b3aa1 319 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 320 }
lsm 0:b21e585b3aa1 321 if (out != NULL) {
lsm 0:b21e585b3aa1 322 *out = '\0';
lsm 0:b21e585b3aa1 323 }
lsm 0:b21e585b3aa1 324 return r;
lsm 0:b21e585b3aa1 325 }
lsm 0:b21e585b3aa1 326
lsm 0:b21e585b3aa1 327 static int freshen_json_escape(const char *in, size_t in_sz, char *out,
lsm 0:b21e585b3aa1 328 size_t out_sz) {
lsm 0:b21e585b3aa1 329 size_t n = 0;
lsm 0:b21e585b3aa1 330 for (; in != NULL && *in && in_sz > 0; in++, in_sz--) {
lsm 0:b21e585b3aa1 331 const char *chr;
lsm 0:b21e585b3aa1 332 size_t chr_sz;
lsm 0:b21e585b3aa1 333 switch (*in) {
lsm 0:b21e585b3aa1 334 case '\\':
lsm 0:b21e585b3aa1 335 chr = "\\\\";
lsm 0:b21e585b3aa1 336 chr_sz = 2;
lsm 0:b21e585b3aa1 337 break;
lsm 0:b21e585b3aa1 338 case '"':
lsm 0:b21e585b3aa1 339 chr = "\\\"";
lsm 0:b21e585b3aa1 340 chr_sz = 2;
lsm 0:b21e585b3aa1 341 break;
lsm 0:b21e585b3aa1 342 case '/':
lsm 0:b21e585b3aa1 343 chr = "\\/";
lsm 0:b21e585b3aa1 344 chr_sz = 2;
lsm 0:b21e585b3aa1 345 break;
lsm 0:b21e585b3aa1 346 case '\b':
lsm 0:b21e585b3aa1 347 chr = "\\b";
lsm 0:b21e585b3aa1 348 chr_sz = 2;
lsm 0:b21e585b3aa1 349 break;
lsm 0:b21e585b3aa1 350 case '\f':
lsm 0:b21e585b3aa1 351 chr = "\\f";
lsm 0:b21e585b3aa1 352 chr_sz = 2;
lsm 0:b21e585b3aa1 353 break;
lsm 0:b21e585b3aa1 354 case '\n':
lsm 0:b21e585b3aa1 355 chr = "\\n";
lsm 0:b21e585b3aa1 356 chr_sz = 2;
lsm 0:b21e585b3aa1 357 break;
lsm 0:b21e585b3aa1 358 case '\r':
lsm 0:b21e585b3aa1 359 chr = "\\r";
lsm 0:b21e585b3aa1 360 chr_sz = 2;
lsm 0:b21e585b3aa1 361 break;
lsm 0:b21e585b3aa1 362 case '\t':
lsm 0:b21e585b3aa1 363 chr = "\\t";
lsm 0:b21e585b3aa1 364 chr_sz = 2;
lsm 0:b21e585b3aa1 365 break;
lsm 0:b21e585b3aa1 366 default:
lsm 0:b21e585b3aa1 367 chr = in;
lsm 0:b21e585b3aa1 368 chr_sz = 1;
lsm 0:b21e585b3aa1 369 break;
lsm 0:b21e585b3aa1 370 }
lsm 0:b21e585b3aa1 371 if (out != NULL && out_sz > chr_sz) {
lsm 0:b21e585b3aa1 372 memmove(out + n, chr, chr_sz);
lsm 0:b21e585b3aa1 373 out = out + chr_sz;
lsm 0:b21e585b3aa1 374 out_sz = out_sz - chr_sz;
lsm 0:b21e585b3aa1 375 }
lsm 0:b21e585b3aa1 376 n = n + chr_sz;
lsm 0:b21e585b3aa1 377 }
lsm 0:b21e585b3aa1 378 if (out != NULL && out_sz > 0) {
lsm 0:b21e585b3aa1 379 *out = '\0';
lsm 0:b21e585b3aa1 380 }
lsm 0:b21e585b3aa1 381 n = n + 1;
lsm 0:b21e585b3aa1 382 return n;
lsm 0:b21e585b3aa1 383 }
lsm 0:b21e585b3aa1 384 static void freshen_buf_init(struct freshen_buf *buf, void *data,
lsm 0:b21e585b3aa1 385 size_t data_sz) {
lsm 0:b21e585b3aa1 386 buf->ptr = data;
lsm 0:b21e585b3aa1 387 buf->len = 0;
lsm 0:b21e585b3aa1 388 buf->cap = data_sz;
lsm 0:b21e585b3aa1 389 buf->overflow = 0;
lsm 0:b21e585b3aa1 390 }
lsm 0:b21e585b3aa1 391
lsm 0:b21e585b3aa1 392 static void freshen_buf_append(struct freshen_buf *buf, const void *data,
lsm 0:b21e585b3aa1 393 size_t data_sz) {
lsm 0:b21e585b3aa1 394 if (buf->overflow) {
lsm 0:b21e585b3aa1 395 return;
lsm 0:b21e585b3aa1 396 }
lsm 0:b21e585b3aa1 397 if (buf->cap - buf->len < data_sz) {
lsm 0:b21e585b3aa1 398 data_sz = buf->cap - buf->len;
lsm 0:b21e585b3aa1 399 buf->overflow = 1;
lsm 0:b21e585b3aa1 400 }
lsm 0:b21e585b3aa1 401 memmove((char *) buf->ptr + buf->len, data, data_sz);
lsm 0:b21e585b3aa1 402 buf->len = buf->len + data_sz;
lsm 0:b21e585b3aa1 403 }
lsm 0:b21e585b3aa1 404
lsm 0:b21e585b3aa1 405 static void freshen_buf_append_int(struct freshen_buf *buf, int n) {
lsm 0:b21e585b3aa1 406 if (n < 0) {
lsm 0:b21e585b3aa1 407 freshen_buf_append(buf, "-", 1);
lsm 0:b21e585b3aa1 408 freshen_buf_append_int(buf, -n);
lsm 0:b21e585b3aa1 409 return;
lsm 0:b21e585b3aa1 410 } else if (n > 10) {
lsm 0:b21e585b3aa1 411 freshen_buf_append_int(buf, n / 10);
lsm 0:b21e585b3aa1 412 }
lsm 0:b21e585b3aa1 413 freshen_buf_append(buf, &("0123456789"[n % 10]), 1);
lsm 0:b21e585b3aa1 414 }
lsm 0:b21e585b3aa1 415
lsm 0:b21e585b3aa1 416 static void freshen_buf_append_json_string(struct freshen_buf *buf,
lsm 0:b21e585b3aa1 417 const char *s) {
lsm 0:b21e585b3aa1 418 for (; s != NULL && *s; s++) {
lsm 0:b21e585b3aa1 419 char out[7];
lsm 0:b21e585b3aa1 420 int n = freshen_json_escape(s, 1, out, sizeof(out));
lsm 0:b21e585b3aa1 421 freshen_buf_append(buf, out, n - 1);
lsm 0:b21e585b3aa1 422 }
lsm 0:b21e585b3aa1 423 }
lsm 0:b21e585b3aa1 424 FRESHEN_API int freshen_core_notify(struct freshen_ctx *freshen,
lsm 0:b21e585b3aa1 425 const char *method, const char *arg) {
lsm 0:b21e585b3aa1 426 if (method == NULL || strlen(method) == 0) {
lsm 0:b21e585b3aa1 427 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 428 }
lsm 0:b21e585b3aa1 429 if (arg == NULL) {
lsm 0:b21e585b3aa1 430 arg = "";
lsm 0:b21e585b3aa1 431 }
lsm 0:b21e585b3aa1 432 int method_sz = strlen(method) + 1;
lsm 0:b21e585b3aa1 433 int arg_sz = strlen(arg) + 1;
lsm 0:b21e585b3aa1 434 freshen_spinlock_lock(&freshen->queue.lock);
lsm 0:b21e585b3aa1 435 if (freshen->queue.tail + method_sz + arg_sz >= freshen->queue.buf_sz) {
lsm 0:b21e585b3aa1 436 freshen_spinlock_unlock(&freshen->queue.lock);
lsm 0:b21e585b3aa1 437 return FRESHEN_ERROR_INTERNAL;
lsm 0:b21e585b3aa1 438 }
lsm 0:b21e585b3aa1 439 memmove((char *) freshen->queue.buf + freshen->queue.tail, method, method_sz);
lsm 0:b21e585b3aa1 440 memmove((char *) freshen->queue.buf + freshen->queue.tail + method_sz, arg,
lsm 0:b21e585b3aa1 441 arg_sz);
lsm 0:b21e585b3aa1 442 freshen->queue.tail = freshen->queue.tail + method_sz + arg_sz;
lsm 0:b21e585b3aa1 443 freshen_spinlock_unlock(&freshen->queue.lock);
lsm 0:b21e585b3aa1 444 return 0;
lsm 0:b21e585b3aa1 445 }
lsm 0:b21e585b3aa1 446
lsm 0:b21e585b3aa1 447 static int freshen_jsonrpc_notify(struct freshen_ctx *freshen, void *res,
lsm 0:b21e585b3aa1 448 size_t res_sz) {
lsm 0:b21e585b3aa1 449 struct freshen_buf buf;
lsm 0:b21e585b3aa1 450 freshen_buf_init(&buf, res, res_sz);
lsm 0:b21e585b3aa1 451 freshen_spinlock_lock(&freshen->queue.lock);
lsm 0:b21e585b3aa1 452 char *method = (char *) freshen->queue.buf;
lsm 0:b21e585b3aa1 453 int method_sz = strlen(method) + 1;
lsm 0:b21e585b3aa1 454 if (method_sz == 1) {
lsm 0:b21e585b3aa1 455 freshen_spinlock_unlock(&freshen->queue.lock);
lsm 0:b21e585b3aa1 456 return 0;
lsm 0:b21e585b3aa1 457 }
lsm 0:b21e585b3aa1 458 char *arg = (char *) freshen->queue.buf + method_sz;
lsm 0:b21e585b3aa1 459 int arg_sz = strlen(arg) + 1;
lsm 0:b21e585b3aa1 460
lsm 0:b21e585b3aa1 461 freshen_buf_append(&buf, "{\"method\":\"", 11);
lsm 0:b21e585b3aa1 462 freshen_buf_append_json_string(&buf, method);
lsm 0:b21e585b3aa1 463 freshen_buf_append(&buf, "\",\"params\":[\"", 13);
lsm 0:b21e585b3aa1 464 freshen_buf_append_json_string(&buf, arg);
lsm 0:b21e585b3aa1 465 freshen_buf_append(&buf, "\"]}", 4);
lsm 0:b21e585b3aa1 466
lsm 0:b21e585b3aa1 467 memmove(freshen->queue.buf, (char *) freshen->queue.buf + method_sz + arg_sz,
lsm 0:b21e585b3aa1 468 freshen->queue.buf_sz - method_sz - arg_sz);
lsm 0:b21e585b3aa1 469 memset(
lsm 0:b21e585b3aa1 470 (char *) freshen->queue.buf + freshen->queue.buf_sz - method_sz - arg_sz,
lsm 0:b21e585b3aa1 471 0, method_sz + arg_sz);
lsm 0:b21e585b3aa1 472 freshen->queue.tail = freshen->queue.tail - method_sz - arg_sz;
lsm 0:b21e585b3aa1 473 freshen_spinlock_unlock(&freshen->queue.lock);
lsm 0:b21e585b3aa1 474
lsm 0:b21e585b3aa1 475 if (buf.overflow) {
lsm 0:b21e585b3aa1 476 return FRESHEN_ERROR_INTERNAL;
lsm 0:b21e585b3aa1 477 }
lsm 0:b21e585b3aa1 478 return buf.len;
lsm 0:b21e585b3aa1 479 }
lsm 0:b21e585b3aa1 480
lsm 0:b21e585b3aa1 481 static int freshen_jsonrpc_result(struct freshen_buf *buf, const char *id,
lsm 0:b21e585b3aa1 482 size_t id_sz, int code, const char *res) {
lsm 0:b21e585b3aa1 483 freshen_buf_append(buf, "{\"id\":", 6);
lsm 0:b21e585b3aa1 484 freshen_buf_append(buf, id, id_sz);
lsm 0:b21e585b3aa1 485 if (code == 0) {
lsm 0:b21e585b3aa1 486 freshen_buf_append(buf, ",\"result\":\"", 11);
lsm 0:b21e585b3aa1 487 } else {
lsm 0:b21e585b3aa1 488 freshen_buf_append(buf, ",\"error\":{\"code\":", 17);
lsm 0:b21e585b3aa1 489 freshen_buf_append_int(buf, code);
lsm 0:b21e585b3aa1 490 freshen_buf_append(buf, ",\"message\":\"", 12);
lsm 0:b21e585b3aa1 491 }
lsm 0:b21e585b3aa1 492 freshen_buf_append_json_string(buf, res);
lsm 0:b21e585b3aa1 493 if (code == 0) {
lsm 0:b21e585b3aa1 494 freshen_buf_append(buf, "\"}", 2);
lsm 0:b21e585b3aa1 495 } else {
lsm 0:b21e585b3aa1 496 freshen_buf_append(buf, "\"}}", 3);
lsm 0:b21e585b3aa1 497 }
lsm 0:b21e585b3aa1 498 freshen_buf_append(buf, "", 1);
lsm 0:b21e585b3aa1 499 if (buf->overflow) {
lsm 0:b21e585b3aa1 500 return FRESHEN_ERROR_INTERNAL;
lsm 0:b21e585b3aa1 501 }
lsm 0:b21e585b3aa1 502 return buf->len;
lsm 0:b21e585b3aa1 503 }
lsm 0:b21e585b3aa1 504
lsm 0:b21e585b3aa1 505 static int freshen_jsonrpc_call(struct freshen_ctx *freshen, char *req,
lsm 0:b21e585b3aa1 506 size_t req_sz, char *res, size_t res_sz) {
lsm 0:b21e585b3aa1 507 int n;
lsm 0:b21e585b3aa1 508 const char *id = NULL, *method = NULL, *params = NULL, *arg = NULL;
lsm 0:b21e585b3aa1 509 size_t id_sz = 0, method_sz = 0, params_sz = 0, arg_sz = 0;
lsm 0:b21e585b3aa1 510 struct freshen_method *m;
lsm 0:b21e585b3aa1 511
lsm 0:b21e585b3aa1 512 /* ID must exist */
lsm 0:b21e585b3aa1 513 if (freshen_json(req, req_sz, "id", 2, &id, &id_sz) < 0) {
lsm 0:b21e585b3aa1 514 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 515 }
lsm 0:b21e585b3aa1 516
lsm 0:b21e585b3aa1 517 /* Method must exist and must be a string*/
lsm 0:b21e585b3aa1 518 if (freshen_json(req, req_sz, "method", 6, &method, &method_sz) < 0) {
lsm 0:b21e585b3aa1 519 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 520 }
lsm 0:b21e585b3aa1 521 if (method_sz < 3 || method[0] != '\"' || method[method_sz - 1] != '\"') {
lsm 0:b21e585b3aa1 522 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 523 }
lsm 0:b21e585b3aa1 524
lsm 0:b21e585b3aa1 525 /* Params must exist and must be an array */
lsm 0:b21e585b3aa1 526 if (freshen_json(req, req_sz, "params", 6, &params, &params_sz) < 0) {
lsm 0:b21e585b3aa1 527 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 528 }
lsm 0:b21e585b3aa1 529 if (params_sz < 2 || params[0] != '[' || params[params_sz - 1] != ']') {
lsm 0:b21e585b3aa1 530 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 531 }
lsm 0:b21e585b3aa1 532
lsm 0:b21e585b3aa1 533 /* Extract first param value and unescape it as a string */
lsm 0:b21e585b3aa1 534 if (freshen_json(params, params_sz, NULL, 0, &arg, &arg_sz) < 0) {
lsm 0:b21e585b3aa1 535 return FRESHEN_ERROR_INVALID;
lsm 0:b21e585b3aa1 536 }
lsm 0:b21e585b3aa1 537 if ((n = freshen_json_unescape(arg, arg_sz, req)) < 0) {
lsm 0:b21e585b3aa1 538 return n;
lsm 0:b21e585b3aa1 539 }
lsm 0:b21e585b3aa1 540
lsm 0:b21e585b3aa1 541 /* number of bytes used for success result prologue */
lsm 0:b21e585b3aa1 542 const size_t prologue_sz = 19;
lsm 0:b21e585b3aa1 543 struct freshen_buf buf;
lsm 0:b21e585b3aa1 544 freshen_buf_init(&buf, res, res_sz);
lsm 0:b21e585b3aa1 545 for (m = freshen->methods; m != NULL; m = m->next) {
lsm 0:b21e585b3aa1 546 if (m->method_sz == method_sz - 2 &&
lsm 0:b21e585b3aa1 547 memcmp(m->method, method + 1, method_sz - 2) == 0) {
lsm 0:b21e585b3aa1 548 int code = m->func(req, res, res_sz - id_sz - prologue_sz, m->userdata);
lsm 0:b21e585b3aa1 549 /*
lsm 0:b21e585b3aa1 550 * move result string to the end of the buffer, then escape it in-place
lsm 0:b21e585b3aa1 551 * when preparing the result JSON. If overflow happens - return an error
lsm 0:b21e585b3aa1 552 */
lsm 0:b21e585b3aa1 553 size_t n = strlen(res) + 1;
lsm 0:b21e585b3aa1 554 char *p = res + res_sz - n;
lsm 0:b21e585b3aa1 555 memmove(p, res, n);
lsm 0:b21e585b3aa1 556 int r = freshen_jsonrpc_result(&buf, id, id_sz, code, p);
lsm 0:b21e585b3aa1 557 if (buf.overflow) {
lsm 0:b21e585b3aa1 558 freshen_buf_init(&buf, res, res_sz);
lsm 0:b21e585b3aa1 559 return freshen_jsonrpc_result(&buf, id, id_sz, FRESHEN_ERROR_INTERNAL,
lsm 0:b21e585b3aa1 560 "result is too long");
lsm 0:b21e585b3aa1 561 }
lsm 0:b21e585b3aa1 562 return r;
lsm 0:b21e585b3aa1 563 }
lsm 0:b21e585b3aa1 564 }
lsm 0:b21e585b3aa1 565 return freshen_jsonrpc_result(&buf, id, id_sz, FRESHEN_ERROR_NOT_FOUND,
lsm 0:b21e585b3aa1 566 "method not found");
lsm 0:b21e585b3aa1 567 }
lsm 0:b21e585b3aa1 568
lsm 0:b21e585b3aa1 569 FRESHEN_API int freshen_core_process(struct freshen_ctx *freshen, void *req,
lsm 0:b21e585b3aa1 570 size_t req_sz, void *res, size_t res_sz) {
lsm 0:b21e585b3aa1 571 int r = freshen_jsonrpc_notify(freshen, res, res_sz);
lsm 0:b21e585b3aa1 572
lsm 0:b21e585b3aa1 573 if (r == 0 && req_sz > 0) {
lsm 0:b21e585b3aa1 574 r = freshen_jsonrpc_call(freshen, (char *) req, req_sz, (char *) res,
lsm 0:b21e585b3aa1 575 res_sz);
lsm 0:b21e585b3aa1 576 }
lsm 0:b21e585b3aa1 577 return r;
lsm 0:b21e585b3aa1 578 }
lsm 0:b21e585b3aa1 579 #include <stdio.h>
lsm 0:b21e585b3aa1 580 #include <stdlib.h>
lsm 0:b21e585b3aa1 581 /*
lsm 0:b21e585b3aa1 582 * Common OTA code
lsm 0:b21e585b3aa1 583 */
lsm 0:b21e585b3aa1 584 static int b64rev(int c) {
lsm 0:b21e585b3aa1 585 if (c >= 'A' && c <= 'Z') {
lsm 0:b21e585b3aa1 586 return c - 'A';
lsm 0:b21e585b3aa1 587 } else if (c >= 'a' && c <= 'z') {
lsm 0:b21e585b3aa1 588 return c + 26 - 'a';
lsm 0:b21e585b3aa1 589 } else if (c >= '0' && c <= '9') {
lsm 0:b21e585b3aa1 590 return c + 52 - '0';
lsm 0:b21e585b3aa1 591 } else if (c == '+') {
lsm 0:b21e585b3aa1 592 return 62;
lsm 0:b21e585b3aa1 593 } else if (c == '/') {
lsm 0:b21e585b3aa1 594 return 63;
lsm 0:b21e585b3aa1 595 } else {
lsm 0:b21e585b3aa1 596 return 64;
lsm 0:b21e585b3aa1 597 }
lsm 0:b21e585b3aa1 598 }
lsm 0:b21e585b3aa1 599
lsm 0:b21e585b3aa1 600 static int b64dec(const char *src, int n, char *dst) {
lsm 0:b21e585b3aa1 601 const char *end = src + n;
lsm 0:b21e585b3aa1 602 int len = 0;
lsm 0:b21e585b3aa1 603 while (src + 3 < end) {
lsm 0:b21e585b3aa1 604 int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]),
lsm 0:b21e585b3aa1 605 d = b64rev(src[3]);
lsm 0:b21e585b3aa1 606 dst[len++] = (a << 2) | (b >> 4);
lsm 0:b21e585b3aa1 607 if (src[2] != '=') {
lsm 0:b21e585b3aa1 608 dst[len++] = (b << 4) | (c >> 2);
lsm 0:b21e585b3aa1 609 if (src[3] != '=') {
lsm 0:b21e585b3aa1 610 dst[len++] = (c << 6) | d;
lsm 0:b21e585b3aa1 611 }
lsm 0:b21e585b3aa1 612 }
lsm 0:b21e585b3aa1 613 src += 4;
lsm 0:b21e585b3aa1 614 }
lsm 0:b21e585b3aa1 615 return len;
lsm 0:b21e585b3aa1 616 }
lsm 0:b21e585b3aa1 617
lsm 0:b21e585b3aa1 618 static int freshen_ota_init(struct freshen_ctx *ctx);
lsm 0:b21e585b3aa1 619 static int freshen_ota_enabled(struct freshen_ctx *ctx);
lsm 0:b21e585b3aa1 620 static int freshen_ota_begin(struct freshen_ctx *ctx);
lsm 0:b21e585b3aa1 621 static int freshen_ota_end(struct freshen_ctx *ctx, int success);
lsm 0:b21e585b3aa1 622 static int freshen_ota_write(struct freshen_ctx *ctx, void *buf, size_t bufsz);
lsm 0:b21e585b3aa1 623 static int freshen_ota_commit(struct freshen_ctx *ctx);
lsm 0:b21e585b3aa1 624
lsm 0:b21e585b3aa1 625 static int freshen_rpc_ota_begin(const char *args, char *buf, size_t len,
lsm 0:b21e585b3aa1 626 void *userdata) {
lsm 0:b21e585b3aa1 627 struct freshen_ctx *ctx = (struct freshen_ctx *) userdata;
lsm 0:b21e585b3aa1 628 int r = freshen_ota_begin(ctx);
lsm 0:b21e585b3aa1 629 snprintf(buf, len, "%s", (r == 0 ? "true" : "false"));
lsm 0:b21e585b3aa1 630 return 0;
lsm 0:b21e585b3aa1 631 (void) args;
lsm 0:b21e585b3aa1 632 }
lsm 0:b21e585b3aa1 633
lsm 0:b21e585b3aa1 634 static int freshen_rpc_ota_end(const char *args, char *buf, size_t len,
lsm 0:b21e585b3aa1 635 void *userdata) {
lsm 0:b21e585b3aa1 636 struct freshen_ctx *ctx = (struct freshen_ctx *) userdata;
lsm 0:b21e585b3aa1 637 int success = 0;
lsm 0:b21e585b3aa1 638 if (sscanf(args, "%d", &success) != 1) {
lsm 0:b21e585b3aa1 639 snprintf(buf, len, "bad args");
lsm 0:b21e585b3aa1 640 return 400;
lsm 0:b21e585b3aa1 641 } else if (freshen_ota_end(ctx, success) != 0) {
lsm 0:b21e585b3aa1 642 snprintf(buf, len, "failed");
lsm 0:b21e585b3aa1 643 return 500;
lsm 0:b21e585b3aa1 644 } else {
lsm 0:b21e585b3aa1 645 snprintf(buf, len, "true");
lsm 0:b21e585b3aa1 646 return 0;
lsm 0:b21e585b3aa1 647 }
lsm 0:b21e585b3aa1 648 }
lsm 0:b21e585b3aa1 649
lsm 0:b21e585b3aa1 650 static int freshen_rpc_ota_write(const char *args, char *buf, size_t len,
lsm 0:b21e585b3aa1 651 void *userdata) {
lsm 0:b21e585b3aa1 652 struct freshen_ctx *ctx = (struct freshen_ctx *) userdata;
lsm 0:b21e585b3aa1 653 char *dec = (char *) args; // Decode in-place
lsm 0:b21e585b3aa1 654 int dec_len = b64dec(args, strlen(args), dec);
lsm 0:b21e585b3aa1 655 dec[len] = '\0';
lsm 0:b21e585b3aa1 656 if (freshen_ota_write(ctx, (void *) dec, dec_len) != 0) {
lsm 0:b21e585b3aa1 657 snprintf(buf, len, "cannot write");
lsm 0:b21e585b3aa1 658 return 500;
lsm 0:b21e585b3aa1 659 } else {
lsm 0:b21e585b3aa1 660 snprintf(buf, len, "true");
lsm 0:b21e585b3aa1 661 return 0;
lsm 0:b21e585b3aa1 662 }
lsm 0:b21e585b3aa1 663 }
lsm 0:b21e585b3aa1 664
lsm 0:b21e585b3aa1 665 #ifdef ESP_PLATFORM
lsm 0:b21e585b3aa1 666
lsm 0:b21e585b3aa1 667 #include <esp_log.h>
lsm 0:b21e585b3aa1 668 #include <esp_ota_ops.h>
lsm 0:b21e585b3aa1 669 #include <esp_system.h>
lsm 0:b21e585b3aa1 670 #include <nvs_flash.h>
lsm 0:b21e585b3aa1 671 #include <unistd.h>
lsm 0:b21e585b3aa1 672
lsm 0:b21e585b3aa1 673 #define FRESHEN_TAG "freshen"
lsm 0:b21e585b3aa1 674 #define FRESHEN_NVS_NAMESPACE "freshen"
lsm 0:b21e585b3aa1 675 #define ROLLBACK_KV_KEY "__rollback"
lsm 0:b21e585b3aa1 676
lsm 0:b21e585b3aa1 677 static struct {
lsm 0:b21e585b3aa1 678 int can_rollback;
lsm 0:b21e585b3aa1 679 const esp_partition_t *update_partition;
lsm 0:b21e585b3aa1 680 esp_ota_handle_t update_handle;
lsm 0:b21e585b3aa1 681 } s_ota;
lsm 0:b21e585b3aa1 682
lsm 0:b21e585b3aa1 683 static int freshen_kv_set(const char *key, const char *value) {
lsm 0:b21e585b3aa1 684 nvs_handle nvs_handle;
lsm 0:b21e585b3aa1 685 esp_err_t err;
lsm 0:b21e585b3aa1 686 err = nvs_open(FRESHEN_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
lsm 0:b21e585b3aa1 687 if (err != ESP_OK) {
lsm 0:b21e585b3aa1 688 ESP_LOGE(FRESHEN_TAG, "nvs_open: err=%d", err);
lsm 0:b21e585b3aa1 689 return -1;
lsm 0:b21e585b3aa1 690 }
lsm 0:b21e585b3aa1 691 err = nvs_set_str(nvs_handle, key, value == NULL ? "" : value);
lsm 0:b21e585b3aa1 692 if (err != ESP_OK) {
lsm 0:b21e585b3aa1 693 ESP_LOGE(FRESHEN_TAG, "nvs_set_str: err=%d", err);
lsm 0:b21e585b3aa1 694 return -1;
lsm 0:b21e585b3aa1 695 }
lsm 0:b21e585b3aa1 696 err = nvs_commit(nvs_handle);
lsm 0:b21e585b3aa1 697 if (err != ESP_OK) {
lsm 0:b21e585b3aa1 698 ESP_LOGE(FRESHEN_TAG, "nvs_commit: err=%d", err);
lsm 0:b21e585b3aa1 699 return -1;
lsm 0:b21e585b3aa1 700 }
lsm 0:b21e585b3aa1 701 nvs_close(nvs_handle);
lsm 0:b21e585b3aa1 702 return 0;
lsm 0:b21e585b3aa1 703 }
lsm 0:b21e585b3aa1 704
lsm 0:b21e585b3aa1 705 static const char *freshen_kv_get(const char *key) {
lsm 0:b21e585b3aa1 706 nvs_handle nvs_handle = 0;
lsm 0:b21e585b3aa1 707 size_t size;
lsm 0:b21e585b3aa1 708 char *value = NULL;
lsm 0:b21e585b3aa1 709 esp_err_t err;
lsm 0:b21e585b3aa1 710
lsm 0:b21e585b3aa1 711 if ((err = nvs_open(FRESHEN_NVS_NAMESPACE, NVS_READONLY, &nvs_handle)) !=
lsm 0:b21e585b3aa1 712 ESP_OK) {
lsm 0:b21e585b3aa1 713 ESP_LOGE(FRESHEN_TAG, "nvs_open: err=%d", err);
lsm 0:b21e585b3aa1 714 } else if ((err = nvs_get_str(nvs_handle, key, NULL, &size)) != ESP_OK) {
lsm 0:b21e585b3aa1 715 ESP_LOGE(FRESHEN_TAG, "nvs_get_str: err=%d", err);
lsm 0:b21e585b3aa1 716 } else if ((value = (char *) calloc(1, size)) == NULL) {
lsm 0:b21e585b3aa1 717 ESP_LOGE(FRESHEN_TAG, "OOM: value is %zu", size);
lsm 0:b21e585b3aa1 718 } else if ((err = nvs_get_str(nvs_handle, key, value, &size)) != ESP_OK) {
lsm 0:b21e585b3aa1 719 ESP_LOGE(FRESHEN_TAG, "nvs_get_str: err=%d", err);
lsm 0:b21e585b3aa1 720 }
lsm 0:b21e585b3aa1 721 nvs_close(nvs_handle);
lsm 0:b21e585b3aa1 722
lsm 0:b21e585b3aa1 723 return value;
lsm 0:b21e585b3aa1 724 }
lsm 0:b21e585b3aa1 725
lsm 0:b21e585b3aa1 726 static int freshen_ota_init(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 727 const char *label = freshen_kv_get(ROLLBACK_KV_KEY);
lsm 0:b21e585b3aa1 728 if (label == NULL) {
lsm 0:b21e585b3aa1 729 ESP_LOGI(FRESHEN_TAG,
lsm 0:b21e585b3aa1 730 "no rollback partition is given, booting up normally");
lsm 0:b21e585b3aa1 731 return 0;
lsm 0:b21e585b3aa1 732 }
lsm 0:b21e585b3aa1 733 const esp_partition_t *p = esp_partition_find_first(
lsm 0:b21e585b3aa1 734 ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, label);
lsm 0:b21e585b3aa1 735 if (p == NULL) {
lsm 0:b21e585b3aa1 736 ESP_LOGI(FRESHEN_TAG, "can't find partition %s for rollback", label);
lsm 0:b21e585b3aa1 737 return 0;
lsm 0:b21e585b3aa1 738 }
lsm 0:b21e585b3aa1 739 ESP_LOGI(FRESHEN_TAG, "use partition %s for rollback", label);
lsm 0:b21e585b3aa1 740 freshen_kv_set(ROLLBACK_KV_KEY, NULL);
lsm 0:b21e585b3aa1 741 esp_ota_set_boot_partition(p);
lsm 0:b21e585b3aa1 742 s_ota.can_rollback = 1;
lsm 0:b21e585b3aa1 743 return 0;
lsm 0:b21e585b3aa1 744 (void) ctx;
lsm 0:b21e585b3aa1 745 }
lsm 0:b21e585b3aa1 746
lsm 0:b21e585b3aa1 747 static int freshen_ota_enabled(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 748 return esp_ota_get_next_update_partition(NULL) == NULL ? 0 : 1;
lsm 0:b21e585b3aa1 749 (void) ctx;
lsm 0:b21e585b3aa1 750 }
lsm 0:b21e585b3aa1 751
lsm 0:b21e585b3aa1 752 static int freshen_ota_begin(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 753 if (s_ota.update_partition != NULL) {
lsm 0:b21e585b3aa1 754 ESP_LOGE(FRESHEN_TAG, "another OTA is already in-progress");
lsm 0:b21e585b3aa1 755 return -1;
lsm 0:b21e585b3aa1 756 }
lsm 0:b21e585b3aa1 757 s_ota.update_partition = esp_ota_get_next_update_partition(NULL);
lsm 0:b21e585b3aa1 758 ESP_LOGI(FRESHEN_TAG, "Starting OTA. update_partition=%p",
lsm 0:b21e585b3aa1 759 s_ota.update_partition);
lsm 0:b21e585b3aa1 760 esp_err_t err = esp_ota_begin(s_ota.update_partition, OTA_SIZE_UNKNOWN,
lsm 0:b21e585b3aa1 761 &s_ota.update_handle);
lsm 0:b21e585b3aa1 762 if (err != ESP_OK) {
lsm 0:b21e585b3aa1 763 ESP_LOGE(FRESHEN_TAG, "esp_ota_begin failed, err=%#x", err);
lsm 0:b21e585b3aa1 764 return -1;
lsm 0:b21e585b3aa1 765 }
lsm 0:b21e585b3aa1 766 return 0;
lsm 0:b21e585b3aa1 767 (void) ctx;
lsm 0:b21e585b3aa1 768 }
lsm 0:b21e585b3aa1 769
lsm 0:b21e585b3aa1 770 static int freshen_ota_write(struct freshen_ctx *ctx, void *buf, size_t bufsz) {
lsm 0:b21e585b3aa1 771 esp_err_t err = esp_ota_write(s_ota.update_handle, buf, bufsz);
lsm 0:b21e585b3aa1 772 if (err != ESP_OK) {
lsm 0:b21e585b3aa1 773 ESP_LOGE(FRESHEN_TAG, "esp_ota_write failed, err=%#x", err);
lsm 0:b21e585b3aa1 774 return -1;
lsm 0:b21e585b3aa1 775 }
lsm 0:b21e585b3aa1 776 ESP_LOGD(FRESHEN_TAG, "written %d bytes", bufsz);
lsm 0:b21e585b3aa1 777 return 0;
lsm 0:b21e585b3aa1 778 (void) ctx;
lsm 0:b21e585b3aa1 779 }
lsm 0:b21e585b3aa1 780
lsm 0:b21e585b3aa1 781 static int freshen_ota_end(struct freshen_ctx *ctx, int success) {
lsm 0:b21e585b3aa1 782 const esp_partition_t *p = s_ota.update_partition;
lsm 0:b21e585b3aa1 783 esp_err_t err = esp_ota_end(s_ota.update_handle);
lsm 0:b21e585b3aa1 784 if (err != ESP_OK) {
lsm 0:b21e585b3aa1 785 ESP_LOGE(FRESHEN_TAG, "err=0x%x", err);
lsm 0:b21e585b3aa1 786 return -1;
lsm 0:b21e585b3aa1 787 }
lsm 0:b21e585b3aa1 788 s_ota.update_partition = NULL;
lsm 0:b21e585b3aa1 789 if (success) {
lsm 0:b21e585b3aa1 790 const esp_partition_t *rollback = esp_ota_get_running_partition();
lsm 0:b21e585b3aa1 791 ESP_LOGI(FRESHEN_TAG, "use partition %s for the next boot",
lsm 0:b21e585b3aa1 792 rollback->label);
lsm 0:b21e585b3aa1 793 if (freshen_kv_set(ROLLBACK_KV_KEY, rollback->label) < 0) return -1;
lsm 0:b21e585b3aa1 794 esp_err_t err = esp_ota_set_boot_partition(p);
lsm 0:b21e585b3aa1 795 if (err != ESP_OK) {
lsm 0:b21e585b3aa1 796 ESP_LOGE(FRESHEN_TAG, "esp_ota_set_boot_partition: err=0x%d", err);
lsm 0:b21e585b3aa1 797 return -1;
lsm 0:b21e585b3aa1 798 }
lsm 0:b21e585b3aa1 799 ESP_LOGI(FRESHEN_TAG, "restarting...");
lsm 0:b21e585b3aa1 800 sleep(2);
lsm 0:b21e585b3aa1 801 esp_restart();
lsm 0:b21e585b3aa1 802 }
lsm 0:b21e585b3aa1 803 return 0;
lsm 0:b21e585b3aa1 804 (void) ctx;
lsm 0:b21e585b3aa1 805 }
lsm 0:b21e585b3aa1 806
lsm 0:b21e585b3aa1 807 static int freshen_ota_commit(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 808 if (!s_ota.can_rollback) return 0;
lsm 0:b21e585b3aa1 809 const esp_partition_t *p = esp_ota_get_running_partition();
lsm 0:b21e585b3aa1 810 esp_err_t err = esp_ota_set_boot_partition(p);
lsm 0:b21e585b3aa1 811 if (err != ESP_OK) {
lsm 0:b21e585b3aa1 812 ESP_LOGE(FRESHEN_TAG, "esp_ota_set_boot_partition: err=0x%d", err);
lsm 0:b21e585b3aa1 813 return -1;
lsm 0:b21e585b3aa1 814 }
lsm 0:b21e585b3aa1 815 s_ota.can_rollback = 0;
lsm 0:b21e585b3aa1 816 return 0;
lsm 0:b21e585b3aa1 817 (void) ctx;
lsm 0:b21e585b3aa1 818 }
lsm 0:b21e585b3aa1 819
lsm 0:b21e585b3aa1 820 #elif (defined(__linux__) || defined(__APPLE__))
lsm 0:b21e585b3aa1 821
lsm 0:b21e585b3aa1 822 #include <errno.h>
lsm 0:b21e585b3aa1 823 #include <signal.h>
lsm 0:b21e585b3aa1 824 #include <sys/stat.h>
lsm 0:b21e585b3aa1 825 #include <sys/types.h>
lsm 0:b21e585b3aa1 826 #include <sys/wait.h>
lsm 0:b21e585b3aa1 827 #include <time.h>
lsm 0:b21e585b3aa1 828 #include <unistd.h>
lsm 0:b21e585b3aa1 829
lsm 0:b21e585b3aa1 830 #define FRESHEN_OTA_FILE "./tmp"
lsm 0:b21e585b3aa1 831
lsm 0:b21e585b3aa1 832 #ifdef __APPLE__
lsm 0:b21e585b3aa1 833 /* https://groups.google.com/forum/#!topic/comp.unix.programmer/JlTIncgY-vg */
lsm 0:b21e585b3aa1 834 int sigtimedwait(const sigset_t *set, siginfo_t *info,
lsm 0:b21e585b3aa1 835 const struct timespec *timeout) {
lsm 0:b21e585b3aa1 836 struct timespec elapsed = {0, 0}, rem;
lsm 0:b21e585b3aa1 837 sigset_t pending;
lsm 0:b21e585b3aa1 838 int signo;
lsm 0:b21e585b3aa1 839 long ns;
lsm 0:b21e585b3aa1 840
lsm 0:b21e585b3aa1 841 do {
lsm 0:b21e585b3aa1 842 sigpending(&pending); /* doesn't clear pending queue */
lsm 0:b21e585b3aa1 843 for (signo = 1; signo < NSIG; signo++) {
lsm 0:b21e585b3aa1 844 if (sigismember(set, signo) && sigismember(&pending, signo)) {
lsm 0:b21e585b3aa1 845 if (info) {
lsm 0:b21e585b3aa1 846 memset(info, 0, sizeof *info);
lsm 0:b21e585b3aa1 847 info->si_signo = signo;
lsm 0:b21e585b3aa1 848 }
lsm 0:b21e585b3aa1 849
lsm 0:b21e585b3aa1 850 return signo;
lsm 0:b21e585b3aa1 851 }
lsm 0:b21e585b3aa1 852 }
lsm 0:b21e585b3aa1 853 ns = 200000000L; /* 2/10th second */
lsm 0:b21e585b3aa1 854 nanosleep(&(struct timespec){0, ns}, &rem);
lsm 0:b21e585b3aa1 855 ns -= rem.tv_nsec;
lsm 0:b21e585b3aa1 856 elapsed.tv_sec += (elapsed.tv_nsec + ns) / 1000000000L;
lsm 0:b21e585b3aa1 857 elapsed.tv_nsec = (elapsed.tv_nsec + ns) % 1000000000L;
lsm 0:b21e585b3aa1 858 } while (elapsed.tv_sec < timeout->tv_sec ||
lsm 0:b21e585b3aa1 859 (elapsed.tv_sec == timeout->tv_sec &&
lsm 0:b21e585b3aa1 860 elapsed.tv_nsec < timeout->tv_nsec));
lsm 0:b21e585b3aa1 861 errno = EAGAIN;
lsm 0:b21e585b3aa1 862 return -1;
lsm 0:b21e585b3aa1 863 }
lsm 0:b21e585b3aa1 864 #endif
lsm 0:b21e585b3aa1 865
lsm 0:b21e585b3aa1 866 static struct {
lsm 0:b21e585b3aa1 867 int can_rollback;
lsm 0:b21e585b3aa1 868 FILE *ota_file;
lsm 0:b21e585b3aa1 869 } s_ota = {0, NULL};
lsm 0:b21e585b3aa1 870
lsm 0:b21e585b3aa1 871 static const char *freshen_arg(int n) {
lsm 0:b21e585b3aa1 872 #if defined(__linux__)
lsm 0:b21e585b3aa1 873 static char cmdline[8192];
lsm 0:b21e585b3aa1 874 FILE *f = fopen("/proc/self/cmdline", "r");
lsm 0:b21e585b3aa1 875 if (f == NULL) {
lsm 0:b21e585b3aa1 876 return NULL;
lsm 0:b21e585b3aa1 877 }
lsm 0:b21e585b3aa1 878 int sz = fread(cmdline, 1, sizeof(cmdline), f);
lsm 0:b21e585b3aa1 879 if (sz < 0) {
lsm 0:b21e585b3aa1 880 return NULL;
lsm 0:b21e585b3aa1 881 }
lsm 0:b21e585b3aa1 882 fclose(f);
lsm 0:b21e585b3aa1 883 int i = 0;
lsm 0:b21e585b3aa1 884 char *arg = &cmdline[0];
lsm 0:b21e585b3aa1 885 for (i = 0; i < n && *arg; i++) {
lsm 0:b21e585b3aa1 886 arg = arg + strlen(arg) + 1;
lsm 0:b21e585b3aa1 887 }
lsm 0:b21e585b3aa1 888 if (*arg == '\0') {
lsm 0:b21e585b3aa1 889 return NULL;
lsm 0:b21e585b3aa1 890 }
lsm 0:b21e585b3aa1 891 return arg;
lsm 0:b21e585b3aa1 892 #elif defined(__APPLE__)
lsm 0:b21e585b3aa1 893 extern int *_NSGetArgc(void);
lsm 0:b21e585b3aa1 894 extern char ***_NSGetArgv(void);
lsm 0:b21e585b3aa1 895 if (n < *_NSGetArgc()) {
lsm 0:b21e585b3aa1 896 return (*_NSGetArgv())[n];
lsm 0:b21e585b3aa1 897 }
lsm 0:b21e585b3aa1 898 return NULL;
lsm 0:b21e585b3aa1 899 #endif
lsm 0:b21e585b3aa1 900 }
lsm 0:b21e585b3aa1 901
lsm 0:b21e585b3aa1 902 static void freshen_ota_restart(void) {
lsm 0:b21e585b3aa1 903 FLOGI("called system restart..");
lsm 0:b21e585b3aa1 904 int exit_code = 0;
lsm 0:b21e585b3aa1 905 struct stat st;
lsm 0:b21e585b3aa1 906 if (strcmp(freshen_arg(0), FRESHEN_OTA_FILE) != 0 &&
lsm 0:b21e585b3aa1 907 stat(FRESHEN_OTA_FILE, &st) == 0) {
lsm 0:b21e585b3aa1 908 FLOGI("update is available");
lsm 0:b21e585b3aa1 909 sigset_t mask;
lsm 0:b21e585b3aa1 910 sigemptyset(&mask);
lsm 0:b21e585b3aa1 911 sigaddset(&mask, SIGCHLD);
lsm 0:b21e585b3aa1 912 sigaddset(&mask, SIGUSR1);
lsm 0:b21e585b3aa1 913 if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
lsm 0:b21e585b3aa1 914 FLOGE("sigprocmask(): errno=%d", errno);
lsm 0:b21e585b3aa1 915 goto error;
lsm 0:b21e585b3aa1 916 }
lsm 0:b21e585b3aa1 917 int pid = fork();
lsm 0:b21e585b3aa1 918 FLOGI("pid=%d, ppid=%d", pid, getppid());
lsm 0:b21e585b3aa1 919 if (pid < 0) {
lsm 0:b21e585b3aa1 920 FLOGE("fork(): errno=%d", errno);
lsm 0:b21e585b3aa1 921 goto error;
lsm 0:b21e585b3aa1 922 }
lsm 0:b21e585b3aa1 923 if (pid == 0) {
lsm 0:b21e585b3aa1 924 int argc = 0;
lsm 0:b21e585b3aa1 925 for (; freshen_arg(argc) != NULL; argc++)
lsm 0:b21e585b3aa1 926 ;
lsm 0:b21e585b3aa1 927 char **argv = (char **) calloc(argc + 1, sizeof(char *));
lsm 0:b21e585b3aa1 928 if (argv == NULL) {
lsm 0:b21e585b3aa1 929 exit(EXIT_FAILURE);
lsm 0:b21e585b3aa1 930 }
lsm 0:b21e585b3aa1 931 for (int i = 0; i < argc + 1; i++) {
lsm 0:b21e585b3aa1 932 argv[i] = (char *) freshen_arg(i);
lsm 0:b21e585b3aa1 933 }
lsm 0:b21e585b3aa1 934 argv[0] = FRESHEN_OTA_FILE;
lsm 0:b21e585b3aa1 935 chmod(FRESHEN_OTA_FILE, 0755);
lsm 0:b21e585b3aa1 936 execv(FRESHEN_OTA_FILE, argv);
lsm 0:b21e585b3aa1 937 exit(EXIT_FAILURE);
lsm 0:b21e585b3aa1 938 }
lsm 0:b21e585b3aa1 939
lsm 0:b21e585b3aa1 940 siginfo_t sig;
lsm 0:b21e585b3aa1 941 int status;
lsm 0:b21e585b3aa1 942 struct timespec t;
lsm 0:b21e585b3aa1 943 t.tv_sec = 10;
lsm 0:b21e585b3aa1 944 t.tv_nsec = 0;
lsm 0:b21e585b3aa1 945 for (;;) {
lsm 0:b21e585b3aa1 946 if (sigtimedwait(&mask, &sig, &t) < 0) {
lsm 0:b21e585b3aa1 947 if (errno == EINTR) {
lsm 0:b21e585b3aa1 948 FLOGI("interruped by another signal, continue");
lsm 0:b21e585b3aa1 949 continue;
lsm 0:b21e585b3aa1 950 } else if (errno == EAGAIN) {
lsm 0:b21e585b3aa1 951 FLOGE("timeout, killing child process");
lsm 0:b21e585b3aa1 952 kill(pid, SIGKILL);
lsm 0:b21e585b3aa1 953 } else {
lsm 0:b21e585b3aa1 954 FLOGE("sigtimedwait() failed: errno=%d", errno);
lsm 0:b21e585b3aa1 955 }
lsm 0:b21e585b3aa1 956 break;
lsm 0:b21e585b3aa1 957 }
lsm 0:b21e585b3aa1 958 if (sig.si_signo == SIGUSR1) {
lsm 0:b21e585b3aa1 959 FLOGI("child process succeeded!");
lsm 0:b21e585b3aa1 960 rename(FRESHEN_OTA_FILE, freshen_arg(0));
lsm 0:b21e585b3aa1 961 } else {
lsm 0:b21e585b3aa1 962 FLOGI("child process exited!");
lsm 0:b21e585b3aa1 963 }
lsm 0:b21e585b3aa1 964 break;
lsm 0:b21e585b3aa1 965 }
lsm 0:b21e585b3aa1 966 if (waitpid(pid, &status, 0) < 0) {
lsm 0:b21e585b3aa1 967 FLOGE("waitpid(): errno=%d", errno);
lsm 0:b21e585b3aa1 968 return;
lsm 0:b21e585b3aa1 969 }
lsm 0:b21e585b3aa1 970 exit_code = (WEXITSTATUS(status));
lsm 0:b21e585b3aa1 971 FLOGI("child process exited with %d", exit_code);
lsm 0:b21e585b3aa1 972 goto error;
lsm 0:b21e585b3aa1 973 }
lsm 0:b21e585b3aa1 974 return;
lsm 0:b21e585b3aa1 975 error:
lsm 0:b21e585b3aa1 976 unlink(FRESHEN_OTA_FILE);
lsm 0:b21e585b3aa1 977 }
lsm 0:b21e585b3aa1 978
lsm 0:b21e585b3aa1 979 static int freshen_ota_init(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 980 FLOGI("argv0=%s", freshen_arg(0));
lsm 0:b21e585b3aa1 981 if (strcmp(freshen_arg(0), FRESHEN_OTA_FILE) == 0) {
lsm 0:b21e585b3aa1 982 s_ota.can_rollback = 1;
lsm 0:b21e585b3aa1 983 }
lsm 0:b21e585b3aa1 984 return 0;
lsm 0:b21e585b3aa1 985 (void) ctx;
lsm 0:b21e585b3aa1 986 }
lsm 0:b21e585b3aa1 987
lsm 0:b21e585b3aa1 988 static int freshen_ota_enabled(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 989 return 1;
lsm 0:b21e585b3aa1 990 (void) ctx;
lsm 0:b21e585b3aa1 991 }
lsm 0:b21e585b3aa1 992
lsm 0:b21e585b3aa1 993 static int freshen_ota_begin(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 994 FLOGI("ctx: %p", (void *) ctx);
lsm 0:b21e585b3aa1 995 if (s_ota.ota_file != NULL) {
lsm 0:b21e585b3aa1 996 FLOGE("another OTA process is in-progress");
lsm 0:b21e585b3aa1 997 return -1;
lsm 0:b21e585b3aa1 998 }
lsm 0:b21e585b3aa1 999 s_ota.ota_file = fopen(FRESHEN_OTA_FILE, "wb");
lsm 0:b21e585b3aa1 1000 if (s_ota.ota_file == NULL) {
lsm 0:b21e585b3aa1 1001 FLOGE("failed to open temporary file");
lsm 0:b21e585b3aa1 1002 return -1;
lsm 0:b21e585b3aa1 1003 }
lsm 0:b21e585b3aa1 1004 return 0;
lsm 0:b21e585b3aa1 1005 (void) ctx;
lsm 0:b21e585b3aa1 1006 }
lsm 0:b21e585b3aa1 1007
lsm 0:b21e585b3aa1 1008 static int freshen_ota_end(struct freshen_ctx *ctx, int success) {
lsm 0:b21e585b3aa1 1009 FLOGI("ctx: %p, success=%d", (void *) ctx, success);
lsm 0:b21e585b3aa1 1010 if (s_ota.ota_file == NULL) {
lsm 0:b21e585b3aa1 1011 FLOGE("ota_write: OTA process is not started");
lsm 0:b21e585b3aa1 1012 return -1;
lsm 0:b21e585b3aa1 1013 }
lsm 0:b21e585b3aa1 1014 fclose(s_ota.ota_file);
lsm 0:b21e585b3aa1 1015 s_ota.ota_file = NULL;
lsm 0:b21e585b3aa1 1016 if (!success) {
lsm 0:b21e585b3aa1 1017 FLOGI("not succeeded, remove temporary file");
lsm 0:b21e585b3aa1 1018 unlink(FRESHEN_OTA_FILE);
lsm 0:b21e585b3aa1 1019 } else {
lsm 0:b21e585b3aa1 1020 freshen_ota_restart();
lsm 0:b21e585b3aa1 1021 }
lsm 0:b21e585b3aa1 1022 return 0;
lsm 0:b21e585b3aa1 1023 (void) ctx;
lsm 0:b21e585b3aa1 1024 }
lsm 0:b21e585b3aa1 1025
lsm 0:b21e585b3aa1 1026 static int freshen_ota_write(struct freshen_ctx *ctx, void *buf, size_t bufsz) {
lsm 0:b21e585b3aa1 1027 FLOGI("ctx: %p, bufsz=%zu", (void *) ctx, bufsz);
lsm 0:b21e585b3aa1 1028 if (s_ota.ota_file == NULL) {
lsm 0:b21e585b3aa1 1029 FLOGE("OTA process is not started");
lsm 0:b21e585b3aa1 1030 return -1;
lsm 0:b21e585b3aa1 1031 }
lsm 0:b21e585b3aa1 1032 if (fwrite(buf, bufsz, 1, s_ota.ota_file) != 1) {
lsm 0:b21e585b3aa1 1033 FLOGE("failed to write chunk");
lsm 0:b21e585b3aa1 1034 return -1;
lsm 0:b21e585b3aa1 1035 }
lsm 0:b21e585b3aa1 1036 return 0;
lsm 0:b21e585b3aa1 1037 (void) ctx;
lsm 0:b21e585b3aa1 1038 }
lsm 0:b21e585b3aa1 1039
lsm 0:b21e585b3aa1 1040 static int freshen_ota_commit(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 1041 if (!s_ota.can_rollback) {
lsm 0:b21e585b3aa1 1042 return 0;
lsm 0:b21e585b3aa1 1043 }
lsm 0:b21e585b3aa1 1044 FLOGI("ctx: %p, argv0=%s", (void *) ctx, freshen_arg(0));
lsm 0:b21e585b3aa1 1045 s_ota.can_rollback = 0;
lsm 0:b21e585b3aa1 1046 return kill(getppid(), SIGUSR1);
lsm 0:b21e585b3aa1 1047 (void) ctx;
lsm 0:b21e585b3aa1 1048 }
lsm 0:b21e585b3aa1 1049 #else
lsm 0:b21e585b3aa1 1050
lsm 0:b21e585b3aa1 1051 static int freshen_ota_enabled(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 1052 (void) ctx;
lsm 0:b21e585b3aa1 1053 return 0;
lsm 0:b21e585b3aa1 1054 }
lsm 0:b21e585b3aa1 1055
lsm 0:b21e585b3aa1 1056 static int freshen_ota_init(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 1057 (void) ctx;
lsm 0:b21e585b3aa1 1058 return 0;
lsm 0:b21e585b3aa1 1059 }
lsm 0:b21e585b3aa1 1060
lsm 0:b21e585b3aa1 1061 static int freshen_ota_commit(struct freshen_ctx *ctx) {
lsm 0:b21e585b3aa1 1062 (void) ctx;
lsm 0:b21e585b3aa1 1063 return 0;
lsm 0:b21e585b3aa1 1064 }
lsm 0:b21e585b3aa1 1065
lsm 0:b21e585b3aa1 1066 #endif
lsm 0:b21e585b3aa1 1067
lsm 0:b21e585b3aa1 1068 #if defined(FRESHEN_ENABLE_DASH)
lsm 0:b21e585b3aa1 1069 #define FRESHEN_ENABLE_MBEDTLS
lsm 0:b21e585b3aa1 1070
lsm 0:b21e585b3aa1 1071 #include <errno.h>
lsm 0:b21e585b3aa1 1072 #include <stdarg.h>
lsm 0:b21e585b3aa1 1073 #include <stdbool.h>
lsm 0:b21e585b3aa1 1074 #include <stdint.h>
lsm 0:b21e585b3aa1 1075 #include <stdio.h>
lsm 0:b21e585b3aa1 1076 #include <stdlib.h>
lsm 0:b21e585b3aa1 1077 #include <string.h>
lsm 0:b21e585b3aa1 1078 #include <time.h>
lsm 0:b21e585b3aa1 1079
lsm 0:b21e585b3aa1 1080 #if defined(__unix__) || defined(__APPLE__)
lsm 0:b21e585b3aa1 1081 #define FRESHEN_ENABLE_SOCKET
lsm 0:b21e585b3aa1 1082 #include <signal.h>
lsm 0:b21e585b3aa1 1083 #include <unistd.h>
lsm 0:b21e585b3aa1 1084 #endif
lsm 0:b21e585b3aa1 1085
lsm 0:b21e585b3aa1 1086 #if defined(FRESHEN_ENABLE_SOCKET)
lsm 0:b21e585b3aa1 1087 #include <netdb.h>
lsm 0:b21e585b3aa1 1088 #include <sys/select.h>
lsm 0:b21e585b3aa1 1089 #include <sys/socket.h>
lsm 0:b21e585b3aa1 1090 #endif
lsm 0:b21e585b3aa1 1091
lsm 0:b21e585b3aa1 1092 #if defined(MBED_LIBRARY_VERSION)
lsm 0:b21e585b3aa1 1093 #include "mbed.h"
lsm 0:b21e585b3aa1 1094 #endif
lsm 0:b21e585b3aa1 1095
lsm 0:b21e585b3aa1 1096 #if defined(FRESHEN_ENABLE_MBEDTLS)
lsm 0:b21e585b3aa1 1097 #include "mbedtls/ctr_drbg.h"
lsm 0:b21e585b3aa1 1098 #include "mbedtls/entropy.h"
lsm 0:b21e585b3aa1 1099 #include "mbedtls/net.h"
lsm 0:b21e585b3aa1 1100 #include "mbedtls/ssl.h"
lsm 0:b21e585b3aa1 1101 #endif
lsm 0:b21e585b3aa1 1102
lsm 0:b21e585b3aa1 1103 struct conn {
lsm 0:b21e585b3aa1 1104 int sock;
lsm 0:b21e585b3aa1 1105 bool is_tls;
lsm 0:b21e585b3aa1 1106 bool is_ws;
lsm 0:b21e585b3aa1 1107
lsm 0:b21e585b3aa1 1108 #if defined(MBED_LIBRARY_VERSION)
lsm 0:b21e585b3aa1 1109 TCPSocket *mbedsock;
lsm 0:b21e585b3aa1 1110 #endif
lsm 0:b21e585b3aa1 1111
lsm 0:b21e585b3aa1 1112 #if defined(FRESHEN_ENABLE_MBEDTLS)
lsm 0:b21e585b3aa1 1113 mbedtls_net_context server_fd;
lsm 0:b21e585b3aa1 1114 mbedtls_entropy_context entropy;
lsm 0:b21e585b3aa1 1115 mbedtls_ctr_drbg_context drbg;
lsm 0:b21e585b3aa1 1116 mbedtls_ssl_context ssl;
lsm 0:b21e585b3aa1 1117 mbedtls_ssl_config conf;
lsm 0:b21e585b3aa1 1118 mbedtls_x509_crt cacert;
lsm 0:b21e585b3aa1 1119 #endif
lsm 0:b21e585b3aa1 1120 };
lsm 0:b21e585b3aa1 1121
lsm 0:b21e585b3aa1 1122 static void freshen_net_init_conn(struct conn *c) {
lsm 0:b21e585b3aa1 1123 memset(c, 0, sizeof(*c));
lsm 0:b21e585b3aa1 1124 c->sock = -1;
lsm 0:b21e585b3aa1 1125 }
lsm 0:b21e585b3aa1 1126
lsm 0:b21e585b3aa1 1127 static bool freshen_net_is_disconnected(const struct conn *c) {
lsm 0:b21e585b3aa1 1128 #if defined(MBED_LIBRARY_VERSION)
lsm 0:b21e585b3aa1 1129 return c->mbedsock == NULL;
lsm 0:b21e585b3aa1 1130 #else
lsm 0:b21e585b3aa1 1131 return c->sock == -1;
lsm 0:b21e585b3aa1 1132 #endif
lsm 0:b21e585b3aa1 1133 }
lsm 0:b21e585b3aa1 1134
lsm 0:b21e585b3aa1 1135 static int freshen_net_recv(struct conn *c, void *buf, size_t len) {
lsm 0:b21e585b3aa1 1136 int n = 0;
lsm 0:b21e585b3aa1 1137
lsm 0:b21e585b3aa1 1138 #if defined(FRESHEN_ENABLE_SOCKET)
lsm 0:b21e585b3aa1 1139 /* If socket has no data, return immediately */
lsm 0:b21e585b3aa1 1140 struct timeval tv = {0, 0};
lsm 0:b21e585b3aa1 1141 fd_set rset;
lsm 0:b21e585b3aa1 1142 FD_ZERO(&rset);
lsm 0:b21e585b3aa1 1143 FD_SET(c->sock, &rset);
lsm 0:b21e585b3aa1 1144 if (select(c->sock + 1, &rset, NULL, NULL, &tv) != 1) return n;
lsm 0:b21e585b3aa1 1145 #endif
lsm 0:b21e585b3aa1 1146
lsm 0:b21e585b3aa1 1147 #if defined(FRESHEN_ENABLE_MBEDTLS)
lsm 0:b21e585b3aa1 1148 if (c->is_tls) {
lsm 0:b21e585b3aa1 1149 n = mbedtls_ssl_read(&c->ssl, (unsigned char *) buf, len);
lsm 0:b21e585b3aa1 1150 } else
lsm 0:b21e585b3aa1 1151 #endif
lsm 0:b21e585b3aa1 1152 {
lsm 0:b21e585b3aa1 1153 #if defined(FRESHEN_ENABLE_SOCKET)
lsm 0:b21e585b3aa1 1154 n = recv(c->sock, buf, len, 0);
lsm 0:b21e585b3aa1 1155 #endif
lsm 0:b21e585b3aa1 1156 }
lsm 0:b21e585b3aa1 1157 return n <= 0 ? -1 : n;
lsm 0:b21e585b3aa1 1158 }
lsm 0:b21e585b3aa1 1159
lsm 0:b21e585b3aa1 1160 static int freshen_net_send(struct conn *c, const void *buf, size_t len) {
lsm 0:b21e585b3aa1 1161 #if defined(FRESHEN_ENABLE_MBEDTLS)
lsm 0:b21e585b3aa1 1162 if (c->is_tls) {
lsm 0:b21e585b3aa1 1163 return mbedtls_ssl_write(&c->ssl, (const unsigned char *) buf, len);
lsm 0:b21e585b3aa1 1164 } else
lsm 0:b21e585b3aa1 1165 #endif
lsm 0:b21e585b3aa1 1166 {
lsm 0:b21e585b3aa1 1167 #if defined(FRESHEN_ENABLE_SOCKET)
lsm 0:b21e585b3aa1 1168 return send(c->sock, buf, len, 0);
lsm 0:b21e585b3aa1 1169 #endif
lsm 0:b21e585b3aa1 1170 }
lsm 0:b21e585b3aa1 1171 }
lsm 0:b21e585b3aa1 1172
lsm 0:b21e585b3aa1 1173 static void freshen_net_close(struct conn *c) {
lsm 0:b21e585b3aa1 1174 #if defined(MBED_LIBRARY_VERSION)
lsm 0:b21e585b3aa1 1175 if (c->mbedsock != NULL) c->mbedsock->close();
lsm 0:b21e585b3aa1 1176 c->mbedsock = NULL;
lsm 0:b21e585b3aa1 1177 FLOGI("closed mbedsock");
lsm 0:b21e585b3aa1 1178 #endif
lsm 0:b21e585b3aa1 1179
lsm 0:b21e585b3aa1 1180 #if defined(FRESHEN_ENABLE_MBEDTLS)
lsm 0:b21e585b3aa1 1181 if (c->is_tls) {
lsm 0:b21e585b3aa1 1182 #if defined(FRESHEN_ENABLE_SOCKET)
lsm 0:b21e585b3aa1 1183 mbedtls_net_free(&c->server_fd);
lsm 0:b21e585b3aa1 1184 #endif
lsm 0:b21e585b3aa1 1185 mbedtls_ssl_free(&c->ssl);
lsm 0:b21e585b3aa1 1186 mbedtls_ssl_config_free(&c->conf);
lsm 0:b21e585b3aa1 1187 mbedtls_ctr_drbg_free(&c->drbg);
lsm 0:b21e585b3aa1 1188 mbedtls_entropy_free(&c->entropy);
lsm 0:b21e585b3aa1 1189 mbedtls_x509_crt_free(&c->cacert);
lsm 0:b21e585b3aa1 1190 }
lsm 0:b21e585b3aa1 1191 #endif
lsm 0:b21e585b3aa1 1192
lsm 0:b21e585b3aa1 1193 if (c->sock != -1) close(c->sock);
lsm 0:b21e585b3aa1 1194 c->sock = -1;
lsm 0:b21e585b3aa1 1195 c->is_ws = false;
lsm 0:b21e585b3aa1 1196 }
lsm 0:b21e585b3aa1 1197
lsm 0:b21e585b3aa1 1198 #if defined(MBED_LIBRARY_VERSION)
lsm 0:b21e585b3aa1 1199 NetworkInterface *freshen_net;
lsm 0:b21e585b3aa1 1200
lsm 0:b21e585b3aa1 1201 static int tls_net_recv(void *ctx, unsigned char *buf, size_t len) {
lsm 0:b21e585b3aa1 1202 TCPSocket *socket = static_cast<TCPSocket *>(ctx);
lsm 0:b21e585b3aa1 1203 int n = socket->recv(buf, len);
lsm 0:b21e585b3aa1 1204 return n;
lsm 0:b21e585b3aa1 1205 }
lsm 0:b21e585b3aa1 1206
lsm 0:b21e585b3aa1 1207 static int tls_net_send(void *ctx, const unsigned char *buf, size_t len) {
lsm 0:b21e585b3aa1 1208 TCPSocket *socket = static_cast<TCPSocket *>(ctx);
lsm 0:b21e585b3aa1 1209 int n = socket->send(buf, len);
lsm 0:b21e585b3aa1 1210 return n;
lsm 0:b21e585b3aa1 1211 }
lsm 0:b21e585b3aa1 1212
lsm 0:b21e585b3aa1 1213 void mbedtls_net_init(mbedtls_net_context *ctx) {
lsm 0:b21e585b3aa1 1214 (void) ctx;
lsm 0:b21e585b3aa1 1215 }
lsm 0:b21e585b3aa1 1216
lsm 0:b21e585b3aa1 1217 void mbedtls_net_free(mbedtls_net_context *ctx) {
lsm 0:b21e585b3aa1 1218 (void) ctx;
lsm 0:b21e585b3aa1 1219 }
lsm 0:b21e585b3aa1 1220
lsm 0:b21e585b3aa1 1221 int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len,
lsm 0:b21e585b3aa1 1222 size_t *olen) {
lsm 0:b21e585b3aa1 1223 memset(output, 0, len);
lsm 0:b21e585b3aa1 1224 return 0;
lsm 0:b21e585b3aa1 1225 }
lsm 0:b21e585b3aa1 1226 #endif
lsm 0:b21e585b3aa1 1227
lsm 0:b21e585b3aa1 1228 static bool fwsconnect(struct conn *c, const char *host, const char *port,
lsm 0:b21e585b3aa1 1229 const char *uri, const char *token) {
lsm 0:b21e585b3aa1 1230 int n, sent;
lsm 0:b21e585b3aa1 1231 char buf[512];
lsm 0:b21e585b3aa1 1232 if ((n = snprintf(buf, sizeof(buf),
lsm 0:b21e585b3aa1 1233 "GET %s HTTP/1.1\r\n"
lsm 0:b21e585b3aa1 1234 "Host: %s:%s\r\n"
lsm 0:b21e585b3aa1 1235 "Authorization: Bearer %s\r\n"
lsm 0:b21e585b3aa1 1236 "Sec-WebSocket-Version: 13\r\n"
lsm 0:b21e585b3aa1 1237 "Sec-WebSocket-Key: p0EAAPE61hDZrLdgKgy1Og==\r\n"
lsm 0:b21e585b3aa1 1238 "Sec-WebSocket-Protocol: dash.freshen.cc\r\n"
lsm 0:b21e585b3aa1 1239 "Upgrade: websocket\r\n"
lsm 0:b21e585b3aa1 1240 "Connection: Upgrade\r\n"
lsm 0:b21e585b3aa1 1241 "Origin: http://%s\r\n"
lsm 0:b21e585b3aa1 1242 "\r\n",
lsm 0:b21e585b3aa1 1243 uri, host, port, token, host)) > (int) sizeof(buf) ||
lsm 0:b21e585b3aa1 1244 n < 0) {
lsm 0:b21e585b3aa1 1245 FLOGE("snprintf: %d", n);
lsm 0:b21e585b3aa1 1246 } else if ((sent = freshen_net_send(c, buf, n)) != n) {
lsm 0:b21e585b3aa1 1247 FLOGE("send(%d) = %d : %d", n, sent, errno);
lsm 0:b21e585b3aa1 1248 } else {
lsm 0:b21e585b3aa1 1249 return true;
lsm 0:b21e585b3aa1 1250 }
lsm 0:b21e585b3aa1 1251 freshen_net_close(c);
lsm 0:b21e585b3aa1 1252 return false;
lsm 0:b21e585b3aa1 1253 }
lsm 0:b21e585b3aa1 1254
lsm 0:b21e585b3aa1 1255 static bool freconnect(const char *url, const char *token, struct conn *c) {
lsm 0:b21e585b3aa1 1256 #if defined(FRESHEN_ENABLE_SOCKET)
lsm 0:b21e585b3aa1 1257 struct sockaddr_in sin;
lsm 0:b21e585b3aa1 1258 struct hostent *he;
lsm 0:b21e585b3aa1 1259 #endif
lsm 0:b21e585b3aa1 1260 char proto[10], host[100], port[10] = "80", uri[20];
lsm 0:b21e585b3aa1 1261 int sock;
lsm 0:b21e585b3aa1 1262
lsm 0:b21e585b3aa1 1263 if (!freshen_net_is_disconnected(c)) return true;
lsm 0:b21e585b3aa1 1264
lsm 0:b21e585b3aa1 1265 sock = c->sock = -1;
lsm 0:b21e585b3aa1 1266 proto[0] = host[0] = port[0] = uri[0] = '\0';
lsm 0:b21e585b3aa1 1267 if (sscanf(url, "%9[^:]://%99[^:]:%9[0-9]%19s", proto, host, port, uri) !=
lsm 0:b21e585b3aa1 1268 4) {
lsm 0:b21e585b3aa1 1269 if (strcmp(proto, "wss") == 0) strcpy(port, "443");
lsm 0:b21e585b3aa1 1270 sscanf(url, "%9[^:]://%99[^/]%19s", proto, host, uri);
lsm 0:b21e585b3aa1 1271 }
lsm 0:b21e585b3aa1 1272 c->is_tls = strcmp(proto, "wss") == 0 ? true : false;
lsm 0:b21e585b3aa1 1273 FLOGI("dst [%s][%s][%s][%s]", proto, host, port, uri);
lsm 0:b21e585b3aa1 1274
lsm 0:b21e585b3aa1 1275 #if defined(FRESHEN_ENABLE_SOCKET)
lsm 0:b21e585b3aa1 1276 memset(&sin, 0, sizeof(sin));
lsm 0:b21e585b3aa1 1277 sin.sin_family = AF_INET;
lsm 0:b21e585b3aa1 1278 sin.sin_port = htons((uint16_t) atoi(port));
lsm 0:b21e585b3aa1 1279 if ((he = gethostbyname(host)) == NULL) {
lsm 0:b21e585b3aa1 1280 FLOGE("gethostbyname(%s): %d", host, errno);
lsm 0:b21e585b3aa1 1281 } else if (!memcpy(&sin.sin_addr, he->h_addr_list[0], sizeof(sin.sin_addr))) {
lsm 0:b21e585b3aa1 1282 } else if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
lsm 0:b21e585b3aa1 1283 FLOGE("socket: %d", errno);
lsm 0:b21e585b3aa1 1284 } else if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
lsm 0:b21e585b3aa1 1285 FLOGE("connect: %d", errno);
lsm 0:b21e585b3aa1 1286 close(sock);
lsm 0:b21e585b3aa1 1287 } else if (!c->is_tls) {
lsm 0:b21e585b3aa1 1288 c->sock = sock;
lsm 0:b21e585b3aa1 1289 return true;
lsm 0:b21e585b3aa1 1290 } else {
lsm 0:b21e585b3aa1 1291 #elif defined(MBED_LIBRARY_VERSION)
lsm 0:b21e585b3aa1 1292 if (freshen_net == NULL) {
lsm 0:b21e585b3aa1 1293 FLOGE("freshen_net pointer is not set");
lsm 0:b21e585b3aa1 1294 } else if ((c->mbedsock = new TCPSocket(freshen_net)) == NULL) {
lsm 0:b21e585b3aa1 1295 FLOGE("cant create tcp socket");
lsm 0:b21e585b3aa1 1296 } else if (c->mbedsock->connect(host, atoi(port)) != NSAPI_ERROR_OK) {
lsm 0:b21e585b3aa1 1297 FLOGE("mbed connect(%s:%d): %d", host, atoi(port), errno);
lsm 0:b21e585b3aa1 1298 } else {
lsm 0:b21e585b3aa1 1299 #endif
lsm 0:b21e585b3aa1 1300 #if defined(FRESHEN_ENABLE_MBEDTLS)
lsm 0:b21e585b3aa1 1301
lsm 0:b21e585b3aa1 1302 #if !defined(FRESHEN_CA_PEM)
lsm 0:b21e585b3aa1 1303 #define FRESHEN_CA_PEM \
lsm 0:b21e585b3aa1 1304 "-----BEGIN CERTIFICATE-----\n" \
lsm 0:b21e585b3aa1 1305 "MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \
lsm 0:b21e585b3aa1 1306 "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \
lsm 0:b21e585b3aa1 1307 "DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \
lsm 0:b21e585b3aa1 1308 "SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \
lsm 0:b21e585b3aa1 1309 "GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
lsm 0:b21e585b3aa1 1310 "AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \
lsm 0:b21e585b3aa1 1311 "q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \
lsm 0:b21e585b3aa1 1312 "SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \
lsm 0:b21e585b3aa1 1313 "Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \
lsm 0:b21e585b3aa1 1314 "a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \
lsm 0:b21e585b3aa1 1315 "/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \
lsm 0:b21e585b3aa1 1316 "AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \
lsm 0:b21e585b3aa1 1317 "CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \
lsm 0:b21e585b3aa1 1318 "bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \
lsm 0:b21e585b3aa1 1319 "c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \
lsm 0:b21e585b3aa1 1320 "VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \
lsm 0:b21e585b3aa1 1321 "ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \
lsm 0:b21e585b3aa1 1322 "MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \
lsm 0:b21e585b3aa1 1323 "Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \
lsm 0:b21e585b3aa1 1324 "AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \
lsm 0:b21e585b3aa1 1325 "uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \
lsm 0:b21e585b3aa1 1326 "wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \
lsm 0:b21e585b3aa1 1327 "X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \
lsm 0:b21e585b3aa1 1328 "PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \
lsm 0:b21e585b3aa1 1329 "KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \
lsm 0:b21e585b3aa1 1330 "-----END CERTIFICATE-----"
lsm 0:b21e585b3aa1 1331 #endif
lsm 0:b21e585b3aa1 1332
lsm 0:b21e585b3aa1 1333 const char *ca_pem = FRESHEN_CA_PEM;
lsm 0:b21e585b3aa1 1334 int res;
lsm 0:b21e585b3aa1 1335 mbedtls_ssl_init(&c->ssl);
lsm 0:b21e585b3aa1 1336 mbedtls_ssl_config_init(&c->conf);
lsm 0:b21e585b3aa1 1337 mbedtls_entropy_init(&c->entropy);
lsm 0:b21e585b3aa1 1338 mbedtls_ctr_drbg_init(&c->drbg);
lsm 0:b21e585b3aa1 1339 mbedtls_x509_crt_init(&c->cacert);
lsm 0:b21e585b3aa1 1340 mbedtls_ctr_drbg_seed(&c->drbg, mbedtls_entropy_func, &c->entropy, 0, 0);
lsm 0:b21e585b3aa1 1341 mbedtls_ssl_config_defaults(&c->conf, MBEDTLS_SSL_IS_CLIENT,
lsm 0:b21e585b3aa1 1342 MBEDTLS_SSL_TRANSPORT_STREAM,
lsm 0:b21e585b3aa1 1343 MBEDTLS_SSL_PRESET_DEFAULT);
lsm 0:b21e585b3aa1 1344 mbedtls_ssl_conf_rng(&c->conf, mbedtls_ctr_drbg_random, &c->drbg);
lsm 0:b21e585b3aa1 1345 res = mbedtls_x509_crt_parse(&c->cacert, (const unsigned char *) ca_pem,
lsm 0:b21e585b3aa1 1346 strlen(ca_pem) + 1); /* must contain \0 */
lsm 0:b21e585b3aa1 1347 if (res != 0) FLOGE("crt_parse: -%#x", -res);
lsm 0:b21e585b3aa1 1348 mbedtls_ssl_conf_ca_chain(&c->conf, &c->cacert, NULL);
lsm 0:b21e585b3aa1 1349 mbedtls_ssl_conf_authmode(&c->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
lsm 0:b21e585b3aa1 1350
lsm 0:b21e585b3aa1 1351 res = mbedtls_ssl_setup(&c->ssl, &c->conf);
lsm 0:b21e585b3aa1 1352 if (res != 0) FLOGE("ssl_setup: -%#x", -res);
lsm 0:b21e585b3aa1 1353 mbedtls_ssl_set_hostname(&c->ssl, host);
lsm 0:b21e585b3aa1 1354 #if defined(FRESHEN_ENABLE_SOCKET)
lsm 0:b21e585b3aa1 1355 mbedtls_net_init(&c->server_fd);
lsm 0:b21e585b3aa1 1356 c->server_fd.fd = c->sock = sock;
lsm 0:b21e585b3aa1 1357 mbedtls_ssl_set_bio(&c->ssl, &c->server_fd, mbedtls_net_send,
lsm 0:b21e585b3aa1 1358 mbedtls_net_recv, NULL);
lsm 0:b21e585b3aa1 1359 #elif defined(MBED_LIBRARY_VERSION)
lsm 0:b21e585b3aa1 1360 mbedtls_ssl_set_bio(&c->ssl, static_cast<void *>(c->mbedsock), tls_net_send,
lsm 0:b21e585b3aa1 1361 tls_net_recv, NULL);
lsm 0:b21e585b3aa1 1362 #endif
lsm 0:b21e585b3aa1 1363 if ((res = mbedtls_ssl_handshake(&c->ssl)) == 0) {
lsm 0:b21e585b3aa1 1364 return fwsconnect(c, host, port, uri, token);
lsm 0:b21e585b3aa1 1365 }
lsm 0:b21e585b3aa1 1366 FLOGE("handshake error: -%#x", -res);
lsm 0:b21e585b3aa1 1367 #else
lsm 0:b21e585b3aa1 1368 FLOGE("TLS support is not enabled");
lsm 0:b21e585b3aa1 1369 #endif
lsm 0:b21e585b3aa1 1370 }
lsm 0:b21e585b3aa1 1371
lsm 0:b21e585b3aa1 1372 freshen_net_close(c);
lsm 0:b21e585b3aa1 1373 return false;
lsm 0:b21e585b3aa1 1374 }
lsm 0:b21e585b3aa1 1375
lsm 0:b21e585b3aa1 1376 static void freshen_sleep(int seconds) {
lsm 0:b21e585b3aa1 1377 #if defined(MBED_LIBRARY_VERSION)
lsm 0:b21e585b3aa1 1378 wait(seconds);
lsm 0:b21e585b3aa1 1379 #else
lsm 0:b21e585b3aa1 1380 sleep(seconds);
lsm 0:b21e585b3aa1 1381 #endif
lsm 0:b21e585b3aa1 1382 }
lsm 0:b21e585b3aa1 1383 #endif
lsm 0:b21e585b3aa1 1384 #if defined(FRESHEN_ENABLE_DASH)
lsm 0:b21e585b3aa1 1385 #define FRESHEN_ENABLE_SOCKET
lsm 0:b21e585b3aa1 1386 #define FRESHEN_ENABLE_MBEDTLS
lsm 0:b21e585b3aa1 1387 #endif
lsm 0:b21e585b3aa1 1388
lsm 0:b21e585b3aa1 1389 #if defined(FRESHEN_ENABLE_DASH)
lsm 0:b21e585b3aa1 1390
lsm 0:b21e585b3aa1 1391 // WEBSOCKET
lsm 0:b21e585b3aa1 1392 #define WEBSOCKET_OP_CONTINUE 0
lsm 0:b21e585b3aa1 1393 #define WEBSOCKET_OP_TEXT 1
lsm 0:b21e585b3aa1 1394 #define WEBSOCKET_OP_BINARY 2
lsm 0:b21e585b3aa1 1395 #define WEBSOCKET_OP_CLOSE 8
lsm 0:b21e585b3aa1 1396 #define WEBSOCKET_OP_PING 9
lsm 0:b21e585b3aa1 1397 #define WEBSOCKET_OP_PONG 10
lsm 0:b21e585b3aa1 1398
lsm 0:b21e585b3aa1 1399 #define FLAGS_MASK_FIN (1 << 7)
lsm 0:b21e585b3aa1 1400 #define FLAGS_MASK_OP 0x0f
lsm 0:b21e585b3aa1 1401
lsm 0:b21e585b3aa1 1402 struct ws_msg {
lsm 0:b21e585b3aa1 1403 int header_len;
lsm 0:b21e585b3aa1 1404 int data_len;
lsm 0:b21e585b3aa1 1405 int flags;
lsm 0:b21e585b3aa1 1406 };
lsm 0:b21e585b3aa1 1407
lsm 0:b21e585b3aa1 1408 #ifndef IN_BUF_SIZE
lsm 0:b21e585b3aa1 1409 #define IN_BUF_SIZE 4096
lsm 0:b21e585b3aa1 1410 #endif
lsm 0:b21e585b3aa1 1411
lsm 0:b21e585b3aa1 1412 #ifndef OUT_BUF_SIZE
lsm 0:b21e585b3aa1 1413 #define OUT_BUF_SIZE IN_BUF_SIZE
lsm 0:b21e585b3aa1 1414 #endif
lsm 0:b21e585b3aa1 1415
lsm 0:b21e585b3aa1 1416 #ifndef QUEUE_BUF_SIZE
lsm 0:b21e585b3aa1 1417 #define QUEUE_BUF_SIZE OUT_BUF_SIZE
lsm 0:b21e585b3aa1 1418 #endif
lsm 0:b21e585b3aa1 1419
lsm 0:b21e585b3aa1 1420 static int parse_ws_frame(char *buf, int len, struct ws_msg *msg) {
lsm 0:b21e585b3aa1 1421 int i, n = 0, mask_len = 0;
lsm 0:b21e585b3aa1 1422 msg->header_len = msg->data_len = 0;
lsm 0:b21e585b3aa1 1423 if (len >= 2) {
lsm 0:b21e585b3aa1 1424 n = buf[1] & 0x7f;
lsm 0:b21e585b3aa1 1425 mask_len = buf[1] & FLAGS_MASK_FIN ? 4 : 0;
lsm 0:b21e585b3aa1 1426 msg->flags = *(unsigned char *) buf;
lsm 0:b21e585b3aa1 1427 if (n < 126 && len >= mask_len) {
lsm 0:b21e585b3aa1 1428 msg->data_len = n;
lsm 0:b21e585b3aa1 1429 msg->header_len = 2 + mask_len;
lsm 0:b21e585b3aa1 1430 } else if (n == 126 && len >= 4 + mask_len) {
lsm 0:b21e585b3aa1 1431 msg->header_len = 4 + mask_len;
lsm 0:b21e585b3aa1 1432 msg->data_len = ntohs(*(uint16_t *) &buf[2]);
lsm 0:b21e585b3aa1 1433 } else if (len >= 10 + mask_len) {
lsm 0:b21e585b3aa1 1434 msg->header_len = 10 + mask_len;
lsm 0:b21e585b3aa1 1435 msg->data_len = (((uint64_t) ntohl(*(uint32_t *) &buf[2])) << 32) +
lsm 0:b21e585b3aa1 1436 ntohl(*(uint32_t *) &buf[6]);
lsm 0:b21e585b3aa1 1437 }
lsm 0:b21e585b3aa1 1438 }
lsm 0:b21e585b3aa1 1439 if (msg->header_len + msg->data_len > len) return 0;
lsm 0:b21e585b3aa1 1440 /* Apply mask */
lsm 0:b21e585b3aa1 1441 if (mask_len > 0) {
lsm 0:b21e585b3aa1 1442 for (i = 0; i < msg->data_len; i++) {
lsm 0:b21e585b3aa1 1443 buf[i + msg->header_len] ^= (buf + msg->header_len - mask_len)[i % 4];
lsm 0:b21e585b3aa1 1444 }
lsm 0:b21e585b3aa1 1445 }
lsm 0:b21e585b3aa1 1446 return msg->header_len + msg->data_len;
lsm 0:b21e585b3aa1 1447 }
lsm 0:b21e585b3aa1 1448
lsm 0:b21e585b3aa1 1449 static void ws_send(struct conn *c, int op, char *buf, int len) {
lsm 0:b21e585b3aa1 1450 unsigned char header[10];
lsm 0:b21e585b3aa1 1451 int i, header_len = 0;
lsm 0:b21e585b3aa1 1452 uint8_t mask[4] = {0x71, 0x3e, 0x5a, 0xcc}; /* it is random, i bet ya */
lsm 0:b21e585b3aa1 1453 header[0] = op | FLAGS_MASK_FIN;
lsm 0:b21e585b3aa1 1454
lsm 0:b21e585b3aa1 1455 if (len < 126) {
lsm 0:b21e585b3aa1 1456 header[1] = (unsigned char) len;
lsm 0:b21e585b3aa1 1457 header_len = 2;
lsm 0:b21e585b3aa1 1458 } else if (len < 65535) {
lsm 0:b21e585b3aa1 1459 uint16_t tmp = htons((uint16_t) len);
lsm 0:b21e585b3aa1 1460 header[1] = 126;
lsm 0:b21e585b3aa1 1461 memcpy(&header[2], &tmp, sizeof(tmp));
lsm 0:b21e585b3aa1 1462 header_len = 4;
lsm 0:b21e585b3aa1 1463 } else {
lsm 0:b21e585b3aa1 1464 uint32_t tmp;
lsm 0:b21e585b3aa1 1465 header[1] = 127;
lsm 0:b21e585b3aa1 1466 tmp = htonl((uint32_t)((uint64_t) len >> 32));
lsm 0:b21e585b3aa1 1467 memcpy(&header[2], &tmp, sizeof(tmp));
lsm 0:b21e585b3aa1 1468 tmp = htonl((uint32_t)(len & 0xffffffff));
lsm 0:b21e585b3aa1 1469 memcpy(&header[6], &tmp, sizeof(tmp));
lsm 0:b21e585b3aa1 1470 header_len = 10;
lsm 0:b21e585b3aa1 1471 }
lsm 0:b21e585b3aa1 1472 header[1] |= 1 << 7; /* set masking flag */
lsm 0:b21e585b3aa1 1473 freshen_net_send(c, header, header_len);
lsm 0:b21e585b3aa1 1474 freshen_net_send(c, mask, sizeof(mask));
lsm 0:b21e585b3aa1 1475 for (i = 0; i < len; i++) {
lsm 0:b21e585b3aa1 1476 buf[i] ^= mask[i % sizeof(mask)];
lsm 0:b21e585b3aa1 1477 }
lsm 0:b21e585b3aa1 1478 freshen_net_send(c, buf, len);
lsm 0:b21e585b3aa1 1479 }
lsm 0:b21e585b3aa1 1480
lsm 0:b21e585b3aa1 1481 static int info(const char *args, char *buf, size_t len, void *userdata) {
lsm 0:b21e585b3aa1 1482 struct freshen_ctx *ctx = (struct freshen_ctx *) userdata;
lsm 0:b21e585b3aa1 1483 const char *arch =
lsm 0:b21e585b3aa1 1484 #if defined(__APPLE__)
lsm 0:b21e585b3aa1 1485 "darwin"
lsm 0:b21e585b3aa1 1486 #elif defined(__linux__)
lsm 0:b21e585b3aa1 1487 "linux"
lsm 0:b21e585b3aa1 1488 #elif defined(ESP_PLATFORM)
lsm 0:b21e585b3aa1 1489 "esp32"
lsm 0:b21e585b3aa1 1490 #elif defined(MBED_LIBRARY_VERSION)
lsm 0:b21e585b3aa1 1491 "mbedOS"
lsm 0:b21e585b3aa1 1492 #else
lsm 0:b21e585b3aa1 1493 "posix"
lsm 0:b21e585b3aa1 1494 #endif
lsm 0:b21e585b3aa1 1495 ;
lsm 0:b21e585b3aa1 1496 int ota_enabled = freshen_ota_enabled(ctx);
lsm 0:b21e585b3aa1 1497 snprintf(buf, len,
lsm 0:b21e585b3aa1 1498 "{\"fw_version\": \"%s\",\"arch\": \"%s\""
lsm 0:b21e585b3aa1 1499 ",\"ota_enabled\": %s,\"built\": \"%s\"}",
lsm 0:b21e585b3aa1 1500 (const char *) userdata, arch, ota_enabled ? "true" : "false",
lsm 0:b21e585b3aa1 1501 __DATE__ " " __TIME__);
lsm 0:b21e585b3aa1 1502 return 0;
lsm 0:b21e585b3aa1 1503 (void) args;
lsm 0:b21e585b3aa1 1504 }
lsm 0:b21e585b3aa1 1505
lsm 0:b21e585b3aa1 1506 static int rpclist(const char *args, char *buf, size_t len, void *userdata) {
lsm 0:b21e585b3aa1 1507 struct freshen_ctx *ctx = (struct freshen_ctx *) userdata;
lsm 0:b21e585b3aa1 1508 struct freshen_method *m;
lsm 0:b21e585b3aa1 1509 const char *comma = "";
lsm 0:b21e585b3aa1 1510 buf[0] = '\0';
lsm 0:b21e585b3aa1 1511 snprintf(buf + strlen(buf), len - strlen(buf), "[");
lsm 0:b21e585b3aa1 1512 for (m = ctx->methods; m != NULL; m = m->next) {
lsm 0:b21e585b3aa1 1513 snprintf(buf + strlen(buf), len - strlen(buf), "%s\"%s\"", comma,
lsm 0:b21e585b3aa1 1514 m->method);
lsm 0:b21e585b3aa1 1515 comma = ",";
lsm 0:b21e585b3aa1 1516 }
lsm 0:b21e585b3aa1 1517 snprintf(buf + strlen(buf), len - strlen(buf), "]");
lsm 0:b21e585b3aa1 1518 return 0;
lsm 0:b21e585b3aa1 1519 (void) args;
lsm 0:b21e585b3aa1 1520 }
lsm 0:b21e585b3aa1 1521
lsm 0:b21e585b3aa1 1522 struct privdata {
lsm 0:b21e585b3aa1 1523 struct conn conn;
lsm 0:b21e585b3aa1 1524 char *queue, *in, *out;
lsm 0:b21e585b3aa1 1525 int in_len;
lsm 0:b21e585b3aa1 1526 };
lsm 0:b21e585b3aa1 1527
lsm 0:b21e585b3aa1 1528 static void freshen_poll(struct freshen_ctx *ctx, const char *url,
lsm 0:b21e585b3aa1 1529 const char *version, const char *token) {
lsm 0:b21e585b3aa1 1530 struct privdata *pd = (struct privdata *) ctx->privdata;
lsm 0:b21e585b3aa1 1531 if (pd == NULL) {
lsm 0:b21e585b3aa1 1532 ctx->privdata = pd = (struct privdata *) calloc(1, sizeof(*pd));
lsm 0:b21e585b3aa1 1533 #if defined(__unix__)
lsm 0:b21e585b3aa1 1534 signal(SIGPIPE, SIG_IGN);
lsm 0:b21e585b3aa1 1535 #endif
lsm 0:b21e585b3aa1 1536 ctx->queue.buf = malloc(QUEUE_BUF_SIZE);
lsm 0:b21e585b3aa1 1537 ctx->queue.buf_sz = QUEUE_BUF_SIZE;
lsm 0:b21e585b3aa1 1538
lsm 0:b21e585b3aa1 1539 freshen_net_init_conn(&pd->conn);
lsm 0:b21e585b3aa1 1540
lsm 0:b21e585b3aa1 1541 freshen_core_export(ctx, "info", info, (void *) version);
lsm 0:b21e585b3aa1 1542 freshen_core_export(ctx, "rpc.list", rpclist, ctx);
lsm 0:b21e585b3aa1 1543
lsm 0:b21e585b3aa1 1544 #if defined(__unix__) || defined(__APPLE__) || defined(ESP_PLATFORM)
lsm 0:b21e585b3aa1 1545 freshen_core_export(ctx, "ota.begin", freshen_rpc_ota_begin, ctx);
lsm 0:b21e585b3aa1 1546 freshen_core_export(ctx, "ota.write", freshen_rpc_ota_write, ctx);
lsm 0:b21e585b3aa1 1547 freshen_core_export(ctx, "ota.end", freshen_rpc_ota_end, ctx);
lsm 0:b21e585b3aa1 1548 #endif
lsm 0:b21e585b3aa1 1549
lsm 0:b21e585b3aa1 1550 pd->in = (char *) malloc(IN_BUF_SIZE);
lsm 0:b21e585b3aa1 1551 pd->out = (char *) malloc(OUT_BUF_SIZE);
lsm 0:b21e585b3aa1 1552
lsm 0:b21e585b3aa1 1553 pd->in[0] = pd->out[0] = ((char *) ctx->queue.buf)[0] = '\0';
lsm 0:b21e585b3aa1 1554
lsm 0:b21e585b3aa1 1555 freshen_ota_init(ctx);
lsm 0:b21e585b3aa1 1556 }
lsm 0:b21e585b3aa1 1557
lsm 0:b21e585b3aa1 1558 if (!freconnect(url, token, &pd->conn)) {
lsm 0:b21e585b3aa1 1559 freshen_sleep(1);
lsm 0:b21e585b3aa1 1560 return;
lsm 0:b21e585b3aa1 1561 }
lsm 0:b21e585b3aa1 1562
lsm 0:b21e585b3aa1 1563 if (pd->in_len >= IN_BUF_SIZE) {
lsm 0:b21e585b3aa1 1564 FLOGE("input buffer overflow, closing..");
lsm 0:b21e585b3aa1 1565 freshen_net_close(&pd->conn);
lsm 0:b21e585b3aa1 1566 return;
lsm 0:b21e585b3aa1 1567 }
lsm 0:b21e585b3aa1 1568
lsm 0:b21e585b3aa1 1569 int n = freshen_net_recv(&pd->conn, pd->in + pd->in_len,
lsm 0:b21e585b3aa1 1570 IN_BUF_SIZE - pd->in_len);
lsm 0:b21e585b3aa1 1571 if (n < 0) {
lsm 0:b21e585b3aa1 1572 FLOGE("read error, closing..");
lsm 0:b21e585b3aa1 1573 freshen_net_close(&pd->conn);
lsm 0:b21e585b3aa1 1574 return;
lsm 0:b21e585b3aa1 1575 }
lsm 0:b21e585b3aa1 1576 pd->in_len += n;
lsm 0:b21e585b3aa1 1577
lsm 0:b21e585b3aa1 1578 // Still in the WS handshake mode. Buffer and skip HTTP reply
lsm 0:b21e585b3aa1 1579 if (!pd->conn.is_ws) {
lsm 0:b21e585b3aa1 1580 for (int i = 4; i <= pd->in_len; i++) {
lsm 0:b21e585b3aa1 1581 if (memcmp(pd->in + i - 4, "\r\n\r\n", 4) == 0) {
lsm 0:b21e585b3aa1 1582 memmove(pd->in, pd->in + i, pd->in_len - i);
lsm 0:b21e585b3aa1 1583 pd->in_len -= i;
lsm 0:b21e585b3aa1 1584 pd->conn.is_ws = true;
lsm 0:b21e585b3aa1 1585 break;
lsm 0:b21e585b3aa1 1586 }
lsm 0:b21e585b3aa1 1587 }
lsm 0:b21e585b3aa1 1588 if (!pd->conn.is_ws) return;
lsm 0:b21e585b3aa1 1589 }
lsm 0:b21e585b3aa1 1590
lsm 0:b21e585b3aa1 1591 freshen_ota_commit(ctx);
lsm 0:b21e585b3aa1 1592
lsm 0:b21e585b3aa1 1593 struct ws_msg msg;
lsm 0:b21e585b3aa1 1594 if (parse_ws_frame(pd->in, pd->in_len, &msg)) {
lsm 0:b21e585b3aa1 1595 char *data = pd->in + msg.header_len;
lsm 0:b21e585b3aa1 1596 int data_len = msg.data_len;
lsm 0:b21e585b3aa1 1597 while (data_len > 0 && data[data_len - 1] != '}') data_len--;
lsm 0:b21e585b3aa1 1598
lsm 0:b21e585b3aa1 1599 switch (msg.flags & FLAGS_MASK_OP) {
lsm 0:b21e585b3aa1 1600 case WEBSOCKET_OP_PING:
lsm 0:b21e585b3aa1 1601 FLOGI("WS PONG");
lsm 0:b21e585b3aa1 1602 ws_send(&pd->conn, WEBSOCKET_OP_PONG, data, data_len);
lsm 0:b21e585b3aa1 1603 break;
lsm 0:b21e585b3aa1 1604 case WEBSOCKET_OP_CLOSE:
lsm 0:b21e585b3aa1 1605 FLOGI("WS close received, closing connection");
lsm 0:b21e585b3aa1 1606 freshen_net_close(&pd->conn);
lsm 0:b21e585b3aa1 1607 return;
lsm 0:b21e585b3aa1 1608 }
lsm 0:b21e585b3aa1 1609
lsm 0:b21e585b3aa1 1610 FLOGI("WS in: %d [%.*s]", data_len, data_len, data);
lsm 0:b21e585b3aa1 1611 pd->out[0] = pd->out[OUT_BUF_SIZE - 1] = '\0';
lsm 0:b21e585b3aa1 1612 freshen_core_process(ctx, data, data_len, pd->out, OUT_BUF_SIZE - 1);
lsm 0:b21e585b3aa1 1613 int out_len = strlen(pd->out);
lsm 0:b21e585b3aa1 1614 FLOGI("WS out: %d [%.*s]", out_len, out_len, pd->out);
lsm 0:b21e585b3aa1 1615 if (out_len > 0) {
lsm 0:b21e585b3aa1 1616 ws_send(&pd->conn, WEBSOCKET_OP_TEXT, pd->out, out_len);
lsm 0:b21e585b3aa1 1617 }
lsm 0:b21e585b3aa1 1618
lsm 0:b21e585b3aa1 1619 int framelen = msg.header_len + msg.data_len;
lsm 0:b21e585b3aa1 1620 memmove(pd->in, pd->in + framelen, pd->in_len - framelen);
lsm 0:b21e585b3aa1 1621 pd->in_len -= framelen;
lsm 0:b21e585b3aa1 1622 }
lsm 0:b21e585b3aa1 1623 }
lsm 0:b21e585b3aa1 1624
lsm 0:b21e585b3aa1 1625 static void freshen_poll_dash(struct freshen_ctx *ctx, const char *version,
lsm 0:b21e585b3aa1 1626 const char *token) {
lsm 0:b21e585b3aa1 1627 freshen_poll(ctx, "wss://dash.freshen.cc/api/v1/rpc", version, token);
lsm 0:b21e585b3aa1 1628 }
lsm 0:b21e585b3aa1 1629 #endif
lsm 0:b21e585b3aa1 1630 #endif /* FRESHEN_DECLARATIONS_ONLY */
lsm 0:b21e585b3aa1 1631
lsm 0:b21e585b3aa1 1632 #ifdef __cplusplus
lsm 0:b21e585b3aa1 1633 }
lsm 0:b21e585b3aa1 1634 #endif /* __cplusplus */
lsm 0:b21e585b3aa1 1635
lsm 0:b21e585b3aa1 1636 #endif /* FRESHEN_H */