1 /*
2  * Copyright (C) 2015-2018 Alibaba Group Holding Limited
3  */
4 
5 #ifndef __OTA_MQTT_C_H__
6 #define __OTA_MQTT_C_H__
7 
8 #if (OTA_SIGNAL_CHANNEL) == 1
9 
10 #include "linkkit/mqtt_api.h"
11 #include "ota_api.h"
12 #include "iotx_ota_internal.h"
13 
14 /* OSC, OTA signal channel */
15 
16 /* Specify the maximum characters of version */
17 #define OTA_MQTT_TOPIC_LEN (128)
18 
19 typedef struct {
20     void *mqtt;
21     const char *product_key;
22     const char *device_name;
23     char topic_upgrade[OTA_MQTT_TOPIC_LEN];
24     char topic_request[OTA_MQTT_TOPIC_LEN];
25     char topic_config_get[OTA_MQTT_TOPIC_LEN];
26     char topic_config_push[OTA_MQTT_TOPIC_LEN];
27     ota_cb_fpt cb;
28     void *context;
29 } otamqtt_Struct_t, *otamqtt_Struct_pt;
30 
31 /* Generate topic name according to @ota_topic_type, @product_key, @device_name
32  */
33 /* and then copy to @buf. */
34 /* 0, successful; -1, failed */
otamqtt_GenTopicName(char * buf,size_t buf_len,const char * ota_topic_type,const char * product_key,const char * device_name)35 static int otamqtt_GenTopicName(char *buf, size_t buf_len,
36                                 const char *ota_topic_type,
37                                 const char *product_key,
38                                 const char *device_name)
39 {
40     int ret;
41 
42     ret = HAL_Snprintf(buf, buf_len, "/ota/device/%s/%s/%s", ota_topic_type,
43                        product_key, device_name);
44 
45     if (ret >= buf_len) {
46         return -1;
47     }
48 
49     if (ret < 0) {
50         OTA_LOG_ERROR("HAL_Snprintf failed");
51         return -1;
52     }
53 
54     return 0;
55 }
56 
57 /* report progress of OTA */
otamqtt_Publish(otamqtt_Struct_pt handle,const char * topic_type,int qos,const char * msg)58 static int otamqtt_Publish(otamqtt_Struct_pt handle, const char *topic_type,
59                            int qos, const char *msg)
60 {
61     int ret;
62     char topic_name[OTA_MQTT_TOPIC_LEN];
63     iotx_mqtt_topic_info_t topic_info;
64 
65     memset(&topic_info, 0, sizeof(iotx_mqtt_topic_info_t));
66 
67     if (0 == qos) {
68         topic_info.qos = IOTX_MQTT_QOS0;
69     } else {
70         topic_info.qos = IOTX_MQTT_QOS1;
71     }
72     topic_info.payload = (void *)msg;
73     topic_info.payload_len = strlen(msg);
74 
75     /* inform OTA to topic: "/ota/device/progress/$(product_key)/$(device_name)"
76      */
77     ret = otamqtt_GenTopicName(topic_name, OTA_MQTT_TOPIC_LEN, topic_type,
78                                handle->product_key, handle->device_name);
79     if (ret < 0) {
80         OTA_LOG_ERROR("generate topic name of info failed");
81         return -1;
82     }
83 
84     ret = IOT_MQTT_Publish(handle->mqtt, topic_name, &topic_info);
85     if (ret < 0) {
86         OTA_LOG_ERROR("publish failed");
87         return IOT_OTAE_OSC_FAILED;
88     }
89 
90     return 0;
91 }
92 
otamqtt_publish_full_topic(otamqtt_Struct_pt handle,const char * topic_name,iotx_mqtt_topic_info_pt topic_msg)93 static int otamqtt_publish_full_topic(otamqtt_Struct_pt handle,
94                                       const char *topic_name,
95                                       iotx_mqtt_topic_info_pt topic_msg)
96 {
97     if (IOT_MQTT_Publish(handle->mqtt, topic_name, topic_msg) < 0) {
98         OTA_LOG_ERROR("publish failed");
99         return IOT_OTAE_OSC_FAILED;
100     }
101 
102     return 0;
103 }
104 
105 /* decode JSON string to get firmware information, like firmware version, URL,
106  * file size, MD5. */
107 /* return NONE */
otamqtt_UpgrageCb(void * pcontext,void * pclient,iotx_mqtt_event_msg_pt msg)108 static void otamqtt_UpgrageCb(void *pcontext, void *pclient,
109                               iotx_mqtt_event_msg_pt msg)
110 {
111     otamqtt_Struct_pt handle = (otamqtt_Struct_pt)pcontext;
112     iotx_mqtt_topic_info_pt topic_info = (iotx_mqtt_topic_info_pt)msg->msg;
113 
114     OTA_LOG_DEBUG("topic=%.*s", topic_info->topic_len, topic_info->ptopic);
115     OTA_LOG_DEBUG("len=%u, topic_msg=%.*s", topic_info->payload_len,
116                   topic_info->payload_len, (char *)topic_info->payload);
117 
118     if (IOTX_MQTT_EVENT_PUBLISH_RECEIVED != msg->event_type) {
119         return;
120     }
121 
122     if (NULL != strstr(topic_info->ptopic, "/ota/device/request")) {
123         OTA_LOG_DEBUG("receive device request");
124         /*if(NULL != HAL_strnstr(topic_info->payload, topic_info->payload_len,
125             "url", strlen("url")))*/
126         if (NULL != strstr(topic_info->payload, "url")) {
127             OTA_LOG_INFO("get request reply for new version image");
128             if (NULL != handle->cb) {
129                 handle->cb(handle->context, topic_info->payload,
130                            topic_info->payload_len,
131                            IOTX_OTA_TOPIC_TYPE_DEVICE_REQUEST);
132             }
133         }
134     } else if (NULL != strstr(topic_info->ptopic, "/ota/device/upgrade")) {
135         OTA_LOG_DEBUG("receive device upgrade");
136         if (NULL != handle->cb) {
137             handle->cb(handle->context, topic_info->payload,
138                        topic_info->payload_len,
139                        IOTX_OTA_TOPIC_TYPE_DEVICE_UPGRATE);
140         }
141     } else if (NULL != strstr(topic_info->ptopic, "/thing/config/get_reply")) {
142         OTA_LOG_DEBUG("receive config get_reply");
143         if (NULL != handle->cb) {
144             handle->cb(handle->context, topic_info->payload,
145                        topic_info->payload_len, IOTX_OTA_TOPIC_TYPE_CONFIG_GET);
146         }
147     } else if (NULL != strstr(topic_info->ptopic, "/thing/config/push")) {
148         OTA_LOG_DEBUG("receive config push");
149         if (NULL != handle->cb) {
150             if (0 != handle->cb(handle->context, topic_info->payload,
151                                 topic_info->payload_len,
152                                 IOTX_OTA_TOPIC_TYPE_CONFIG_PUSH)) {
153                 /* fail, send fail response code:400 */
154                 const char *pvalue;
155                 uint32_t val_len;
156                 char topic[OTA_MQTT_TOPIC_LEN] = { 0 };
157                 char message[OTA_MQTT_TOPIC_LEN] = { 0 };
158                 iotx_mqtt_topic_info_t message_info;
159 
160                 memset(&message_info, 0, sizeof(iotx_mqtt_topic_info_t));
161 
162                 pvalue =
163                     otalib_JsonValueOf(topic_info->payload,
164                                        topic_info->payload_len, "id", &val_len);
165 
166                 HAL_Snprintf(topic, OTA_MQTT_TOPIC_LEN,
167                              "/sys/%s/%s/thing/config/push_reply",
168                              handle->product_key, handle->device_name);
169                 HAL_Snprintf(message, OTA_MQTT_TOPIC_LEN,
170                              "\"id\":%.*s,\"code\":\"%d\",\"data\":{}", val_len,
171                              pvalue, 400);
172                 message_info.qos = IOTX_MQTT_QOS0;
173                 message_info.payload = (void *)message;
174                 message_info.payload_len = strlen(message);
175 
176                 if (IOT_MQTT_Publish(handle->mqtt, topic, &message_info) < 0) {
177                     OTA_LOG_ERROR("publish failed");
178                 }
179             }
180         }
181     }
182 }
183 
osc_Init(const char * product_key,const char * device_name,void * ch_signal,ota_cb_fpt cb,void * context)184 void *osc_Init(const char *product_key, const char *device_name,
185                void *ch_signal, ota_cb_fpt cb, void *context)
186 {
187     int ret;
188     otamqtt_Struct_pt h_osc = NULL;
189 
190     h_osc = OTA_MALLOC(sizeof(otamqtt_Struct_t));
191     if (h_osc == NULL) {
192         OTA_LOG_ERROR("allocate for h_osc failed");
193         return NULL;
194     }
195 
196     memset(h_osc, 0, sizeof(otamqtt_Struct_t));
197 
198     /* subscribe the OTA topic:
199      * "/ota/device/request/$(product_key)/$(device_name)" */
200     ret = otamqtt_GenTopicName(h_osc->topic_request, OTA_MQTT_TOPIC_LEN,
201                                "request", product_key, device_name);
202     if (ret < 0) {
203         OTA_LOG_ERROR("generate topic name of request failed");
204         goto do_exit;
205     }
206 
207 #ifdef MQTT_AUTO_SUBSCRIBE
208     ret =
209         IOT_MQTT_Subscribe(ch_signal, h_osc->topic_request,
210                            IOTX_MQTT_QOS3_SUB_LOCAL, otamqtt_UpgrageCb, h_osc);
211 #else
212     ret = IOT_MQTT_Subscribe(ch_signal, h_osc->topic_request, IOTX_MQTT_QOS1,
213                              otamqtt_UpgrageCb, h_osc);
214 #endif
215     if (ret < 0) {
216         OTA_LOG_ERROR("mqtt subscribe failed");
217         goto do_exit;
218     }
219 
220     /* subscribe the OTA topic:
221      * "/ota/device/upgrade/$(product_key)/$(device_name)" */
222     ret = otamqtt_GenTopicName(h_osc->topic_upgrade, OTA_MQTT_TOPIC_LEN,
223                                "upgrade", product_key, device_name);
224     if (ret < 0) {
225         OTA_LOG_ERROR("generate topic name of upgrade failed");
226         goto do_exit;
227     }
228 
229 #ifdef MQTT_AUTO_SUBSCRIBE
230     ret =
231         IOT_MQTT_Subscribe(ch_signal, h_osc->topic_upgrade,
232                            IOTX_MQTT_QOS3_SUB_LOCAL, otamqtt_UpgrageCb, h_osc);
233 #else
234     ret = IOT_MQTT_Subscribe(ch_signal, h_osc->topic_upgrade, IOTX_MQTT_QOS1,
235                              otamqtt_UpgrageCb, h_osc);
236 #endif
237     if (ret < 0) {
238         OTA_LOG_ERROR("mqtt subscribe failed");
239         goto do_exit;
240     }
241 
242     /* subscribe the OTA topic:
243      * "/sys/{productKey}/{deviceName}/thing/config/get_reply" */
244     ret = HAL_Snprintf(h_osc->topic_config_get, OTA_MQTT_TOPIC_LEN,
245                        "/sys/%s/%s/thing/config/get_reply", product_key,
246                        device_name);
247     if (ret < 0) {
248         OTA_LOG_ERROR("generate topic name of config get failed");
249         goto do_exit;
250     }
251 
252 #ifdef MQTT_AUTO_SUBSCRIBE
253     ret =
254         IOT_MQTT_Subscribe(ch_signal, h_osc->topic_config_get,
255                            IOTX_MQTT_QOS3_SUB_LOCAL, otamqtt_UpgrageCb, h_osc);
256 #else
257     ret = IOT_MQTT_Subscribe(ch_signal, h_osc->topic_config_get, IOTX_MQTT_QOS0,
258                              otamqtt_UpgrageCb, h_osc);
259 #endif
260     if (ret < 0) {
261         OTA_LOG_ERROR("mqtt subscribe failed");
262         goto do_exit;
263     }
264 
265     /* subscribe the OTA topic:
266      * "/sys/{productKey}/{deviceName}/thing/config/push" */
267     ret =
268         HAL_Snprintf(h_osc->topic_config_push, OTA_MQTT_TOPIC_LEN,
269                      "/sys/%s/%s/thing/config/push", product_key, device_name);
270     if (ret < 0) {
271         OTA_LOG_ERROR("generate topic name of config get failed");
272         goto do_exit;
273     }
274 
275 #ifdef MQTT_AUTO_SUBSCRIBE
276     ret =
277         IOT_MQTT_Subscribe(ch_signal, h_osc->topic_config_push,
278                            IOTX_MQTT_QOS3_SUB_LOCAL, otamqtt_UpgrageCb, h_osc);
279 #else
280     ret = IOT_MQTT_Subscribe(ch_signal, h_osc->topic_config_push,
281                              IOTX_MQTT_QOS0, otamqtt_UpgrageCb, h_osc);
282 #endif
283     if (ret < 0) {
284         OTA_LOG_ERROR("mqtt subscribe failed");
285         goto do_exit;
286     }
287 
288     h_osc->mqtt = ch_signal;
289     h_osc->product_key = product_key;
290     h_osc->device_name = device_name;
291     h_osc->cb = cb;
292     h_osc->context = context;
293 
294     return h_osc;
295 
296 do_exit:
297     if (NULL != h_osc) {
298         OTA_FREE(h_osc);
299     }
300 
301     return NULL;
302 }
303 
osc_Deinit(void * handle)304 int osc_Deinit(void *handle)
305 {
306     if (NULL != handle) {
307         OTA_FREE(handle);
308     }
309 
310     return 0;
311 }
312 
313 /* report progress of OTA */
osc_ReportProgress(void * handle,const char * msg)314 int osc_ReportProgress(void *handle, const char *msg)
315 {
316     return otamqtt_Publish(handle, "progress", 0, msg);
317 }
318 
319 /* report version of OTA firmware */
osc_ReportVersion(void * handle,const char * msg)320 int osc_ReportVersion(void *handle, const char *msg)
321 {
322     return otamqtt_Publish(handle, "inform", 1, msg);
323 }
324 
325 /* request the OTA firmware pushed by user*/
osc_RequestImage(void * handle,const char * msg)326 int osc_RequestImage(void *handle, const char *msg)
327 {
328     return otamqtt_Publish(handle, "request", 1, msg);
329 }
330 
331 /* request the config */
osc_RequestConfig(void * handle,const char * topic_name,iotx_mqtt_topic_info_pt topic_msg)332 int osc_RequestConfig(void *handle, const char *topic_name,
333                       iotx_mqtt_topic_info_pt topic_msg)
334 {
335     return otamqtt_publish_full_topic(handle, topic_name, topic_msg);
336 }
337 
338 #endif /* #if (OTA_SIGNAL_CHANNEL) == 1 */
339 #endif /* #ifndef __OTA_MQTT_C_H__ */
340