Add APIs of cloud provisioning for Android and JNI layer
[platform/upstream/iotivity.git] / service / easy-setup / mediator / richsdk / src / RemoteEnrollee.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 "RemoteEnrollee.h"
22 #include "EnrolleeResource.h"
23 #include "CloudResource.h"
24 #include "OCPlatform.h"
25 #include "ESException.h"
26 #include "logger.h"
27 #include "OCResource.h"
28 #ifdef __WITH_DTLS__
29 #include "EnrolleeSecurity.h"
30 #endif //__WITH_DTLS
31
32 namespace OIC
33 {
34     namespace Service
35     {
36         static const char ES_BASE_RES_URI[] = "/oic/res";
37         #define ES_REMOTE_ENROLLEE_TAG "ES_REMOTE_ENROLLEE"
38         #define DISCOVERY_TIMEOUT 5
39
40         RemoteEnrollee::RemoteEnrollee()
41         {
42             m_enrolleeSecStatusCb = nullptr;
43             m_RequestPropertyDataStatusCb = nullptr;
44             m_securityPinCb = nullptr;
45             m_secProvisioningDbPathCb = nullptr;
46             m_dataProvStatusCb = nullptr;
47             m_cloudProvStatusCb = nullptr;
48
49             //m_deviceId = nullptr;
50         }
51
52 #ifdef __WITH_DTLS__
53         ESResult RemoteEnrollee::registerSecurityCallbackHandler(SecurityPinCb securityPinCb,
54                 SecProvisioningDbPathCb secProvisioningDbPathCb)
55         {
56             // No need to check NULL for m_secProvisioningDbPathCB as this is not a mandatory
57             // callback function. If m_secProvisioningDbPathCB is NULL, provisioning manager
58             // in security layer will try to find the PDM.db file in the local path.
59             // If PDM.db is found, the provisioning manager operations will succeed.
60             // Otherwise all the provisioning manager operations will fail.
61             m_secProvisioningDbPathCb = secProvisioningDbPathCb;
62             m_securityPinCb = securityPinCb;
63             return ES_OK;
64         }
65 #endif //__WITH_DTLS__
66
67         void RemoteEnrollee::easySetupSecurityStatusCallback(
68                         std::shared_ptr< SecProvisioningStatus > status)
69         {
70             OIC_LOG_V(DEBUG, ES_REMOTE_ENROLLEE_TAG, "easySetupStatusCallback status is, UUID = %s, "
71                     "Status = %d", status->getDeviceUUID().c_str(),
72                     status->getResult());
73
74             if(status->getResult() == ES_OK)
75             {
76                 OIC_LOG(DEBUG, ES_REMOTE_ENROLLEE_TAG, "Ownership and ACL are successful. "
77                         "Continue with Network information provisioning");
78
79                 OIC_LOG(DEBUG,ES_REMOTE_ENROLLEE_TAG,"Before ProvisionEnrollee");
80
81                 m_enrolleeSecStatusCb(status);
82             }
83             else
84             {
85                 OIC_LOG(DEBUG, ES_REMOTE_ENROLLEE_TAG, "Ownership and ACL are fail");
86
87                 m_enrolleeSecStatusCb(status);
88             }
89         }
90
91         void RemoteEnrollee::RequestPropertyDataStatusHandler (
92                 std::shared_ptr< RequestPropertyDataStatus > status)
93         {
94             OIC_LOG(DEBUG,ES_REMOTE_ENROLLEE_TAG,"Entering RequestPropertyDataStatusHandler");
95
96             OIC_LOG_V(DEBUG,ES_REMOTE_ENROLLEE_TAG,"RequestPropertyDataStatus = %d", status->getESResult());
97
98             if(status->getESResult() == ES_OK)
99             {
100                 m_propertyData = status->getPropertyData();
101             }
102
103             m_RequestPropertyDataStatusCb(status);
104         }
105
106         void RemoteEnrollee::dataProvisioningStatusHandler(
107                 std::shared_ptr< DataProvisioningStatus > status)
108         {
109             OIC_LOG(DEBUG,ES_REMOTE_ENROLLEE_TAG,"Entering dataProvisioningStatusHandler");
110
111             OIC_LOG_V(DEBUG,ES_REMOTE_ENROLLEE_TAG,"ProvStatus = %d", status->getESResult());
112
113             if (status->getESResult() == ES_OK)
114             {
115                 if (status->getESState() >= ESState::ES_PROVISIONED_ALREADY)
116                 {
117                     OIC_LOG_V(DEBUG,ES_REMOTE_ENROLLEE_TAG,"ProvStatus = %d", status->getESResult());
118                 }
119             }
120             m_dataProvStatusCb(status);
121
122             return;
123         }
124
125         void RemoteEnrollee::cloudProvisioningStatusHandler (
126                 std::shared_ptr< CloudProvisioningStatus > status)
127         {
128             OIC_LOG(DEBUG,ES_REMOTE_ENROLLEE_TAG,"Entering cloudProvisioningStatusHandler");
129
130             OIC_LOG_V(DEBUG,ES_REMOTE_ENROLLEE_TAG,"CloudProvStatus = %d", status->getESCloudState());
131
132             m_cloudProvStatusCb(status);
133             return;
134         }
135
136         ESResult RemoteEnrollee::ESDiscoveryTimeout(unsigned short waittime)
137         {
138             struct timespec startTime;
139             startTime.tv_sec=0;
140             startTime.tv_sec=0;
141             struct timespec currTime;
142             currTime.tv_sec=0;
143             currTime.tv_nsec=0;
144
145             ESResult res = ES_OK;
146             #ifdef _POSIX_MONOTONIC_CLOCK
147                 int clock_res = clock_gettime(CLOCK_MONOTONIC, &startTime);
148             #else
149                 int clock_res = clock_gettime(CLOCK_REALTIME, &startTime);
150             #endif
151
152             if (0 != clock_res)
153             {
154                 return ES_ERROR;
155             }
156
157             while (ES_OK == res || m_discoveryResponse == false)
158             {
159                 #ifdef _POSIX_MONOTONIC_CLOCK
160                         clock_res = clock_gettime(CLOCK_MONOTONIC, &currTime);
161                 #else
162                         clock_res = clock_gettime(CLOCK_REALTIME, &currTime);
163                 #endif
164
165                 if (0 != clock_res)
166                 {
167                     return ES_ERROR;
168                 }
169                 long elapsed = (currTime.tv_sec - startTime.tv_sec);
170                 if (elapsed > waittime)
171                 {
172                     return ES_OK;
173                 }
174                 if (m_discoveryResponse)
175                 {
176                     res = ES_OK;
177                 }
178              }
179              return res;
180         }
181
182         void RemoteEnrollee::onDeviceDiscovered(std::shared_ptr<OC::OCResource> resource)
183         {
184             OIC_LOG (DEBUG, ES_REMOTE_ENROLLEE_TAG, "onDeviceDiscovered");
185
186             std::string resourceURI;
187             std::string hostAddress;
188             std::string hostDeviceID;
189             try
190             {
191                 if(resource)
192                 {
193                     // Get the resource URI
194                     resourceURI = resource->uri();
195                     OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_TAG,
196                             "URI of the resource: %s", resourceURI.c_str());
197
198                     // Get the resource host address
199                     hostAddress = resource->host();
200                     OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_TAG,
201                             "Host address of the resource: %s", hostAddress.c_str());
202
203                     hostDeviceID = resource->sid();
204                     OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_TAG,
205                             "Host DeviceID of the resource: %s", hostDeviceID.c_str());
206
207                     if(m_deviceId .empty())
208                     {
209                         /*
210                          * Easysetup is always performed with a single Enrollee device and
211                          * in a private network (SoftAP or BLE), so the assumption is that
212                          * only the intended device will respond for the discovery.
213                          * With the above assumption the below two statements are written.
214                          */
215                         m_ocResource = resource;
216                         m_deviceId = resource->sid();
217                         m_discoveryResponse = true;
218                     }
219
220                     else if(!m_deviceId.empty() && m_deviceId == hostDeviceID)
221                     {
222                         OIC_LOG (DEBUG, ES_REMOTE_ENROLLEE_TAG, "Find matched CloudResource");
223                         m_ocResource = resource;
224                         m_discoveryResponse = true;
225                     }
226                 }
227             }
228             catch(std::exception& e)
229             {
230                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_TAG,
231                         "Exception in foundResource: %s", e.what());
232             }
233         }
234
235         ESResult RemoteEnrollee::discoverResource()
236         {
237             OIC_LOG(DEBUG, ES_REMOTE_ENROLLEE_TAG, "Enter discoverResource");
238
239             if (m_ocResource != nullptr)
240             {
241                 throw ESBadRequestException("resource is already created");
242             }
243
244             std::string query("");
245             query.append(ES_BASE_RES_URI);
246             query.append("?rt=");
247             query.append(OC_RSRVD_ES_PROV_RES_TYPE);
248
249             OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_TAG, "query = %s", query.c_str());
250
251             m_discoveryResponse = false;
252
253             std::function< void (std::shared_ptr<OC::OCResource>) > onDeviceDiscoveredCb =
254                     std::bind(&RemoteEnrollee::onDeviceDiscovered, this,
255                                                     std::placeholders::_1);
256             OCStackResult result = OC::OCPlatform::findResource("", query, CT_DEFAULT,
257                     onDeviceDiscoveredCb);
258
259             if (result != OCStackResult::OC_STACK_OK)
260             {
261                 OIC_LOG(ERROR,ES_REMOTE_ENROLLEE_TAG,
262                         "Failed discoverResource");
263                 return ES_ERROR;
264             }
265
266             ESResult foundResponse = ESDiscoveryTimeout (DISCOVERY_TIMEOUT);
267
268             if (foundResponse == ES_ERROR || !m_discoveryResponse)
269             {
270                 OIC_LOG(ERROR,ES_REMOTE_ENROLLEE_TAG,
271                         "Failed discoverResource because timeout");
272                 return ES_ERROR;
273             }
274             return ES_OK;
275         }
276
277         void RemoteEnrollee::initRemoteEnrollee()
278         {
279             ESResult result = ES_ERROR;
280
281             if (m_enrolleeResource != nullptr)
282             {
283                 throw ESBadRequestException ("Already created");
284             }
285
286             result = discoverResource();
287
288             if (result == ES_ERROR)
289             {
290                 OIC_LOG(ERROR,ES_REMOTE_ENROLLEE_TAG,
291                                     "Failed to create resource object using discoverResource");
292                 throw ESBadRequestException ("Resource object not created");
293             }
294
295             else
296             {
297                 if(m_ocResource != nullptr)
298                 {
299                     m_enrolleeResource = std::make_shared<EnrolleeResource>(std::move(m_ocResource));
300                 }
301                 else
302                 {
303                     throw ESBadGetException ("Resource handle is invalid");
304                 }
305             }
306         }
307
308         void RemoteEnrollee::startSecurityProvisioning(EnrolleeSecStatusCb callback)
309         {
310 #ifdef __WITH_DTLS__
311
312             m_enrolleeSecStatusCb = callback;
313
314             EnrolleeSecStatusCb securityProvStatusCb = std::bind(
315                     &RemoteEnrollee::easySetupSecurityStatusCallback,
316                     this,
317                     std::placeholders::_1);
318             //TODO : DBPath is passed empty as of now. Need to take dbpath from application.
319             m_enrolleeSecurity = std::make_shared <EnrolleeSecurity> (m_enrolleeResource, "");
320
321             m_enrolleeSecurity->registerCallbackHandler(securityProvStatusCb,
322                     m_securityPinCb, m_secProvisioningDbPathCb);
323
324             try
325             {
326                 if (!m_enrolleeSecurity->performOwnershipTransfer())
327                 {
328                     OIC_LOG(DEBUG,ES_REMOTE_ENROLLEE_TAG,"Fail performOwnershipTransfer");
329                     std::shared_ptr< SecProvisioningStatus > securityProvisioningStatus =
330                             std::make_shared< SecProvisioningStatus >(nullptr, ES_ERROR);
331                     m_enrolleeSecStatusCb(securityProvisioningStatus);
332                     return;
333                 }
334             }
335             catch (OCException & e)
336             {
337                 OIC_LOG_V(ERROR, ES_REMOTE_ENROLLEE_TAG,
338                         "Exception for performOwnershipTransfer : %s", e.reason().c_str());
339                 return ;
340             }
341 #else
342             OIC_LOG(DEBUG,ES_REMOTE_ENROLLEE_TAG,"Mediator is unsecured.");
343
344             std::shared_ptr< SecProvisioningStatus > securityProvisioningStatus =
345                      std::make_shared< SecProvisioningStatus >(nullptr, ES_ERROR);
346             m_enrolleeSecStatusCb(securityProvisioningStatus);
347 #endif
348         }
349
350         void RemoteEnrollee::requestPropertyData(RequestPropertyDataStatusCb callback)
351         {
352             if(!callback)
353             {
354                 throw ESInvalidParameterException("Callback is empty");
355             }
356
357             m_RequestPropertyDataStatusCb = callback;
358
359             if (m_enrolleeResource == nullptr)
360             {
361                 throw ESBadRequestException ("Device not created");
362             }
363
364             RequestPropertyDataStatusCb RequestPropertyDataStatusCb = std::bind(
365                     &RemoteEnrollee::RequestPropertyDataStatusHandler, this, std::placeholders::_1);
366             m_enrolleeResource->registerRequestPropertyDataStatusCallback(RequestPropertyDataStatusCb);
367             m_enrolleeResource->RequestPropertyData();
368         }
369
370         void RemoteEnrollee::startDataProvisioning(DataProvStatusCb callback)
371         {
372             OIC_LOG(DEBUG,ES_REMOTE_ENROLLEE_TAG,"Enter startDataProvisioning");
373
374             if(!callback)
375             {
376                 throw ESInvalidParameterException("Callback is empty");
377             }
378
379             m_dataProvStatusCb = callback;
380
381             if (m_enrolleeResource == nullptr)
382             {
383                 throw ESBadRequestException ("Device not created");
384             }
385
386             if(m_dataProvInfo.WIFI.ssid.empty())
387             {
388                 throw ESBadRequestException ("Invalid Provisiong Data.");
389             }
390
391             m_dataProvStatusCb = callback;
392
393             DataProvStatusCb dataProvStatusCb = std::bind(
394                     &RemoteEnrollee::dataProvisioningStatusHandler, this, std::placeholders::_1);
395
396             m_enrolleeResource->registerProvStatusCallback(dataProvStatusCb);
397             m_enrolleeResource->provisionEnrollee(m_dataProvInfo);
398         }
399
400         void RemoteEnrollee::initCloudResource()
401         {
402             ESResult result = ES_ERROR;
403
404             if (m_cloudResource != nullptr)
405             {
406                 throw ESBadRequestException ("Already created");
407             }
408
409             result = discoverResource();
410
411             if (result == ES_ERROR)
412             {
413                 OIC_LOG(ERROR,ES_REMOTE_ENROLLEE_TAG,
414                                     "Failed to create resource object using discoverResource");
415                 throw ESBadRequestException ("Resource object not created");
416             }
417
418             else
419             {
420                 if(m_ocResource != nullptr)
421                 {
422                     m_cloudResource = std::make_shared<CloudResource>(std::move(m_ocResource));
423
424                     std::shared_ptr< CloudProvisioningStatus > provStatus = std::make_shared<
425                         CloudProvisioningStatus >(ESResult::ES_ERROR, ESCloudProvState::ES_CLOUD_ENROLLEE_FOUND);
426
427                     m_cloudProvStatusCb(provStatus);
428                 }
429                 else
430                 {
431                     throw ESBadGetException ("Resource handle is invalid");
432                 }
433             }
434         }
435
436
437         void RemoteEnrollee::startCloudProvisioning(CloudProvStatusCb callback)
438         {
439             OIC_LOG(DEBUG,ES_REMOTE_ENROLLEE_TAG,"Enter startCloudProvisioning");
440
441             ESResult result = ES_ERROR;
442
443             if(!callback)
444             {
445                 throw ESInvalidParameterException("Callback is empty");
446             }
447
448             m_cloudProvStatusCb = callback;
449
450             try
451             {
452                 initCloudResource();
453             }
454
455             catch (const std::exception& e)
456             {
457                 OIC_LOG_V(ERROR, ES_REMOTE_ENROLLEE_TAG,
458                     "Exception caught in startCloudProvisioning = %s", e.what());
459
460                 std::shared_ptr< CloudProvisioningStatus > provStatus = std::make_shared<
461                         CloudProvisioningStatus >(ESResult::ES_ERROR, ESCloudProvState::ES_CLOUD_ENROLLEE_NOT_FOUND);
462                 m_cloudProvStatusCb(provStatus);
463             }
464
465             if (m_cloudResource == nullptr)
466             {
467                 throw ESBadRequestException ("Cloud Resource not created");
468             }
469
470             if(m_cloudProvInfo.authCode.empty()
471                 || m_cloudProvInfo.authProvider.empty()
472                 || m_cloudProvInfo.ciServer.empty())
473             {
474                 throw ESBadRequestException ("Invalid Cloud Provisiong Info.");
475             }
476
477             CloudProvStatusCb cloudProvStatusCb = std::bind(
478                     &RemoteEnrollee::cloudProvisioningStatusHandler, this, std::placeholders::_1);
479
480             m_cloudResource->registerCloudProvisioningStatusCallback(cloudProvStatusCb);
481             m_cloudResource->provisionEnrollee(m_cloudProvInfo);
482         }
483
484         void RemoteEnrollee::setDataProvInfo(const DataProvInfo& dataProvInfo)
485         {
486             m_dataProvInfo = dataProvInfo;
487         }
488
489         void RemoteEnrollee::setCloudProvInfo(const CloudProvInfo& cloudProvInfo)
490         {
491             m_cloudProvInfo = cloudProvInfo;
492         }
493
494         DataProvInfo RemoteEnrollee::getDataProvInfo()
495         {
496             return m_dataProvInfo;
497         }
498     }
499 }