SimpleLink CC32xx OTA Library
Simplifies the implementation of Internet connectivity
CdnGithub.c
1 /*
2  * Copyright (c) 2018, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of Texas Instruments Incorporated nor the names of
17  * its contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <ti/net/ota/otauser.h>
34 #include <ti/net/ota/source/OtaHttpClient.h>
35 #include <ti/net/ota/source/OtaJson.h>
36 #include <ti/net/ota/source/CdnVendors/CdnGithub.h>
37 
38 #if OTA_SERVER_TYPE == OTA_SERVER_GITHUB
39 
40 /* Github requests */
41 #define OTA_SERVER_REST_REQ_DIR "/contents/" /* returns files/folder list */
42 #define OTA_SERVER_REST_HDR "User-Agent: "
43 
44 /* Github response metadata names to parse */
45 #define JSON_FILE_SIZE "size"
46 #define JSON_FILE_NAME "download_url"
47 
48 /* JSON key path length of the different fields in the metadata JSON file */
49 #define METADATA_FILENAME_PATH_LENGTH (30)
50 #define METADATA_FILESIZE_PATH_LENGTH (30)
51 
52 char template_GithubMetadata [] = "{\"#\":[{\"size\": int32,\"download_url\": string}]}";
53 
54 extern OtaJson OtaJsonObj;
55 
56 /* GITHUB API - build get contents request, example:
57  GET /repos/CC3X20/SL_OTA/contents/OTA_R2 HTTP/1.1
58  Host: api.github.com
59 */
60 int16_t CdnGithub_SendReqDir(int16_t SockId, uint8_t *pSendBuf, uint8_t *pServerName, uint8_t *pVendorDir, uint8_t *pVendorToken)
61 {
62  uint8_t ReqDirCmdBuf[100];
63 
64  strcpy((char *)ReqDirCmdBuf, OTA_VENDOR_ROOT_DIR);
65  strcat((char *)ReqDirCmdBuf, OTA_SERVER_REST_REQ_DIR);
66 
67  _SlOtaLibTrace(("CdnGithub_SendReqDir: uri=%s\r\n", ReqDirCmdBuf));
68  return HttpClient_SendReq (SockId, pSendBuf, (uint8_t *)"GET ", pServerName, ReqDirCmdBuf , pVendorDir, (uint8_t *)OTA_SERVER_REST_HDR, pVendorToken);
69 }
70 
71 
72 /* GITHUB API - parse response for directory files request, json RESPONSE, example:
73  [
74  {
75  "name": "20151217_1_28.tar",
76  "path": "OTA_R2/20151217_1_28.tar",
77  "sha": "7b30216698eca244ac4851cc9f81aa022f5837ec",
78  "size": 260096,
79  "url": "https://api.github.com/repos/CC3X20/SL_OTA/contents/OTA_R2/20151217_1_28.tar?ref=master",
80  "html_url": "https://github.com/CC3X20/SL_OTA/blob/master/OTA_R2/20151217_1_28.tar",
81  "git_url": "https://api.github.com/repos/CC3X20/SL_OTA/git/blobs/7b30216698eca244ac4851cc9f81aa022f5837ec",
82  "download_url": "https://raw.githubusercontent.com/CC3X20/SL_OTA/master/OTA_R2/20151217_1_28.tar",
83  "type": "file",
84  "_links": {
85  "self": "https://api.github.com/repos/CC3X20/SL_OTA/contents/OTA_R2/20151217_1_28.tar?ref=master",
86  "git": "https://api.github.com/repos/CC3X20/SL_OTA/git/blobs/7b30216698eca244ac4851cc9f81aa022f5837ec",
87  "html": "https://github.com/CC3X20/SL_OTA/blob/master/OTA_R2/20151217_1_28.tar"
88  }
89  }
90  ]
91 */
92 int16_t CdnGithub_ParseRespDir(int16_t SockId, uint8_t *pRespBuf, OtaDirData_t *pOtaDirData)
93 {
94  OtaFileInfo_t CurrFileInfo;
95  int16_t RespLen;
96  uint32_t jsonLen;
97  uint8_t i = 0;
98  int16_t retVal;
99 
100  int16_t files_num = 0;
101  int16_t len = 0;
102  int16_t ProcessedSize;
103  static bool firstRun = TRUE;
104  char * pTempBuf = NULL;
105  char *pJsonArrayKey = "\"#\"";
106 
107  char fileNamePathBuf [METADATA_FILENAME_PATH_LENGTH];
108  char fileSizePathBuf [METADATA_FILESIZE_PATH_LENGTH];
109 
110  pOtaDirData->NumFiles = 0;
111 
112  /* check HTTP status OK 200 and skip all HTTP headers */
113  RespLen = HttpClient_RecvSkipHdr(SockId, pRespBuf, NET_BUF_SIZE, &jsonLen);
114  if (RespLen < 0)
115  {
116  _SlOtaLibTrace(("CdnGithub_ParseRespDir: ERROR HttpClient_RecvSkipHdr, status=%ld\r\n", RespLen));
117  return RespLen;
118  }
119 
120  ProcessedSize = RespLen;
121 
122  /* After parsing the JSON file length, verify this packet holds the entire file */
123  while (ProcessedSize < jsonLen)
124  {
125  if (firstRun)
126  {
127  /* The JSON parser must have the entire file in order to parse it successfully. */
128  /* Allocate a buffer in the size of the entire JSON file and fill it in each round. */
129  pTempBuf = (char*)malloc(jsonLen+1);
130  if (pTempBuf == NULL)
131  {
132  /* Allocation failed, return error. */
133  return -1;
134  }
135  memcpy(pTempBuf, (char *)pRespBuf, ProcessedSize);
136  firstRun = FALSE;
137  }
138  else
139  {
140  /* Copy the received buffer from where we stopped the previous copy */
141  memcpy(&(pTempBuf[ProcessedSize]), (char *)pRespBuf, (jsonLen + 1 - ProcessedSize));
142  ProcessedSize+=len;
143 
144  if (ProcessedSize >= jsonLen)
145  {
146  /* received the entire JSON file. break*/
147  break;
148  }
149  }
150 
151  /* Get the rest of the JSON file */
152  len = HttpClient_RecvAppend(SockId, pRespBuf, NET_BUF_SIZE, ProcessedSize, ProcessedSize);
153 
154  _SlOtaLibTrace(("len is: %d\r\n", len));
155  _SlOtaLibTrace(("After recvAppend pRespBuf is: %s\r\n", pRespBuf));
156  }
157 
158  _SlOtaLibTrace(("the entire JSON pRespBuf is: %s\r\n", pTempBuf));
159 
160  /* At this point the entire JSON file was received */
161  /* Set the fileBuffer to point to the beginning of the JSON file */
162  if(firstRun)
163  {
164  /* Received the entire file in the first run. */
165  /* No additional memory allocation was needed. */
166  OtaJsonObj.jsonBuffer.fileBuffer = (char *)pRespBuf;
167  OtaJsonObj.jsonBuffer.fileBuffer[jsonLen] = '\0';
168  }
169  else
170  {
171  OtaJsonObj.jsonBuffer.fileBuffer = (char *)pTempBuf;
172  OtaJsonObj.jsonBuffer.fileBuffer[jsonLen] = '\0';
173  }
174 
175  /* Set static variable to initial value to allow retry in case of a warning during the OTA process */
176  firstRun = TRUE;
177 
178  /* Initialize the JSON module */
179  retVal = OtaJson_init(template_GithubMetadata, &(OtaJsonObj.jsonBuffer.fileBuffer), jsonLen);
180 
181  /* After calling OtaJson_init, the Json Parser module holds an internal representation */
182  /* of the Json file, there is no longer a use in the text representation. */
183  /* Free dynamic memory that was allocated to contain the Json text representation */
184  /* (whether OtaJson_init returned success or failure) */
185  if (pTempBuf != NULL)
186  {
187  free(pTempBuf);
188  }
189 
190  if (retVal < 0)
191  {
192  _SlOtaLibTrace(("CdnDropbox_ParseRespDir: ERROR OtaJson_init, retVal=%d\r\n", retVal));
193  /* Initializing the JSON module failed, return error */
194  return retVal;
195  }
196 
197  files_num = OtaJson_getArrayMembersCount(OtaJsonObj.jsonObjHandle, pJsonArrayKey);
198  if (files_num < 0)
199  {
200  /* Failed to get array members count, free dynamic memory and return error */
201  return files_num;
202  }
203 
204  for (i = 0; i < files_num; i++)
205  {
206  /* Initialize data containers before parsing */
207  memset (&CurrFileInfo, 0, sizeof(CurrFileInfo));
208 
209  memset (&fileNamePathBuf, 0, sizeof(fileNamePathBuf));
210  snprintf(fileNamePathBuf, METADATA_FILENAME_PATH_LENGTH, "\"#\".[%d].\"download_url\"",i);
211 
212  /* Parse the metadata file name */
213  retVal = OtaJson_getMetadataFileName(CurrFileInfo.OtaFileName, fileNamePathBuf);
214  if (retVal < 0)
215  {
216  /* Parsing metadata file name failed, return error */
217  return retVal;
218  }
219 
220  memset (&fileSizePathBuf, 0, sizeof(fileSizePathBuf));
221  snprintf(fileSizePathBuf, METADATA_FILESIZE_PATH_LENGTH, "\"#\".[%d].\"size\"",i);
222 
223  /* Parse the metadata file size */
224  retVal = OtaJson_getMetadataFileSize((uint32_t *)&CurrFileInfo.OtaFileSize, fileSizePathBuf);
225  if (retVal < 0)
226  {
227  /* Parsing metadata file size failed, return error */
228  return retVal;
229  }
230 
231  pOtaDirData->OtaFileInfo[pOtaDirData->NumFiles++] = CurrFileInfo;
232  _SlOtaLibTrace((" OtaDir FileName=%s, FileSize=%ld\r\n", CurrFileInfo.OtaFileName, CurrFileInfo.OtaFileSize));
233  }
234 
235  /* Flush the rest of the response - assuming full buffer means more data to flush */
236  do
237  {
238  RespLen = HttpClient_Recv(SockId, &pRespBuf[0], NET_BUF_SIZE, 0, 1 /*MAX_EAGAIN_RETRIES*/);
239  if (RespLen <= 0)
240  {
241  break;
242  }
243  } while(RespLen == NET_BUF_SIZE);
244 
245  /* Free dynamic memory */
246  OtaJson_destroy();
247 
248  return pOtaDirData->NumFiles;
249 }
250 
251 int16_t CdnGithub_SendReqFileUrl(int16_t SockId, uint8_t *pRespBuf, uint8_t *pServerName, uint8_t *pFileName, uint8_t *pVendorToken)
252 {
253  /* First check if the filename already contain the full file URL */
254 
255  /* Github, no need to implement, the file url are already in the filename */
256  strcpy((char *)pRespBuf, (const char *)pFileName);
257  return strlen((const char *)pFileName);
258 }
259 
260 int16_t CdnGithub_ParseRespFileUrl(uint16_t SockId, uint8_t *pSendBuf, uint8_t *pFileUrl, uint32_t FileUrlBufSize)
261 {
262  /* Github, The SendBuf(pRespBuf) already have the URL (implemented in CdnGithub_SendReqFileUrl)*/
263  strcpy((char *)pFileUrl, (const char *)pSendBuf);
264  return CDN_STATUS_OK;
265 }
266 
267 
268 #endif /* #if OTA_SERVER_TYPE == OTA_SERVER_GITHUB */