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