1196 lines
46 KiB
C
1196 lines
46 KiB
C
/**
|
|
* @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;
|
|
}
|
|
|