Imported Upstream version 1.1.1
[platform/upstream/iotivity.git] / service / easy-setup / mediator / csdk / src / provisioning.cpp
1 //******************************************************************
2 //
3 // Copyright 2015 Samsung Electronics All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 #include "provisioning.h"
22
23 //Standard includes
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <signal.h>
28 #include <unistd.h>
29 #include <pthread.h>
30
31 //EasySetup include files
32 #include "ocpayload.h"
33 #include "escommon.h"
34
35 // External includes
36 #include "logger.h"
37 #include "oic_malloc.h"
38 #include "oic_string.h"
39
40 #define ES_PROV_TAG "EASY_SETUP_PROVISIONING"
41
42 bool gProvisioningCondFlag = false;
43
44 static ProvConfig *gProvConfig;
45 static WiFiOnboadingConnection *gOnboardConn;
46 static char gSzFindResourceQueryUri[MAX_QUERY_LENGTH] =
47 { 0 };
48
49 /**
50  * @var cbData
51  * @brief Callback for providing provisioning status callback to application
52  */
53 static OCProvisioningStatusCB cbData = NULL;
54
55 /**
56  * Utility function for error callback.
57  */
58 void ErrorCallback(ProvStatus status)
59 {
60     ProvisioningInfo *provInfo = GetCallbackObjectOnError(status);
61     if(cbData != NULL)
62     {
63         cbData(provInfo);
64     }
65 }
66
67 /**
68  * Functions implementing the exposed APIs.
69  */
70 OCStackResult InitProvisioningHandler()
71 {
72     OCStackResult ret = OC_STACK_ERROR;
73     /* Initialize OCStack*/
74     if (OCInit(NULL, 0, OC_CLIENT) != OC_STACK_OK)
75     {
76         OIC_LOG(ERROR, ES_PROV_TAG, "OCStack init error");
77         return ret;
78     }
79
80     return OC_STACK_OK;
81 }
82
83 OCStackResult StartProvisioningProcess(const ProvConfig *netInfo,
84         WiFiOnboadingConnection *onboardConn, OCProvisioningStatusCB provisioningStatusCallback,
85         char *findResQuery)
86 {
87
88     if (findResQuery != NULL)
89     {
90         OICStrcpy(gSzFindResourceQueryUri, sizeof(gSzFindResourceQueryUri) - 1, findResQuery);
91     }
92     else
93     {
94         OIC_LOG(ERROR, ES_PROV_TAG, PCF("Find resource query is NULL"));
95         goto Error;
96     }
97
98     pthread_t thread_handle;
99
100     if (!ValidateEasySetupParams(netInfo, onboardConn, provisioningStatusCallback))
101     {
102         goto Error;
103     }
104
105     if (!SetProgress(provisioningStatusCallback))
106     {
107         // Device provisioning session is running already.
108         OIC_LOG(INFO, ES_PROV_TAG, PCF("Device provisioning session is running already"));
109         goto Error;
110     }
111
112     if (!ConfigEnrolleeObject(netInfo, onboardConn))
113     {
114         goto Error;
115     }
116
117     if (pthread_create(&thread_handle, NULL, FindProvisioningResource, NULL))
118     {
119         goto Error;
120
121     }
122
123     pthread_join(thread_handle, NULL);
124
125     return OC_STACK_OK;
126
127     Error:
128     {
129         ErrorCallback(DEVICE_NOT_PROVISIONED);
130         ClearMemory();
131         return OC_STACK_ERROR;
132     }
133
134 }
135
136 void StopProvisioningProcess()
137 {
138     ResetProgress();
139 }
140
141 OCStackResult TerminateProvisioningHandler()
142 {
143     OCStackResult ret = OC_STACK_ERROR;
144     if (OCStop() != OC_STACK_OK)
145     {
146         OIC_LOG(ERROR, ES_PROV_TAG, "OCStack stop error");
147     }
148
149     gProvisioningCondFlag = true;
150     ResetProgress();
151
152     ret = OC_STACK_OK;
153     return ret;
154 }
155
156 OCStackApplicationResult TriggerNetworkConnectionResponse(void* /*ctx*/, OCDoHandle /*handle*/,
157         OCClientResponse *clientResponse)
158 {
159     OIC_LOG_V(DEBUG, ES_PROV_TAG, "INSIDE TriggerNetworkConnectionResponse");
160
161     // If user stopped the process then return from this function;
162     if (IsSetupStopped())
163     {
164         ErrorCallback(DEVICE_NOT_PROVISIONED);
165         ClearMemory();
166         return OC_STACK_DELETE_TRANSACTION;
167     }
168
169     if (!ValidateEnrolleeBasicResponse(clientResponse))
170     {
171         ErrorCallback(DEVICE_NOT_PROVISIONED);
172         return OC_STACK_DELETE_TRANSACTION;
173     }
174
175     SuccessCallback(clientResponse);
176     return OC_STACK_KEEP_TRANSACTION;
177 }
178
179 OCStackResult TriggerNetworkConnection(OCQualityOfService qos, const char *query,
180         const char *resUri, OCDevAddr *destination, int /*pauseBeforeStart*/)
181 {
182     OIC_LOG_V(INFO, ES_PROV_TAG, "Inside TriggerNetworkConnection");
183
184     OCRepPayload *payload = OCRepPayloadCreate();
185
186     OCRepPayloadSetUri(payload, resUri);
187     OCRepPayloadSetPropInt(payload, OC_RSRVD_ES_TR, ES_PS_TRIGGER_CONNECTION);
188
189     OIC_LOG_V(DEBUG, ES_PROV_TAG, "Triggering network connection from Mediator");
190
191     OCStackResult ret = InvokeOCDoResource(query, OC_REST_POST, destination, qos,
192             TriggerNetworkConnectionResponse, payload, NULL, 0);
193
194     return ret;
195 }
196
197
198
199 bool ClearMemory()
200 {
201
202     OIC_LOG(DEBUG, ES_PROV_TAG, "thread_pool_add_task of FindProvisioningResource failed");
203     OICFree(gProvConfig);
204     OICFree(gOnboardConn);
205
206     return true;
207
208 }
209
210 bool ConfigEnrolleeObject(const ProvConfig *netInfo, WiFiOnboadingConnection *connection)
211 {
212
213     //Copy Network Provisioning  Information
214     gProvConfig = (ProvConfig *) OICCalloc(1, sizeof(ProvConfig));
215     gOnboardConn = (WiFiOnboadingConnection *) OICCalloc(1, sizeof(WiFiOnboadingConnection));
216
217     if (gProvConfig == NULL)
218     {
219         OIC_LOG(ERROR, ES_PROV_TAG, "Invalid input..");
220         return false;
221     }
222
223     if (gOnboardConn == NULL)
224     {
225         OIC_LOG(ERROR, ES_PROV_TAG, "Invalid input..");
226         return false;
227     }
228
229     memcpy(gProvConfig, netInfo, sizeof(ProvConfig));
230     memcpy(gOnboardConn, connection, sizeof(WiFiOnboadingConnection));
231
232     OIC_LOG_V(DEBUG, ES_PROV_TAG, "Network Provisioning Info. SSID = %s",
233             gProvConfig->provData.WIFI.ssid);
234
235     OIC_LOG_V(DEBUG, ES_PROV_TAG, "Network Provisioning Info. PWD = %s",
236             gProvConfig->provData.WIFI.pwd);
237
238     return true;
239
240 }
241
242 void LogProvisioningResponse(OCRepPayloadValue * val)
243 {
244
245     switch (val->type)
246     {
247         case OCREP_PROP_NULL:
248             OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s: NULL", val->name);
249             break;
250         case OCREP_PROP_INT:
251             OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(int):%lld", val->name, val->i);
252             break;
253         case OCREP_PROP_DOUBLE:
254             OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(double):%f", val->name, val->d);
255             break;
256         case OCREP_PROP_BOOL:
257             OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(bool):%s", val->name, val->b ? "true" : "false");
258             break;
259         case OCREP_PROP_STRING:
260             OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(string):%s", val->name, val->str);
261             break;
262         case OCREP_PROP_OBJECT:
263             // Note: Only prints the URI (if available), to print further, you'll
264             // need to dig into the object better!
265             OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(OCRep):%s", val->name, val->obj->uri);
266             break;
267         case OCREP_PROP_ARRAY:
268             switch (val->arr.type)
269             {
270                 case OCREP_PROP_INT:
271                     OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(int array):%zu x %zu x %zu",
272                             val->name,
273                             val->arr.dimensions[0], val->arr.dimensions[1],
274                             val->arr.dimensions[2]);
275                     break;
276                 case OCREP_PROP_DOUBLE:
277                     OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(double array):%zu x %zu x %zu",
278                             val->name,
279                             val->arr.dimensions[0], val->arr.dimensions[1],
280                             val->arr.dimensions[2]);
281                     break;
282                 case OCREP_PROP_BOOL:
283                     OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(bool array):%zu x %zu x %zu",
284                             val->name,
285                             val->arr.dimensions[0], val->arr.dimensions[1],
286                             val->arr.dimensions[2]);
287                     break;
288                 case OCREP_PROP_STRING:
289                     OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(string array):%zu x %zu x %zu",
290                             val->name,
291                             val->arr.dimensions[0], val->arr.dimensions[1],
292                             val->arr.dimensions[2]);
293                     break;
294                 case OCREP_PROP_OBJECT:
295                     OIC_LOG_V(DEBUG, ES_PROV_TAG, "\t\t%s(OCRep array):%zu x %zu x %zu",
296                             val->name,
297                             val->arr.dimensions[0], val->arr.dimensions[1],
298                             val->arr.dimensions[2]);
299                     break;
300                 default:
301                     break;
302             }
303             break;
304         default:
305             break;
306     }
307 }
308
309 OCStackResult FindNetworkResource()
310 {
311     OCStackResult ret = OC_STACK_ERROR;
312     if (OCStop() != OC_STACK_OK)
313     {
314         OIC_LOG(ERROR, ES_PROV_TAG, "OCStack stop error");
315     }
316
317     return ret;
318 }
319
320 ProvisioningInfo *PrepareProvisioingStatusCB(OCClientResponse *clientResponse,
321         ProvStatus provStatus)
322 {
323
324     ProvisioningInfo *provInfo = (ProvisioningInfo *) OICCalloc(1, sizeof(ProvisioningInfo));
325
326     if (provInfo == NULL)
327     {
328         OIC_LOG_V(ERROR, ES_PROV_TAG, "Failed to allocate memory");
329         return NULL;
330     }
331
332     OCDevAddr *devAddr = (OCDevAddr *) OICCalloc(1, sizeof(OCDevAddr));
333
334     if (devAddr == NULL)
335     {
336         OIC_LOG_V(ERROR, ES_PROV_TAG, "Failed to allocate memory");
337         OICFree(provInfo);
338         return NULL;
339     }
340
341     OICStrcpy(devAddr->addr, sizeof(devAddr->addr), clientResponse->addr->addr);
342
343     devAddr->port = clientResponse->addr->port;
344
345     provInfo->provDeviceInfo.addr = devAddr;
346
347     provInfo->provStatus = provStatus;
348
349     return provInfo;
350 }
351
352 bool InProgress()
353 {
354
355     // It means already Easy Setup provisioning session is going on.
356     if (NULL != cbData)
357     {
358         OIC_LOG(ERROR, ES_PROV_TAG, "Easy setup session is already in progress");
359         return true;
360     }
361
362     return false;
363 }
364
365 bool SetProgress(OCProvisioningStatusCB provisioningStatusCallback)
366 {
367
368     if (InProgress())
369         return false;
370
371     cbData = provisioningStatusCallback;
372
373     return true;
374 }
375
376 bool ResetProgress()
377 {
378
379     cbData = NULL;
380     return true;
381 }
382
383 ProvisioningInfo *CreateCallBackObject()
384 {
385
386     ProvisioningInfo *provInfo = (ProvisioningInfo *) OICCalloc(1, sizeof(ProvisioningInfo));
387
388     if (provInfo == NULL)
389     {
390         OIC_LOG_V(ERROR, ES_PROV_TAG, "Failed to allocate memory");
391         return NULL;
392     }
393
394     OCDevAddr *devAddr = (OCDevAddr *) OICCalloc(1, sizeof(OCDevAddr));
395
396     if (devAddr == NULL)
397     {
398         OIC_LOG_V(ERROR, ES_PROV_TAG, "Failed to allocate memory");
399         OICFree(provInfo);
400         return NULL;
401     }
402
403     provInfo->provDeviceInfo.addr = devAddr;
404
405     return provInfo;
406
407 }
408
409 ProvisioningInfo *GetCallbackObjectOnError(ProvStatus status)
410 {
411
412     ProvisioningInfo *provInfo = CreateCallBackObject();
413     OICStrcpy(provInfo->provDeviceInfo.addr->addr, sizeof(provInfo->provDeviceInfo.addr->addr),
414             gOnboardConn->ipAddress);
415
416     provInfo->provDeviceInfo.addr->port = IP_PORT;
417     provInfo->provStatus = status;
418     return provInfo;
419 }
420
421 ProvisioningInfo *GetCallbackObjectOnSuccess(OCClientResponse *clientResponse,
422         ProvStatus provStatus)
423 {
424     ProvisioningInfo *provInfo = CreateCallBackObject();
425     OICStrcpy(provInfo->provDeviceInfo.addr->addr, sizeof(provInfo->provDeviceInfo.addr->addr),
426             clientResponse->addr->addr);
427
428     provInfo->provDeviceInfo.addr->port = clientResponse->addr->port;
429     provInfo->provStatus = provStatus;
430     return provInfo;
431 }
432
433 bool ValidateFindResourceResponse(OCClientResponse * clientResponse)
434 {
435     if (!(clientResponse) || !(clientResponse->payload))
436     {
437         OIC_LOG_V(INFO, ES_PROV_TAG, "Received Null clientResponse");
438         return false;
439     }
440
441     if (clientResponse->payload->type != PAYLOAD_TYPE_DISCOVERY)
442     {
443         OIC_LOG_V(DEBUG, ES_PROV_TAG, "Payload is not discovery type");
444         return false;
445     }
446
447     return true;
448 }
449
450 bool ValidateEnrolleeResponse(OCClientResponse * clientResponse)
451 {
452     if (!(clientResponse) || !(clientResponse->payload))
453     {
454         OIC_LOG_V(INFO, ES_PROV_TAG, "Received Null clientResponse");
455         return false;
456     }
457
458     if (clientResponse->payload->type != PAYLOAD_TYPE_REPRESENTATION)
459     {
460         OIC_LOG_V(DEBUG, ES_PROV_TAG, "Incoming payload is not a representation");
461         return false;
462     }
463
464     // If flow reachese here means no error condition hit.
465     return true;
466 }
467
468 bool ValidateEnrolleeBasicResponse(OCClientResponse * clientResponse)
469 {
470     if (!clientResponse)
471     {
472         OIC_LOG_V(INFO, ES_PROV_TAG, "Received Null clientResponse");
473         return false;
474     }
475
476     if(clientResponse->result != OC_STACK_OK)
477     {
478         OIC_LOG_V(INFO, ES_PROV_TAG, "Received error response");
479         return false;
480     }
481
482     // If flow reaches, then there no error condition hit.
483     return true;
484 }
485
486
487 void SuccessCallback(OCClientResponse * clientResponse)
488 {
489     ProvisioningInfo *provInfo = GetCallbackObjectOnSuccess(clientResponse, DEVICE_PROVISIONED);
490     if(cbData != NULL)
491     {
492         cbData(provInfo);
493     }
494 }
495
496 void* FindProvisioningResource(void* /*data*/)
497 {
498
499     // If user stopped the process before thread get scheduled then check and return from this function;
500     if (IsSetupStopped())
501     {
502         ErrorCallback(DEVICE_NOT_PROVISIONED);
503         ClearMemory();
504         return NULL;
505     }
506
507     OCStackResult ret = OC_STACK_ERROR;
508
509     OIC_LOG_V(DEBUG, ES_PROV_TAG, "szFindResourceQueryUri = %s", gSzFindResourceQueryUri);
510
511     OCCallbackData ocCBData;
512
513     ocCBData.cb = FindProvisioningResourceResponse;
514     ocCBData.context = (void *) EASY_SETUP_DEFAULT_CONTEXT_VALUE;
515     ocCBData.cd = NULL;
516
517     ret = OCDoResource(NULL, OC_REST_DISCOVER, gSzFindResourceQueryUri, NULL, NULL,
518             gProvConfig->connType, OC_LOW_QOS, &ocCBData, NULL, 0);
519
520     if (ret != OC_STACK_OK)
521     {
522         ErrorCallback(DEVICE_NOT_PROVISIONED);
523         ClearMemory();
524     }
525
526     return NULL;
527 }
528
529 OCStackResult InvokeOCDoResource(const char *query, OCMethod method, const OCDevAddr *dest,
530         OCQualityOfService qos, OCClientResponseHandler cb, OCRepPayload *payload,
531         OCHeaderOption *options, uint8_t numOptions)
532 {
533     OCStackResult ret;
534     OCCallbackData cbData;
535
536     cbData.cb = cb;
537     cbData.context = (void *) EASY_SETUP_DEFAULT_CONTEXT_VALUE;
538     cbData.cd = NULL;
539
540     ret = OCDoResource(NULL, method, query, dest, (OCPayload *) payload, gProvConfig->connType, qos,
541             &cbData, options, numOptions);
542
543     if (ret != OC_STACK_OK)
544     {
545         OIC_LOG_V(ERROR, ES_PROV_TAG, "OCDoResource returns error %d with method %d", ret, method);
546     }
547
548     return ret;
549 }
550
551 OCStackResult ProvisionEnrollee(OCQualityOfService qos, const char *query, const char *resUri,
552         OCDevAddr *destination, int pauseBeforeStart)
553 {
554
555     // This sleep is required in case of BLE provisioning due to packet drop issue.
556     OIC_LOG_V(INFO, ES_PROV_TAG, "Sleeping for %d seconds", pauseBeforeStart);
557     sleep(pauseBeforeStart);
558     OIC_LOG_V(INFO, ES_PROV_TAG, "\n\nExecuting ProvisionEnrollee%s", __func__);
559
560     OCRepPayload *payload = OCRepPayloadCreate();
561
562     OCRepPayloadSetUri(payload, resUri);
563     OCRepPayloadSetPropString(payload, OC_RSRVD_ES_TNN, gProvConfig->provData.WIFI.ssid);
564     OCRepPayloadSetPropString(payload, OC_RSRVD_ES_CD, gProvConfig->provData.WIFI.pwd);
565
566     OIC_LOG_V(DEBUG, ES_PROV_TAG, "OCPayload ready for ProvisionEnrollee");
567
568     OCStackResult ret = InvokeOCDoResource(query, OC_REST_POST, destination, qos,
569             ProvisionEnrolleeResponse, payload, NULL, 0);
570
571     return ret;
572 }
573
574 bool IsSetupStopped()
575 {
576     return (cbData == NULL) ? true : false;
577 }