| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 | /*  *  (c) Philippe G. 20201, philippe_44@outlook.com *	see other copyrights below * *  This software is released under the MIT License. *  https://opensource.org/licenses/MIT * */#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <string.h>#include <ctype.h>#include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "esp_task.h"#include "esp_tls.h"#include "esp_http_client.h"#include "esp_heap_caps.h"#include "esp_log.h"#include "tools.h"const static char TAG[] = "tools";/**************************************************************************************** * UTF-8 tools */// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.// Copyright (c) 2017 ZephRay <zephray@outlook.com>//// utf8to1252 - almost equivalent to iconv -f utf-8 -t windows-1252, but better#define UTF8_ACCEPT 0#define UTF8_REJECT 1static const uint8_t utf8d[] = {	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f	7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf	8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df	0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef	0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff	0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2	1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4	1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6	1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8};static uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) {	uint32_t type = utf8d[byte];	*codep = (*state != UTF8_ACCEPT) ?		(byte & 0x3fu) | (*codep << 6) :		(0xff >> type) & (byte);	*state = utf8d[256 + *state*16 + type];	return *state;}static uint8_t UNICODEtoCP1252(uint16_t chr) {	if (chr <= 0xff)		return (chr&0xff);	else {		ESP_LOGI(TAG, "some multi-byte %hx", chr);		switch(chr) {			case 0x20ac: return 0x80; break;			case 0x201a: return 0x82; break;			case 0x0192: return 0x83; break;			case 0x201e: return 0x84; break;			case 0x2026: return 0x85; break;			case 0x2020: return 0x86; break;			case 0x2021: return 0x87; break;			case 0x02c6: return 0x88; break;			case 0x2030: return 0x89; break;			case 0x0160: return 0x8a; break;			case 0x2039: return 0x8b; break;			case 0x0152: return 0x8c; break;			case 0x017d: return 0x8e; break;			case 0x2018: return 0x91; break;			case 0x2019: return 0x92; break;			case 0x201c: return 0x93; break;			case 0x201d: return 0x94; break;			case 0x2022: return 0x95; break;			case 0x2013: return 0x96; break;			case 0x2014: return 0x97; break;			case 0x02dc: return 0x98; break;			case 0x2122: return 0x99; break;			case 0x0161: return 0x9a; break;			case 0x203a: return 0x9b; break;			case 0x0153: return 0x9c; break;			case 0x017e: return 0x9e; break;			case 0x0178: return 0x9f; break;			default: return 0x00; break;		}	}}void utf8_decode(char *src) {	uint32_t codep = 0, state = UTF8_ACCEPT;	char *dst = src;				while (src && *src) {		if (!decode(&state, &codep, *src++)) *dst++ = UNICODEtoCP1252(codep);	}		*dst = '\0';}/**************************************************************************************** * URL tools */static inline char from_hex(char ch) {  return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;}void url_decode(char *url) {	char *p, *src = strdup(url);	for (p = src; *src; url++) {		*url = *src++;		if (*url == '%') {			*url = from_hex(*src++) << 4;			*url |= from_hex(*src++);		} else if (*url == '+') {			*url = ' ';		}	}	*url = '\0';	free(p);}/**************************************************************************************** * Memory tools */void * malloc_init_external(size_t sz){	void * ptr=NULL;	ptr = heap_caps_malloc(sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);	if(ptr==NULL){		ESP_LOGE(TAG,"malloc_init_external:  unable to allocate %d bytes of PSRAM!",sz);	}	else {		memset(ptr,0x00,sz);	}	return ptr;}void * clone_obj_psram(void * source, size_t source_sz){	void * ptr=NULL;	ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);	if(ptr==NULL){		ESP_LOGE(TAG,"clone_obj_psram:  unable to allocate %d bytes of PSRAM!",source_sz);	}	else {		memcpy(ptr,source,source_sz);	}	return ptr;}char * strdup_psram(const char * source){	void * ptr=NULL;	size_t source_sz = strlen(source)+1;	ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);	if(ptr==NULL){		ESP_LOGE(TAG,"strdup_psram:  unable to allocate %d bytes of PSRAM! Cannot clone string %s",source_sz,source);	}	else {		memset(ptr,0x00,source_sz);		strcpy(ptr,source);	}	return ptr;}/**************************************************************************************** * URL download  */ typedef struct {	void *user_context;	http_download_cb_t callback;		size_t max, bytes;	bool abort;	uint8_t *data;	esp_http_client_handle_t client;} http_context_t;static void http_downloader(void *arg);static esp_err_t http_event_handler(esp_http_client_event_t *evt); void http_download(char *url, size_t max, http_download_cb_t callback, void *context) {	http_context_t *http_context = (http_context_t*) heap_caps_calloc(sizeof(http_context_t), 1, MALLOC_CAP_SPIRAM);		esp_http_client_config_t config = {		.url = url,		.event_handler = http_event_handler,		.user_data = http_context,	};				http_context->callback = callback;	http_context->user_context = context;	http_context->max = max;	http_context->client = esp_http_client_init(&config);		xTaskCreate(http_downloader, "downloader", 4*1024, http_context, ESP_TASK_PRIO_MIN + 1, NULL);}static void http_downloader(void *arg) {	http_context_t *http_context = (http_context_t*) arg;	esp_http_client_perform(http_context->client);	esp_http_client_cleanup(http_context->client);		free(http_context);	vTaskDelete(NULL);}static esp_err_t http_event_handler(esp_http_client_event_t *evt) {	http_context_t *http_context = (http_context_t*) evt->user_data;			if (http_context->abort) return ESP_FAIL;	switch(evt->event_id) {	case HTTP_EVENT_ERROR:		http_context->callback(NULL, 0, http_context->user_context);		http_context->abort = true;		break;	case HTTP_EVENT_ON_HEADER:		if (!strcasecmp(evt->header_key, "Content-Length")) {			size_t len = atoi(evt->header_value);			if (!len || len > http_context->max) {				ESP_LOGI(TAG, "content-length null or too large %zu / %zu", len, http_context->max);							http_context->abort = true;			}			}			break;	case HTTP_EVENT_ON_DATA: {		size_t len = esp_http_client_get_content_length(evt->client);		if (!http_context->data) {			if ((http_context->data = (uint8_t*) malloc(len)) == NULL) {				http_context->abort = true;				ESP_LOGE(TAG, "gailed to allocate memory for output buffer %zu", len);				return ESP_FAIL;			}			}			memcpy(http_context->data + http_context->bytes, evt->data, evt->data_len);		http_context->bytes += evt->data_len;		break;	}		case HTTP_EVENT_ON_FINISH:		http_context->callback(http_context->data, http_context->bytes, http_context->user_context);					break;	case HTTP_EVENT_DISCONNECTED: {		int mbedtls_err = 0;		esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL);		if (err != ESP_OK) {			ESP_LOGE(TAG, "HTTP download disconnect %d", err);							if (http_context->data) free(http_context->data);			http_context->callback(NULL, 0, http_context->user_context);					return ESP_FAIL;		}		break;	default:		break;	}		}		return ESP_OK;} 
 |