EC600U_esp32_iap_uart/LinkSDK/components/ota/aiot_ota_api.c

1196 lines
46 KiB
C
Raw Normal View History

2024-02-05 17:39:56 +08:00
/**
* @file aiot_ota_api.c
* @brief OTA模块接口实现文件, OTA的所有用户API
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
*/
#include "aiot_ota_api.h"
#include "core_mqtt.h"
#include "core_string.h"
#include "core_sha256.h"
#include "ota_md5.h"
#include "ota_private.h"
#include "core_log.h"
#include "core_global.h"
#include "aiot_http_api.h"
static int32_t _ota_subscribe(void *mqtt_handle, void *ota_handle);
static int32_t _ota_publish_base(void *handle, char *topic_prefix, char *product_key, char *device_name, char *suffix,
char *params);
static void _ota_mqtt_process(void *handle, const aiot_mqtt_recv_t *const packet, void *userdata);
static int32_t _ota_parse_json(aiot_sysdep_portfile_t *sysdep, void *in, uint32_t in_len, char *key_word, char **out);
static void _http_recv_handler(void *handle, const aiot_http_recv_t *recv_data, void *userdata);
static int32_t _download_parse_url(const char *url, char *host, uint32_t max_host_len, char *path,
uint32_t max_path_len);
static int32_t _download_digest_update(download_handle_t *download_handle, uint8_t *buffer, uint32_t buffer_len);
static int32_t _download_digest_verify(download_handle_t *download_handle);
static void *_download_deep_copy_base(aiot_sysdep_portfile_t *sysdep, char *in);
static void *_download_deep_copy_task_desc(aiot_sysdep_portfile_t *sysdep, void *data);
static int32_t _download_deep_free_task_desc(aiot_sysdep_portfile_t *sysdep, void *data);
static aiot_mqtt_topic_map_t g_ota_topic_map[OTA_TOPIC_NUM];
static void _ota_core_mqtt_process_handler(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event)
{
ota_handle_t *ota_handle = (ota_handle_t *)context;
if (core_event != NULL) {
switch (core_event->type) {
case CORE_MQTTEVT_DEINIT: {
ota_handle->mqtt_handle = NULL;
return;
}
break;
default: {
}
break;
}
}
}
static int32_t _ota_core_mqtt_operate_process_handler(ota_handle_t *ota_handle, core_mqtt_option_t option)
{
core_mqtt_process_data_t process_data;
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _ota_core_mqtt_process_handler;
process_data.context = ota_handle;
return core_mqtt_setopt(ota_handle->mqtt_handle, option, &process_data);
}
void *aiot_ota_init(void)
{
ota_handle_t *ota_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
sysdep = aiot_sysdep_get_portfile();
if (sysdep == NULL) {
return NULL;
}
ota_handle = sysdep->core_sysdep_malloc(sizeof(ota_handle_t), OTA_MODULE_NAME);
if (ota_handle == NULL) {
return NULL;
}
memset(ota_handle, 0, sizeof(ota_handle_t));
ota_handle->data_mutex = sysdep->core_sysdep_mutex_init();
ota_handle->sysdep = sysdep;
core_global_init(sysdep);
return ota_handle;
}
int32_t aiot_ota_deinit(void **handle)
{
ota_handle_t *ota_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
int count = 0;
int32_t res;
if (NULL == handle || NULL == *handle) {
return STATE_OTA_DEINIT_HANDLE_IS_NULL;
}
ota_handle = * (ota_handle_t **)handle;
_ota_core_mqtt_operate_process_handler(ota_handle, CORE_MQTTOPT_REMOVE_PROCESS_HANDLER);
for (count = 0; count < OTA_TOPIC_NUM; count++) {
aiot_mqtt_topic_map_t topic_map = g_ota_topic_map[count];
res = aiot_mqtt_setopt(ota_handle->mqtt_handle, AIOT_MQTTOPT_REMOVE_TOPIC_MAP, (void *)&topic_map);
if (STATE_SUCCESS != res) {
continue;
}
}
sysdep = ota_handle->sysdep;
core_global_deinit(sysdep);
sysdep->core_sysdep_mutex_deinit(&(ota_handle->data_mutex));
sysdep->core_sysdep_free(ota_handle);
*handle = NULL;
return STATE_SUCCESS;
}
int32_t aiot_ota_setopt(void *handle, aiot_ota_option_t option, void *data)
{
int32_t res = STATE_SUCCESS;
ota_handle_t *ota_handle = (ota_handle_t *)handle;
aiot_sysdep_portfile_t *sysdep = NULL;
if (NULL == ota_handle) {
return STATE_OTA_SETOPT_HANDLE_IS_NULL;
}
if (NULL == data) {
return STATE_OTA_SETOPT_DATA_IS_NULL;
}
sysdep = ota_handle->sysdep;
sysdep->core_sysdep_mutex_lock(ota_handle->data_mutex);
switch (option) {
case AIOT_OTAOPT_RECV_HANDLER: {
ota_handle->recv_handler = (aiot_ota_recv_handler_t)data;
}
break;
case AIOT_OTAOPT_USERDATA: {
ota_handle->userdata = data;
}
break;
case AIOT_OTAOPT_MQTT_HANDLE: {
ota_handle->mqtt_handle = data;
res = _ota_subscribe(data, ota_handle);
if (res >= STATE_SUCCESS) {
res = _ota_core_mqtt_operate_process_handler(ota_handle, CORE_MQTTOPT_APPEND_PROCESS_HANDLER);
}
}
break;
case AIOT_OTAOPT_MODULE: {
ota_handle->module = data;
}
break;
default: {
res = STATE_USER_INPUT_UNKNOWN_OPTION;
}
break;
}
sysdep->core_sysdep_mutex_unlock(ota_handle->data_mutex);
return res;
}
void *aiot_download_init(void)
{
download_handle_t *download_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
void *http_handle = NULL;
uint32_t default_body_max_len = OTA_DEFAULT_DOWNLOAD_BUFLEN;
uint32_t default_timeout_ms = OTA_DEFAULT_DOWNLOAD_TIMEOUT_MS;
sysdep = aiot_sysdep_get_portfile();
if (NULL == sysdep) {
return NULL;
}
download_handle = sysdep->core_sysdep_malloc(sizeof(download_handle_t), DOWNLOAD_MODULE_NAME);
if (NULL == download_handle) {
return NULL;
}
memset(download_handle, 0, sizeof(download_handle_t));
download_handle->sysdep = sysdep;
download_handle->data_mutex = sysdep->core_sysdep_mutex_init();
download_handle->recv_mutex = sysdep->core_sysdep_mutex_init();
http_handle = core_http_init();
if (NULL == http_handle) {
sysdep->core_sysdep_free(download_handle);
return NULL;
}
if ((STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_RECV_HANDLER, _http_recv_handler)) ||
(STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_USERDATA, (void *)download_handle)) ||
(STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_BODY_BUFFER_MAX_LEN, (void *)&default_body_max_len)) ||
(STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_RECV_TIMEOUT_MS, (void *)&default_timeout_ms))) {
sysdep->core_sysdep_free(download_handle);
sysdep->core_sysdep_free(http_handle);
return NULL;
}
download_handle->http_handle = http_handle;
return download_handle;
}
int32_t aiot_download_deinit(void **handle)
{
int32_t res = STATE_SUCCESS;
if (NULL == handle || NULL == *handle) {
return STATE_DOWNLOAD_DEINIT_HANDLE_IS_NULL;
}
download_handle_t *download_handle = *(download_handle_t **)(handle);
aiot_sysdep_portfile_t *sysdep = download_handle->sysdep;
core_http_deinit(&(download_handle->http_handle));
if (NULL != download_handle->task_desc) {
if (AIOT_OTA_DIGEST_SHA256 == download_handle->task_desc->digest_method) {
if (NULL != download_handle->digest_ctx) {
core_sha256_free(download_handle->digest_ctx);
sysdep->core_sysdep_free(download_handle->digest_ctx);
}
} else if (AIOT_OTA_DIGEST_MD5 == download_handle->task_desc->digest_method) {
if (NULL != download_handle->digest_ctx) {
utils_md5_free(download_handle->digest_ctx);
sysdep->core_sysdep_free(download_handle->digest_ctx);
}
}
_download_deep_free_task_desc(sysdep, download_handle->task_desc);
sysdep->core_sysdep_free(download_handle->task_desc);
}
sysdep->core_sysdep_mutex_deinit(&(download_handle->data_mutex));
sysdep->core_sysdep_mutex_deinit(&(download_handle->recv_mutex));
sysdep->core_sysdep_free(download_handle);
*handle = NULL;
return res;
}
int32_t aiot_download_setopt(void *handle, aiot_download_option_t option, void *data)
{
int32_t res = STATE_SUCCESS;
download_handle_t *download_handle = (download_handle_t *)handle;
if (download_handle == NULL) {
return STATE_DOWNLOAD_SETOPT_HANDLE_IS_NULL;
}
if (NULL == data) {
return STATE_DOWNLOAD_SETOPT_DATA_IS_NULL;
}
aiot_sysdep_portfile_t *sysdep = download_handle->sysdep;
sysdep->core_sysdep_mutex_lock(download_handle->data_mutex);
switch (option) {
case AIOT_DLOPT_NETWORK_CRED: {
res = core_http_setopt(download_handle->http_handle, CORE_HTTPOPT_NETWORK_CRED, data);
}
break;
case AIOT_DLOPT_NETWORK_PORT: {
res = core_http_setopt(download_handle->http_handle, CORE_HTTPOPT_PORT, data);
}
break;
case AIOT_DLOPT_PROFILE_IDX: {
res = core_http_setopt(download_handle->http_handle, CORE_HTTPOPT_PROFILE_IDX, data);
// uart_printf("%s : line : %d aiot_download_setopt res: %d\r\n",__func__, __LINE__, res);
}
break;
case AIOT_DLOPT_RECV_TIMEOUT_MS: {
uint32_t *timeout_ms = (uint32_t *)data;
void *http_handle = download_handle->http_handle;
res = core_http_setopt(http_handle, CORE_HTTPOPT_RECV_TIMEOUT_MS, timeout_ms);
}
break;
case AIOT_DLOPT_USERDATA: {
download_handle->userdata = data;
}
break;
case AIOT_DLOPT_TASK_DESC: {
void *new_task_desc = _download_deep_copy_task_desc(sysdep, data);
if (NULL == new_task_desc) {
res = STATE_DOWNLOAD_SETOPT_COPIED_DATA_IS_NULL;
break;
}
download_handle->task_desc = (aiot_download_task_desc_t *)new_task_desc;
if (AIOT_OTA_DIGEST_SHA256 == download_handle->task_desc->digest_method) {
core_sha256_context_t *ctx = sysdep->core_sysdep_malloc(sizeof(core_sha256_context_t), OTA_MODULE_NAME);
if (NULL == ctx) {
res = STATE_DOWNLOAD_SETOPT_MALLOC_SHA256_CTX_FAILED;
break;
}
core_sha256_init(ctx);
core_sha256_starts(ctx);
download_handle->digest_ctx = (void *) ctx;
} else if (AIOT_OTA_DIGEST_MD5 == download_handle->task_desc->digest_method) {
utils_md5_context_t *ctx = sysdep->core_sysdep_malloc(sizeof(utils_md5_context_t), OTA_MODULE_NAME);
if (NULL == ctx) {
res = STATE_DOWNLOAD_SETOPT_MALLOC_MD5_CTX_FAILED;
break;
}
utils_md5_init(ctx);
utils_md5_starts(ctx);
download_handle->digest_ctx = (void *) ctx;
}
download_handle->download_status = DOWNLOAD_STATUS_START;
}
break;
case AIOT_DLOPT_RANGE_START: {
download_handle->range_start = *(uint32_t *)data;
download_handle->range_size_fetched = 0;
}
break;
case AIOT_DLOPT_RANGE_END: {
download_handle->range_end = *(uint32_t *)data;
download_handle->range_size_fetched = 0;
}
break;
case AIOT_DLOPT_RECV_HANDLER: {
download_handle->recv_handler = (aiot_download_recv_handler_t)data;
}
break;
case AIOT_DLOPT_BODY_BUFFER_MAX_LEN: {
res = core_http_setopt(download_handle->http_handle, CORE_HTTPOPT_BODY_BUFFER_MAX_LEN, data);
}
break;
default: {
res = STATE_USER_INPUT_OUT_RANGE;
}
break;
}
sysdep->core_sysdep_mutex_unlock(download_handle->data_mutex);
return res;
}
int32_t aiot_ota_report_version(void *handle, char *version)
{
int32_t res = STATE_SUCCESS;
ota_handle_t *ota_handle = NULL;
aiot_sysdep_portfile_t *sysdep;
char *payload_string;
char *product_key;
char *device_name;
ota_handle = (ota_handle_t *)handle;
if (NULL == ota_handle) {
return STATE_OTA_REPORT_HANDLE_IS_NULL;
}
if (NULL == version) {
return STATE_OTA_REPORT_VERSION_IS_NULL;
}
core_mqtt_handle_t *mqtt_handle = ota_handle->mqtt_handle;
if (NULL == mqtt_handle) {
return STATE_OTA_REPORT_MQTT_HANDLE_IS_NULL;
}
product_key = mqtt_handle->product_key;
device_name = mqtt_handle->device_name;
sysdep = ota_handle->sysdep;
if (ota_handle->module) {
char *src[] = {"{\"version\":\"", version, "\",\"module\":\"", ota_handle->module, "\"}"};
uint8_t topic_len = sizeof(src) / sizeof(char *);
core_sprintf(sysdep, &payload_string, "%s%s%s%s%s", src, topic_len, OTA_MODULE_NAME);
} else {
char *src[] = {"{\"version\":\"", version, "\"}"};
uint8_t topic_len = sizeof(src) / sizeof(char *);
core_sprintf(sysdep, &payload_string, "%s%s%s", src, topic_len, OTA_MODULE_NAME);
}
res = _ota_publish_base(mqtt_handle, OTA_VERSION_TOPIC_PREFIX, product_key, device_name, NULL, payload_string);
sysdep->core_sysdep_free(payload_string);
return res;
}
int32_t aiot_ota_report_version_ext(void *handle, char *product_key, char *device_name, char *version)
{
int32_t res = STATE_SUCCESS;
ota_handle_t *ota_handle = NULL;
aiot_sysdep_portfile_t *sysdep;
char *payload_string;
ota_handle = (ota_handle_t *)handle;
if (NULL == ota_handle) {
return STATE_OTA_REPORT_EXT_HANELD_IS_NULL;
}
if (NULL == version) {
return STATE_OTA_REPORT_EXT_VERSION_NULL;
}
if (NULL == product_key) {
return STATE_OTA_REPORT_EXT_PRODUCT_KEY_IS_NULL;
}
if (NULL == device_name) {
return STATE_OTA_REPORT_EXT_DEVICE_NAME_IS_NULL;
}
core_mqtt_handle_t *mqtt_handle = ota_handle->mqtt_handle;
if (NULL == mqtt_handle) {
return STATE_OTA_REPORT_EXT_MQTT_HANDLE_IS_NULL;
}
sysdep = ota_handle->sysdep;
{
char *src[] = {"{\"version\":\"", version, "\"}"};
uint8_t topic_len = sizeof(src) / sizeof(char *);
core_sprintf(sysdep, &payload_string, "%s%s%s", src, topic_len, OTA_MODULE_NAME);
}
res = _ota_publish_base(mqtt_handle, OTA_VERSION_TOPIC_PREFIX, product_key, device_name, NULL, payload_string);
sysdep->core_sysdep_free(payload_string);
return res;
}
int32_t aiot_ota_query_firmware(void *handle)
{
int32_t res = STATE_SUCCESS;
ota_handle_t *ota_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
char *payload_string;
if (NULL == handle) {
return STATE_OTA_QUERY_FIRMWARE_HANDLE_IS_NULL;
}
ota_handle = (ota_handle_t *)handle;
sysdep = ota_handle->sysdep;
if (ota_handle->module) {
char *src[] = {"{\"module\":\"", ota_handle->module, "\"}"};
uint8_t topic_len = sizeof(src) / sizeof(char *);
core_sprintf(sysdep, &payload_string, "%s%s%s", src, topic_len, OTA_MODULE_NAME);
} else {
char *src[] = {"{}"};
uint8_t topic_len = sizeof(src) / sizeof(char *);
core_sprintf(sysdep, &payload_string, "%s", src, topic_len, OTA_MODULE_NAME);
}
res = _ota_publish_base(ota_handle->mqtt_handle, OTA_GET_TOPIC_PREFIX,
((core_mqtt_handle_t *)(ota_handle->mqtt_handle))->product_key,
((core_mqtt_handle_t *)(ota_handle->mqtt_handle))->device_name, OTA_GET_TOPIC_SUFFIX, payload_string);
sysdep->core_sysdep_free(payload_string);
return res;
}
int32_t aiot_download_report_progress(void *handle, int32_t percent)
{
int32_t res = STATE_SUCCESS;
download_handle_t *download_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
char out_buffer[4] = {0};
uint8_t out_len;
char *payload_string;
if (NULL == handle) {
return STATE_DOWNLOAD_REPORT_HANDLE_IS_NULL;
}
download_handle = (download_handle_t *)handle;
sysdep = download_handle->sysdep;
if (NULL == download_handle->task_desc) {
return STATE_DOWNLOAD_REPORT_TASK_DESC_IS_NULL;
}
core_int2str(percent, out_buffer, &out_len);
if (download_handle->task_desc->module) {
char *src[] = {"{\"step\":\"", out_buffer, "\",\"desc\":\"\",\"module\":\"", download_handle->task_desc->module, "\"}"};
uint8_t topic_len = sizeof(src) / sizeof(char *);
core_sprintf(sysdep, &payload_string, "%s%s%s%s%s", src, topic_len, OTA_MODULE_NAME);
} else {
char *src[] = {"{\"step\":\"", out_buffer, "\",\"desc\":\"\"}"};
uint8_t topic_len = sizeof(src) / sizeof(char *);
core_sprintf(sysdep, &payload_string, "%s%s%s", src, topic_len, OTA_MODULE_NAME);
}
res = _ota_publish_base(download_handle->task_desc->mqtt_handle, OTA_PROGRESS_TOPIC_PREFIX,
download_handle->task_desc->product_key,
download_handle->task_desc->device_name, NULL, payload_string);
sysdep->core_sysdep_free(payload_string);
return res;
}
int32_t aiot_download_recv(void *handle)
{
int32_t res = STATE_SUCCESS;
download_handle_t *download_handle = (download_handle_t *)handle;
aiot_sysdep_portfile_t *sysdep = NULL;
void *http_handle = NULL;
if (NULL == download_handle) {
return STATE_DOWNLOAD_RECV_HANDLE_IS_NULL;
}
http_handle = download_handle->http_handle;
sysdep = download_handle->sysdep;
sysdep->core_sysdep_mutex_lock(download_handle->recv_mutex);
switch (download_handle->download_status) {
case DOWNLOAD_STATUS_RENEWAL: {
/* 下载中断, 发起断点续传 */
res = aiot_download_send_request(download_handle);
if (res == STATE_SUCCESS) {
res = STATE_DOWNLOAD_RENEWAL_REQUEST_SENT;
}
}
break;
case DOWNLOAD_STATUS_FETCH: {
/* 去网络收取报文, 并将各种状态值反馈给用户 */
res = core_http_recv(http_handle);
/* 全部固件下载完成 */
if (download_handle->size_fetched == download_handle->task_desc->size_total) {
res = STATE_DOWNLOAD_FINISHED;
break;
}
/* 用户用多个range下载, 可能有重合, 导致最终累计的下载长度超出了固件的实际长度 */
if (download_handle->size_fetched > download_handle->task_desc->size_total) {
res = STATE_DOWNLOAD_FETCH_TOO_MANY;
break;
}
/* 没有下载完整个固件, 但是下载完成了用户指定的range */
if (download_handle->range_size_fetched == download_handle->content_len) {
res = STATE_DOWNLOAD_RANGE_FINISHED;
break;
}
if (res <= 0) {
/* 在没有下载完成, 同时又碰到core_http_recv的返回值<=0的情况, 需要做断点续传 */
uint8_t res_string_len = 0;
char res_string[OTA_MAX_DIGIT_NUM_OF_UINT32] = {0};
core_int2str(res, res_string, &res_string_len);
core_log1(download_handle->sysdep, STATE_DOWNLOAD_RECV_ERROR, "recv got %s, renewal\r\n",
&res_string);
download_handle->download_status = DOWNLOAD_STATUS_RENEWAL;
} else {
/* 在下载未完成(或者未开始), 同时core_http_recv的返回值>0的情况, 需要判断是否出现了http返回值403错误, 以及判断包头格式是否异常 */
if (OTA_RESPONSE_OK != download_handle->http_rsp_status_code
&& OTA_RESPONSE_PARTIAL != download_handle->http_rsp_status_code) {
/* HTTP回复报文的code应该是200或者206, 否则这个下载链接不可用 */
res = STATE_DOWNLOAD_HTTPRSP_CODE_ERROR;
} else if (0 == download_handle->content_len) {
/* HTTP回复报文的header里面应该有Content-Length, 否则这个下载链接为trunked编码, 不可用 */
res = STATE_DOWNLOAD_HTTPRSP_HEADER_ERROR;
}
/* 如果没有上述code错误或者content_length字段错误, 则返回正数值, 表示下载到的字节数 */
}
}
break;
default:
break;
}
sysdep->core_sysdep_mutex_unlock(download_handle->recv_mutex);
return res;
}
/* 对aiot_download_task_desc_t结构体里面的指针所指向的内容进行深度释放 */
int32_t _download_deep_free_task_desc(aiot_sysdep_portfile_t *sysdep, void *data)
{
int32_t res = STATE_SUCCESS;
aiot_download_task_desc_t *task_desc = (aiot_download_task_desc_t *)data;
if (NULL == task_desc) {
return res;
}
if (NULL != task_desc->product_key) {
sysdep->core_sysdep_free(task_desc->product_key);
}
if (NULL != task_desc->device_name) {
sysdep->core_sysdep_free(task_desc->device_name);
}
if (NULL != task_desc->url) {
sysdep->core_sysdep_free(task_desc->url);
}
if (NULL != task_desc->expect_digest) {
sysdep->core_sysdep_free(task_desc->expect_digest);
}
if (NULL != task_desc->version) {
sysdep->core_sysdep_free(task_desc->version);
}
if (NULL != task_desc->module) {
sysdep->core_sysdep_free(task_desc->module);
}
return res;
}
/* 从下行报文的topic中, 解析出product_key, device_name, ota的类型 */
int32_t _ota_prase_topic(aiot_sysdep_portfile_t *sysdep, char *topic, uint8_t topic_len,
ota_type_t *type, char **product_key, char **device_name)
{
char *_product_key, *_device_name, *tmp;
char *_product_key_local, *_device_name_local;
uint8_t _product_key_len, _device_name_len;
const char *SLASH = "/";
uint8_t slash_len = strlen(SLASH);
if (memcmp(topic, OTA_FOTA_TOPIC_PREFIX, strlen(OTA_FOTA_TOPIC_PREFIX)) == 0) {
/* 判断是fota的/ota/device/upgrade的topic, 并且从中解出来product_key, device_name */
_product_key = topic + strlen(OTA_FOTA_TOPIC_PREFIX) + slash_len;
tmp = strstr((const char *)(_product_key), SLASH);
_product_key_len = tmp - _product_key;
_device_name = tmp + slash_len;
_device_name_len = topic_len - (_device_name - topic);
*type = OTA_TYPE_FOTA;
} else if ((topic_len > strlen(OTA_GET_REPLY_TOPIC_SUFFIX))
&& (0 == memcmp(topic + topic_len - strlen(OTA_GET_REPLY_TOPIC_SUFFIX),
OTA_GET_REPLY_TOPIC_SUFFIX, strlen(OTA_GET_REPLY_TOPIC_SUFFIX)))) {
/* 判断是fota的firmware/get的topic, 并且从中解出来product_key, device_name */
_product_key = topic + strlen(OTA_GET_TOPIC_PREFIX) + slash_len;
tmp = strstr((const char *)(_product_key), SLASH);
_product_key_len = tmp - _product_key;
_device_name = tmp + slash_len;
_device_name_len = strstr((const char *)topic, OTA_GET_REPLY_TOPIC_SUFFIX) - _device_name - slash_len;
*type = OTA_TYPE_FOTA;
} else {
/* 判断是cota的topic, 同时还区分是config/push还是config/get, 从中解出来product_key, device_name */
char *postfix = NULL;
_product_key = topic + strlen(OTA_COTA_TOPIC_PREFIX);
tmp = strstr((const char *)(_product_key), SLASH);
_product_key_len = tmp - _product_key;
_device_name = tmp + slash_len;
tmp = strstr((const char *)(_device_name), SLASH);
_device_name_len = tmp - _device_name;
postfix = _device_name + _device_name_len;
if (0 != memcmp(postfix, OTA_COTA_PUSH_TOPIC_POSTFIX, strlen(OTA_COTA_PUSH_TOPIC_POSTFIX))) {
*type = OTA_TYPE_CONFIG_GET;
} else {
*type = OTA_TYPE_CONFIG_PUSH;
};
}
if ((NULL == (_product_key_local = sysdep->core_sysdep_malloc(_product_key_len + 1, OTA_MODULE_NAME)))
|| (NULL == (_device_name_local = sysdep->core_sysdep_malloc(_device_name_len + 1, OTA_MODULE_NAME)))) {
if (NULL != _product_key_local) {
sysdep->core_sysdep_free(_product_key_local);
};
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(_product_key_local, 0, _product_key_len + 1);
memcpy(_product_key_local, _product_key, _product_key_len);
*product_key = _product_key_local;
memset(_device_name_local, 0, _device_name_len + 1);
memcpy(_device_name_local, _device_name, _device_name_len);
*device_name = _device_name_local;
return STATE_SUCCESS;
}
/* OTA topic的mqtt回调函数. 主要作用是解析OTA下行的MQTT报文, 从中解析出url/digest等有关的信息 */
void _ota_mqtt_process(void *handle, const aiot_mqtt_recv_t *const packet, void *userdata)
{
int32_t res = STATE_SUCCESS;
ota_handle_t *ota_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
char *size_string = NULL, *digest_method_string = NULL;
char *product_key = NULL, *device_name = NULL;
char *data = NULL, *key = NULL;
uint32_t data_len = 0, size = 0;
ota_type_t type;
aiot_download_task_desc_t task_desc = {0};
if (AIOT_MQTTRECV_PUB != packet->type) {
return;
}
ota_handle = (ota_handle_t *)userdata;
if (NULL == userdata) {
return;
}
sysdep = ota_handle->sysdep;
res = _ota_prase_topic(sysdep, packet->data.pub.topic, packet->data.pub.topic_len,
&type, &product_key, &device_name);
if (res != STATE_SUCCESS) {
goto exit;
}
task_desc.product_key = product_key;
task_desc.device_name = device_name;
task_desc.mqtt_handle = ota_handle->mqtt_handle;
/* 如果是config/push这种topic, 下载有关的信息存放在json报文的params字段里面
* topic, json报文的data字段里面
*/
key = "data";
if (OTA_TYPE_CONFIG_PUSH == type) {
key = "params";
}
res = core_json_value((const char *)(packet->data.pub.payload), packet->data.pub.payload_len,
key, strlen(key), &data, &data_len);
if (res != STATE_SUCCESS) {
goto exit;
}
if (OTA_TYPE_FOTA == type) {
/* 对于FOTA来说, 下载文件大小的关键字是size, 版本有关的关键字是version */
if ((STATE_SUCCESS != _ota_parse_json(sysdep, data, data_len, "size", &size_string))
|| (STATE_SUCCESS != _ota_parse_json(sysdep, data, data_len, "version", &(task_desc.version)))) {
goto exit;
}
} else {
/* 对于COTA来说, 下载文件大小的关键字是configSize, 版本有关的关键字是configId */
if ((STATE_SUCCESS != _ota_parse_json(sysdep, data, data_len, "configSize", &size_string))
|| (STATE_SUCCESS != _ota_parse_json(sysdep, data, data_len, "configId", &(task_desc.version)))) {
goto exit;
}
}
core_str2uint(size_string, strlen(size_string), &size);
task_desc.size_total = size;
if ((STATE_SUCCESS != _ota_parse_json(sysdep, data, data_len, "url", &(task_desc.url)))
|| (STATE_SUCCESS != _ota_parse_json(sysdep, data, data_len, "sign", &(task_desc.expect_digest)))
|| (STATE_SUCCESS != _ota_parse_json(sysdep, data, data_len, "signMethod", &(digest_method_string)))) {
goto exit;
}
if (strcmp(digest_method_string, "SHA256") == 0 || strcmp(digest_method_string, "Sha256") == 0) {
task_desc.digest_method = AIOT_OTA_DIGEST_SHA256;
} else if (strcmp(digest_method_string, "Md5") == 0) {
task_desc.digest_method = AIOT_OTA_DIGEST_MD5;
} else {
res = STATE_OTA_UNKNOWN_DIGEST_METHOD;
goto exit;
}
if ((task_desc.digest_method == AIOT_OTA_DIGEST_MD5 && strlen(task_desc.expect_digest) != OTA_MD5_LEN) ||
(task_desc.digest_method == AIOT_OTA_DIGEST_SHA256 && strlen(task_desc.expect_digest) != OTA_SHA256_LEN)) {
goto exit;
}
/* module字段, 并非必选(用户可能没有在云端设置过, 因此如果没有解析出来, 也不能算解析失败 */
_ota_parse_json(sysdep, data, data_len, "module", &(task_desc.module));
aiot_ota_recv_t msg = {
/* 对于用户, 需要屏蔽config/push和config/get的区别, 都归并为AIOT_OTARECV_COTA */
.type = (OTA_TYPE_FOTA == type) ? AIOT_OTARECV_FOTA : AIOT_OTARECV_COTA,
.task_desc = &task_desc
};
if (ota_handle->recv_handler) {
ota_handle->recv_handler(ota_handle, &msg, ota_handle->userdata);
}
exit:
if (NULL != size_string) {
sysdep->core_sysdep_free(size_string);
}
if (NULL != digest_method_string) {
sysdep->core_sysdep_free(digest_method_string);
}
_download_deep_free_task_desc(sysdep, (void *)(&task_desc));
}
/* 解析URL, 从中解出来host, path */
static int32_t _download_parse_url(const char *url, char *host, uint32_t max_host_len, char *path,
uint32_t max_path_len)
{
char *host_ptr = (char *) strstr(url, "://");
uint32_t host_len = 0;
uint32_t path_len;
char *path_ptr;
char *fragment_ptr;
if (host_ptr == NULL) {
return STATE_OTA_PARSE_URL_HOST_IS_NULL;
}
host_ptr += 3;
path_ptr = strchr(host_ptr, '/');
if (NULL == path_ptr) {
return STATE_OTA_PARSE_URL_PATH_IS_NULL;
}
if (host_len == 0) {
host_len = path_ptr - host_ptr;
}
if (host_len >= max_host_len) {
return STATE_OTA_HOST_STRING_OVERFLOW;
}
memcpy(host, host_ptr, host_len);
host[host_len] = '\0';
fragment_ptr = strchr(host_ptr, '#');
if (fragment_ptr != NULL) {
path_len = fragment_ptr - path_ptr;
} else {
path_len = strlen(path_ptr);
}
if (path_len >= max_path_len) {
return STATE_OTA_PATH_STRING_OVERFLOW;
}
memcpy(path, path_ptr, path_len);
path[path_len] = '\0';
return STATE_SUCCESS;
}
/* 往固件服务器发送下载所需的GET请求 */
int32_t aiot_download_send_request(void *handle)
{
int32_t res = STATE_SUCCESS;
char host[OTA_HTTPCLIENT_MAX_URL_LEN] = { 0 };
char path[OTA_HTTPCLIENT_MAX_URL_LEN] = { 0 };
char *header_string = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
download_handle_t *download_handle = (download_handle_t *)handle;
if (NULL == download_handle) {
return STATE_DOWNLOAD_REQUEST_HANDLE_IS_NULL;
}
if (NULL == download_handle->task_desc) {
return STATE_DOWNLOAD_REQUEST_TASK_DESC_IS_NULL;
}
if (NULL == download_handle->task_desc->url) {
return STATE_DOWNLOAD_REQUEST_URL_IS_NULL;
}
sysdep = download_handle->sysdep;
{
uint32_t range_start = download_handle->range_start;
uint32_t range_end = download_handle->range_end;
uint8_t range_start_string_len = 0;
uint8_t range_end_string_len = 0;
char range_start_string[OTA_MAX_DIGIT_NUM_OF_UINT32] = {0};
char range_end_string[OTA_MAX_DIGIT_NUM_OF_UINT32] = {0};
/* 对于按照range下载的情况, 如果中间出现了断点续传的情况, 则需要从range_start后面的续传位置开始 */
uint32_t renewal_start = range_start + download_handle->range_size_fetched;
download_handle->range_start = renewal_start;
core_int2str(renewal_start, range_start_string, &range_start_string_len);
/* 对于按照range下载的情况, 即range_end不为0的情况, 需要将其翻译成字符串 */
if (0 != range_end) {
core_int2str(range_end, range_end_string, &range_end_string_len);
}
{
char *prefix = "Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8\r\nRange: bytes=";
char *src[] = { prefix, range_start_string, "-", range_end_string};
uint32_t len = sizeof(src) / sizeof(char *);
res = core_sprintf(sysdep, &header_string, "%s%s%s%s\r\n", src, len, OTA_MODULE_NAME);
if (res != STATE_SUCCESS) {
if (header_string) {
sysdep->core_sysdep_free(header_string);
}
return res;
}
}
}
res = _download_parse_url(download_handle->task_desc->url, host, OTA_HTTPCLIENT_MAX_URL_LEN, path,
OTA_HTTPCLIENT_MAX_URL_LEN);
if (res != STATE_SUCCESS) {
if (header_string) {
sysdep->core_sysdep_free(header_string);
}
return res;
}
res = core_http_setopt(download_handle->http_handle, CORE_HTTPOPT_HOST, host);
if (res != STATE_SUCCESS) {
if (header_string) {
sysdep->core_sysdep_free(header_string);
}
return res;
}
res = core_http_connect(download_handle->http_handle);
if (res != STATE_SUCCESS) {
if (header_string) {
sysdep->core_sysdep_free(header_string);
}
return res;
}
core_http_request_t request = {
.method = "GET",
.path = path,
.header = header_string,
.content = NULL,
.content_len = 0
};
res = core_http_send(download_handle->http_handle, &request);
if (header_string) {
sysdep->core_sysdep_free(header_string);
}
/* core_http_send 返回的是发送的body的长度; 错误返回负数 */
if (res < STATE_SUCCESS) {
download_handle->download_status = DOWNLOAD_STATUS_START;
res = STATE_DOWNLOAD_SEND_REQUEST_FAILED;
} else {
download_handle->download_status = DOWNLOAD_STATUS_FETCH;
aiot_download_report_progress(download_handle, download_handle->percent);
res = STATE_SUCCESS;
}
return res;
}
/* 根据下载到的固件的内容, 计算其digest值 */
static int32_t _download_digest_update(download_handle_t *download_handle, uint8_t *buffer, uint32_t buffer_len)
{
int32_t res = STATE_SUCCESS;
if (AIOT_OTA_DIGEST_SHA256 == download_handle->task_desc->digest_method) {
core_sha256_update(download_handle->digest_ctx, buffer, buffer_len);
} else if (AIOT_OTA_DIGEST_MD5 == download_handle->task_desc->digest_method) {
res = utils_md5_update(download_handle->digest_ctx, buffer, buffer_len);
}
return res;
}
/* 对计算出来的digest值, 与云端下发的digest值进行比较 */
static int32_t _download_digest_verify(download_handle_t *download_handle)
{
uint8_t output[32] = {0};
uint8_t expected_digest[32] = {0};
if (AIOT_OTA_DIGEST_SHA256 == download_handle->task_desc->digest_method) {
core_str2hex(download_handle->task_desc->expect_digest, OTA_SHA256_LEN, expected_digest);
core_sha256_finish(download_handle->digest_ctx, output);
if (memcmp(output, expected_digest, 32) == 0) {
return STATE_SUCCESS;
}
} else if (AIOT_OTA_DIGEST_MD5 == download_handle->task_desc->digest_method) {
core_str2hex(download_handle->task_desc->expect_digest, OTA_MD5_LEN, expected_digest);
utils_md5_finish(download_handle->digest_ctx, output);
if (memcmp(output, expected_digest, 16) == 0) {
return STATE_SUCCESS;
}
}
return STATE_OTA_DIGEST_MISMATCH;
}
/* 对于收到的http报文进行处理的回调函数, 内部处理完后再调用用户的回调函数 */
void _http_recv_handler(void *handle, const aiot_http_recv_t *packet, void *userdata)
{
download_handle_t *download_handle = (download_handle_t *)userdata;
if (NULL == download_handle || NULL == packet) {
return;
}
switch (packet->type) {
case AIOT_HTTPRECV_STATUS_CODE : {
download_handle->http_rsp_status_code = packet->data.status_code.code;
}
break;
case AIOT_HTTPRECV_HEADER: {
if ((strlen(packet->data.header.key) == strlen("Content-Length")) &&
(memcmp(packet->data.header.key, "Content-Length", strlen(packet->data.header.key)) == 0)) {
uint32_t size = 0;
/* 在用户指定的range并非全部固件的情况下, content_len < size_total, 所以不能简单替换 */
core_str2uint(packet->data.header.value, (uint8_t)strlen(packet->data.header.value), &size);
download_handle->content_len = size;
/* 该字段表示在这个range内总共下载了多少字节, 初始化为0 */
download_handle->range_size_fetched = 0;
}
}
break;
case AIOT_HTTPRECV_BODY: {
int32_t percent = 0;
if (OTA_RESPONSE_OK != download_handle->http_rsp_status_code
/* HTTP回复报文的code应该是200或者206, 否则这个下载链接不可用 */
&& OTA_RESPONSE_PARTIAL != download_handle->http_rsp_status_code) {
percent = AIOT_OTAERR_FETCH_FAILED;
core_log(download_handle->sysdep, STATE_DOWNLOAD_HTTPRSP_CODE_ERROR, "wrong http respond code\r\n");
} else if (0 == download_handle->content_len) {
/* HTTP回复报文的header里面应该有Content-Length, 否则这个下载链接为trunked编码, 不可用 */
percent = AIOT_OTAERR_FETCH_FAILED;
core_log(download_handle->sysdep, STATE_DOWNLOAD_HTTPRSP_HEADER_ERROR, "wrong http respond header\r\n");
} else {
/* 正常的固件的报文 */
/* 在按照多个range分片下载的情况下, 判断用户下载到的固件的累计大小是否超过了整体的值 */
if (download_handle->size_fetched > download_handle->task_desc->size_total) {
core_log(download_handle->sysdep, STATE_DOWNLOAD_FETCH_TOO_MANY, "downloaded exceeds expected\r\n");
break;
}
/* 该字段表示累计下载了多少字节, 不区分range */
download_handle->size_fetched += packet->data.body.len;
/* 该字段表示在这个range内总共下载了多少字节 */
download_handle->range_size_fetched += packet->data.body.len;
/* 当size_fetched*100超过uint32_t能表达的范围时, 比如239395702, 会导致percent计算错误, 因此需要一个更大的临时变量 */
uint64_t tmp_size_fetched = 0;
tmp_size_fetched = download_handle->size_fetched;
percent = (100 * tmp_size_fetched) / download_handle->task_desc->size_total;
/* 计算digest, 如果下载完成, 还要看看是否与云端计算出来的一致 */
_download_digest_update(download_handle, packet->data.body.buffer, packet->data.body.len);
if (download_handle->size_fetched == download_handle->task_desc->size_total) {
int32_t ret = _download_digest_verify(download_handle);
if (ret != STATE_SUCCESS) {
percent = AIOT_OTAERR_CHECKSUM_MISMATCH;
core_log(download_handle->sysdep, ret, "digest mismatch\r\n");
aiot_download_report_progress(download_handle, AIOT_OTAERR_CHECKSUM_MISMATCH);
} else {
core_log(download_handle->sysdep, STATE_OTA_DIGEST_MATCH, "digest matched\r\n");
}
}
download_handle->percent = percent;
/* 调用用户的回调函数, 将报文传给用户 */
if (NULL != download_handle->recv_handler) {
aiot_download_recv_t recv_data = {
.type = AIOT_DLRECV_HTTPBODY,
.data = {
.buffer = packet->data.body.buffer,
.len = packet->data.body.len,
.percent = percent
}
};
download_handle->recv_handler(download_handle, &recv_data, download_handle->userdata);
}
}
}
break;
default:
break;
}
}
/* 提供深度拷贝的底层函数 */
static void *_download_deep_copy_base(aiot_sysdep_portfile_t *sysdep, char *in)
{
uint8_t len = 0;
void *tmp = NULL;
if (NULL == in) {
return NULL;
}
len = strlen(in) + 1;
tmp = (aiot_download_task_desc_t *)sysdep->core_sysdep_malloc(len,
DOWNLOAD_MODULE_NAME);
if (NULL == tmp) {
return NULL;
}
memset(tmp, 0, len);
memcpy(tmp, in, strlen(in));
return tmp;
}
/* 对aiot_download_task_desc_t结构体实现深度拷贝 */
static void *_download_deep_copy_task_desc(aiot_sysdep_portfile_t *sysdep, void *data)
{
aiot_download_task_desc_t *src_task_desc = (aiot_download_task_desc_t *)data;
aiot_download_task_desc_t *dst_task_desc = NULL;
dst_task_desc = (aiot_download_task_desc_t *)sysdep->core_sysdep_malloc(sizeof(aiot_download_task_desc_t),
DOWNLOAD_MODULE_NAME);
if (NULL == dst_task_desc) {
return NULL;
}
memset(dst_task_desc, 0, sizeof(aiot_download_task_desc_t));
dst_task_desc->size_total = src_task_desc->size_total;
dst_task_desc->digest_method = src_task_desc->digest_method;
dst_task_desc->mqtt_handle = src_task_desc->mqtt_handle;
/* 对于module字段, 只有判断云端有下发过module的报文, 才能认为出参是要有module字段的 */
if (NULL != src_task_desc->module) {
if (NULL == (dst_task_desc->module = _download_deep_copy_base(sysdep, src_task_desc->module))) {
return NULL;
}
}
if ((NULL == (dst_task_desc->product_key = _download_deep_copy_base(sysdep, src_task_desc->product_key)))
|| (NULL == (dst_task_desc->device_name = _download_deep_copy_base(sysdep, src_task_desc->device_name)))
|| (NULL == (dst_task_desc->url = _download_deep_copy_base(sysdep, src_task_desc->url)))
|| (NULL == (dst_task_desc->version = _download_deep_copy_base(sysdep, src_task_desc->version)))
|| (NULL == (dst_task_desc->expect_digest = _download_deep_copy_base(sysdep, src_task_desc->expect_digest)))) {
_download_deep_free_task_desc(sysdep, dst_task_desc);
sysdep->core_sysdep_free(dst_task_desc);
return NULL;
}
return dst_task_desc;
}
/* 提供上报版本号与上报进度的底层函数 */
static int32_t _ota_publish_base(void *handle, char *topic_prefix, char *product_key,
char *device_name, char *suffix, char *params)
{
int32_t res = STATE_SUCCESS;
core_mqtt_handle_t *mqtt_handle = (core_mqtt_handle_t *)handle;
char *topic_string = NULL;
char *payload_string = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
if (NULL == product_key || NULL == device_name || NULL == mqtt_handle) {
return STATE_USER_INPUT_NULL_POINTER;
}
sysdep = mqtt_handle->sysdep;
/* 拼装topic */
if (NULL == suffix) {
char *src[] = { topic_prefix, product_key, device_name };
uint8_t topic_len = sizeof(src) / sizeof(char *);
res = core_sprintf(sysdep, &topic_string, "%s/%s/%s", src, topic_len, OTA_MODULE_NAME);
if (res != STATE_SUCCESS) {
goto exit;
}
} else {
char *src[] = { topic_prefix, product_key, device_name, suffix };
uint8_t topic_len = sizeof(src) / sizeof(char *);
res = core_sprintf(sysdep, &topic_string, "%s/%s/%s/%s", src, topic_len, OTA_MODULE_NAME);
if (res != STATE_SUCCESS) {
goto exit;
}
}
/* 拼装payload, 几种报文都是基于alink协议, 所以需要alink id */
{
int32_t alink_id;
uint8_t alink_id_string_len;
char alink_id_string[OTA_MAX_DIGIT_NUM_OF_UINT32] = {0};
res = core_global_alink_id_next(sysdep, &alink_id);
if (res != STATE_SUCCESS) {
goto exit;
}
core_int2str(alink_id, alink_id_string, &alink_id_string_len);
{
char *src[] = {
"{\"id\":", alink_id_string, ", \"params\":", params, "}"
};
uint8_t topic_len = sizeof(src) / sizeof(char *);
res = core_sprintf(sysdep, &payload_string, "%s%s%s%s%s", src, topic_len, OTA_MODULE_NAME);
if (res != STATE_SUCCESS) {
goto exit;
}
}
}
res = aiot_mqtt_pub(mqtt_handle, topic_string, (uint8_t *)payload_string, strlen(payload_string), 0);
exit:
if (NULL != topic_string) {
sysdep->core_sysdep_free(topic_string);
}
if (NULL != payload_string) {
sysdep->core_sysdep_free(payload_string);
}
if (res != STATE_SUCCESS) {
core_log(sysdep, STATE_OTA_REPORT_FAILED, topic_prefix);
}
return res;
}
/* 将ota的回调函数挂载到mqtt模块里面 */
static int32_t _ota_subscribe(void *mqtt_handle, void *ota_handle)
{
int32_t res = STATE_SUCCESS;
char *topic[OTA_TOPIC_NUM] = { OTA_FOTA_TOPIC, OTA_COTA_PUSH_TOPIC, OTA_COTA_GET_REPLY_TOPIC, OTA_OTA_GET_REPLY_TOPIC };
uint8_t count = 0;
memset(&g_ota_topic_map, 0, sizeof(g_ota_topic_map));
for (count = 0; count < OTA_TOPIC_NUM; count++) {
char *topic_string = topic[count];
aiot_mqtt_topic_map_t topic_map = {topic_string, _ota_mqtt_process, (void *)ota_handle};
g_ota_topic_map[count] = topic_map;
res = aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_APPEND_TOPIC_MAP, (void *)&topic_map);
if (STATE_SUCCESS != res) {
break;
}
}
return res;
}
/* 解析json报文, 并且将解析出来的内容, 填充到malloc出来的一片内存中 */
static int32_t _ota_parse_json(aiot_sysdep_portfile_t *sysdep, void *input, uint32_t input_len, char *key_word,
char **out)
{
int32_t res = STATE_SUCCESS;
char *value = NULL, *buffer = NULL;
uint32_t value_len = 0, buffer_len = 0;
res = core_json_value((const char *)input, input_len, key_word, strlen(key_word), &value, &value_len);
if (res != STATE_SUCCESS) {
return STATE_OTA_PARSE_JSON_ERROR;
}
buffer_len = value_len + 1;
buffer = sysdep->core_sysdep_malloc(buffer_len, OTA_MODULE_NAME);
if (NULL == buffer) {
return STATE_OTA_PARSE_JSON_MALLOC_FAILED;
}
memset(buffer, 0, buffer_len);
memcpy(buffer, value, value_len);
*out = buffer;
return res;
}