Imported Upstream version 1.1.0
[platform/upstream/iotivity.git] / service / easy-setup / mediator / richsdk / src / RemoteEnrolleeResource.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 <functional>
22 #include <time.h>
23
24 #include "RemoteEnrolleeResource.h"
25
26 #include "OCPlatform.h"
27 #include "ESException.h"
28 #include "OCResource.h"
29 #include "logger.h"
30
31 namespace OIC
32 {
33     namespace Service
34     {
35         #define ES_REMOTE_ENROLLEE_RES_TAG "ES_REMOTE_ENROLLEE_RES"
36         #define DISCOVERY_TIMEOUT 5
37
38         static const char ES_BASE_RES_URI[] = "/oic/res";
39         static const char ES_PROV_RES_URI[] = "/oic/prov";
40         static const char ES_PROV_RES_TYPE[] = "oic.r.prov";
41
42         RemoteEnrolleeResource::RemoteEnrolleeResource(const ProvConfig &provConfig,
43                                                   const WiFiOnboadingConnection &onboardingconn)
44         {
45             m_ProvConfig = provConfig;
46             m_wifiOnboardingconn = onboardingconn;
47             m_discoveryResponse = false;
48         }
49
50         void RemoteEnrolleeResource::triggerNetworkConnectionCb(
51                 const HeaderOptions& /*headerOptions*/, const OCRepresentation& rep,
52                 const int eCode)
53         {
54             OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "checkProvInformationCb : %s, eCode = %d",
55                     rep.getUri().c_str(),
56                     eCode);
57
58             if (eCode != 0)
59             {
60                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
61                         "triggerNetworkConnectionCb : Trigger action failed ");
62                 std::shared_ptr< ProvisioningStatus > provStatus = std::make_shared<
63                         ProvisioningStatus >(ESResult::ES_ERROR, ESState::ES_PROVISIONING_ERROR);
64                 m_provStatusCb(provStatus);
65                 return;
66             }
67             else
68             {
69                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
70                         "triggerNetworkConnectionCb : Provisioning is success ");
71                 std::shared_ptr< ProvisioningStatus > provStatus = std::make_shared<
72                         ProvisioningStatus >(ESResult::ES_OK, ESState::ES_PROVISIONING_SUCCESS);
73                 m_provStatusCb(provStatus);
74                 return;
75             }
76         }
77
78         void RemoteEnrolleeResource::triggerNetworkConnection()
79         {
80             if (m_ocResource == nullptr)
81             {
82                 throw ESBadRequestException("Resource is not initialized");
83             }
84
85             OCRepresentation provisioningRepresentation;
86
87             provisioningRepresentation.setValue(OC_RSRVD_ES_TR, 1);
88
89             m_ocResource->post(provisioningRepresentation, QueryParamsMap(),
90                     std::function<
91                             void(const HeaderOptions& headerOptions, const OCRepresentation& rep,
92                                     const int eCode) >(
93                             std::bind(&RemoteEnrolleeResource::triggerNetworkConnectionCb, this,
94                                     std::placeholders::_1, std::placeholders::_2,
95                                     std::placeholders::_3)));
96         }
97
98         void RemoteEnrolleeResource::checkProvInformationCb(const HeaderOptions& /*headerOptions*/,
99                 const OCRepresentation& rep, const int eCode)
100         {
101             OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "checkProvInformationCb : %s, eCode = %d",
102                     rep.getUri().c_str(),
103                     eCode);
104
105             if (eCode != 0)
106             {
107                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
108                         "checkProvInformationCb : Provisioning is failed ");
109                 std::shared_ptr< ProvisioningStatus > provStatus = std::make_shared<
110                         ProvisioningStatus >(ESResult::ES_ERROR, ESState::ES_PROVISIONING_ERROR);
111                 m_provStatusCb(provStatus);
112                 return;
113             }
114
115             int ps = -1;
116
117             rep.getValue(OC_RSRVD_ES_PS, ps);
118
119             OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "checkProvInformationCb : ps - %d", ps);
120
121             //Provisioning status check
122             if (ps == ES_PS_PROVISIONING_COMPLETED)
123             {
124                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
125                         "checkProvInformationCb : Provisioning is success. "
126                         "Now trigger network connection ");
127
128                 #ifdef REMOTE_ARDUINO_ENROLEE
129                  std::shared_ptr< ProvisioningStatus > provStatus = std::make_shared<
130                         ProvisioningStatus >(ESResult::ES_OK, ESState::ES_PROVISIONING_SUCCESS);
131                 m_provStatusCb(provStatus);
132                 #endif
133
134                 triggerNetworkConnection();
135                 return;
136             }
137             else
138             {
139                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
140                         "checkProvInformationCb : Provisioning is failed ");
141                 std::shared_ptr< ProvisioningStatus > provStatus = std::make_shared<
142                         ProvisioningStatus >(ESResult::ES_ERROR, ESState::ES_PROVISIONING_ERROR);
143                 m_provStatusCb(provStatus);
144                 return;
145             }
146         }
147
148         void RemoteEnrolleeResource::getProvStatusResponse(const HeaderOptions& /*headerOptions*/,
149                 const OCRepresentation& rep, const int eCode)
150         {
151             OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "getProvStatusResponse : %s, eCode = %d",
152                     rep.getUri().c_str(),
153                     eCode);
154
155             if (eCode != 0)
156             {
157                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
158                         "getProvStatusResponse : Provisioning is failed ");
159                 std::shared_ptr< ProvisioningStatus > provStatus = std::make_shared<
160                         ProvisioningStatus >(ESResult::ES_ERROR, ESState::ES_PROVISIONING_ERROR);
161                 m_provStatusCb(provStatus);
162                 return;
163             }
164
165             int ps = -1;
166
167             rep.getValue(OC_RSRVD_ES_PS, ps);
168
169             OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "getProvStatusResponse : ps - %d",
170                     ps);
171
172             if (ps == ES_PS_NEED_PROVISIONING) //Indicates the need for provisioning
173             {
174                 OCRepresentation provisioningRepresentation;
175
176                 provisioningRepresentation.setValue(OC_RSRVD_ES_TNN,
177                 std::string(m_ProvConfig.provData.WIFI.ssid));
178                 provisioningRepresentation.setValue(OC_RSRVD_ES_CD,
179                 std::string(m_ProvConfig.provData.WIFI.pwd));
180
181                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "getProvStatusResponse : ssid - %s",
182                         m_ProvConfig.provData.WIFI.ssid);
183                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "getProvStatusResponse : pwd - %s",
184                         m_ProvConfig.provData.WIFI.pwd);
185
186                 m_ocResource->post(provisioningRepresentation, QueryParamsMap(),
187                         std::function<
188                                 void(const HeaderOptions& headerOptions,
189                                         const OCRepresentation& rep, const int eCode) >(
190                         std::bind(&RemoteEnrolleeResource::checkProvInformationCb, this,
191                         std::placeholders::_1, std::placeholders::_2,
192                         std::placeholders::_3)));
193             }
194             else if (ps == ES_PS_PROVISIONING_COMPLETED) //Indicates that provisioning is completed
195             {
196                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
197                         "getProvStatusResponse : Provisioning is successful");
198                 std::shared_ptr< ProvisioningStatus > provStatus = std::make_shared<
199                         ProvisioningStatus >(ESResult::ES_OK, ESState::ES_PROVISIONED_ALREADY);
200                 m_provStatusCb(provStatus);
201             }
202         }
203
204         void RemoteEnrolleeResource::registerProvStatusCallback(ProvStatusCb provStatusCb)
205         {
206             m_provStatusCb = provStatusCb;
207         }
208
209         ESResult RemoteEnrolleeResource::ESDiscoveryTimeout(unsigned short waittime)
210         {
211             struct timespec startTime;
212             startTime.tv_sec=0;
213             startTime.tv_sec=0;
214             struct timespec currTime;
215             currTime.tv_sec=0;
216             currTime.tv_nsec=0;
217
218             ESResult res = ES_OK;
219             #ifdef _POSIX_MONOTONIC_CLOCK
220                 int clock_res = clock_gettime(CLOCK_MONOTONIC, &startTime);
221             #else
222                 int clock_res = clock_gettime(CLOCK_REALTIME, &startTime);
223             #endif
224
225             if (0 != clock_res)
226             {
227                 return ES_ERROR;
228             }
229
230             while (ES_OK == res || m_discoveryResponse == false)
231             {
232                 #ifdef _POSIX_MONOTONIC_CLOCK
233                         clock_res = clock_gettime(CLOCK_MONOTONIC, &currTime);
234                 #else
235                         clock_res = clock_gettime(CLOCK_REALTIME, &currTime);
236                 #endif
237
238                 if (0 != clock_res)
239                 {
240                     return ES_ERROR;
241                 }
242                 long elapsed = (currTime.tv_sec - startTime.tv_sec);
243                 if (elapsed > waittime)
244                 {
245                     return ES_OK;
246                 }
247                 if (m_discoveryResponse)
248                 {
249                     res = ES_OK;
250                 }
251              }
252              return res;
253         }
254
255         void RemoteEnrolleeResource::onDeviceDiscovered(std::shared_ptr<OC::OCResource> resource)
256         {
257             OIC_LOG (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "onDeviceDiscovered");
258
259             std::string resourceURI;
260             std::string hostAddress;
261             try
262             {
263                 if(resource)
264                 {
265                     // Get the resource URI
266                     resourceURI = resource->uri();
267                     OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
268                             "URI of the resource: %s", resourceURI.c_str());
269
270                     // Get the resource host address
271                     hostAddress = resource->host();
272                     OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
273                             "Host address of the resource: %s", hostAddress.c_str());
274
275                     /*
276                      * Easysetup is always performed with a single Enrollee device and
277                      * in a private network (SoftAP or BLE), so the assumption is that
278                      * only the intended device will respond for the discovery.
279                      * With the above assumption the below two statements are written.
280                      */
281                     m_ocResource = resource;
282                     m_discoveryResponse = true;
283                 }
284                 else
285                 {
286                     OIC_LOG (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "Resource is invalid");
287                 }
288
289             }
290             catch(std::exception& e)
291             {
292                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
293                         "Exception in foundResource: %s", e.what());
294             }
295         }
296
297
298         ESResult RemoteEnrolleeResource::constructResourceObject()
299         {
300             if (m_ocResource != nullptr)
301             {
302                 throw ESBadRequestException("Remote resource is already created");
303             }
304
305 #ifdef REMOTE_ARDUINO_ENROLEE
306             //This process will create OCResource with port 55555 which is specific
307             // to Arduino WiFi enrollee
308             try
309             {
310
311                 std::vector< std::string > interface =
312                 {   DEFAULT_INTERFACE};
313                 std::vector< std::string > resTypes =
314                 {   ES_PROV_RES_TYPE};
315
316                 OIC_LOG(DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "Before OCPlatform::constructResourceObject");
317
318                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "m_host = %s",
319                         m_wifiOnboardingconn.ipAddress);
320                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "ES_PROV_RES_URI = %s", ES_PROV_RES_URI);
321                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "m_connectivityType = %d",
322                         m_ProvConfig.connType);
323                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "resTypes = %s",
324                         resTypes.at(0).c_str());
325                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "interface = %s", interface.at(0).c_str());
326
327                 std::string host;
328                 if(m_wifiOnboardingconn.isSecured)
329                 {
330                     host.append("coaps://");
331                 }
332                 else
333                 {
334                     host.append("coap://");
335                 }
336
337                 if(m_ProvConfig.connType == CT_ADAPTER_IP)
338                 {
339                     // TODO : RemoteEnrollee is current handling easysetup on IP transport.
340                     // WiFiRemoteEnrollee need to extend RemoteEnrollee for providing IP specific
341                     // Enrollee easysetup.
342
343                     host.append(m_wifiOnboardingconn.ipAddress);
344                     //TODO : If the target Enrollee is not a Arduino Wi-Fi device,
345                     // then the port number will be found during resource discovery instead of
346                     // using 55555
347                     host.append(":55555");
348                 }
349
350                 OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "HOST = %s", host.c_str());
351
352                 m_ocResource = OC::OCPlatform::constructResourceObject(host,
353                         ES_PROV_RES_URI,
354                         m_ProvConfig.connType,
355                         true,
356                         resTypes,
357                         interface);
358                 OIC_LOG_V(DEBUG, ES_REMOTE_ENROLLEE_RES_TAG,
359                         "created OCResource : %s", m_ocResource->uri().c_str());
360
361                 return ES_OK;
362             }
363             catch (OCException & e)
364             {
365                 OIC_LOG_V(ERROR, ES_REMOTE_ENROLLEE_RES_TAG,
366                         "Exception for constructResourceObject : %s", e.reason().c_str());
367                 return ES_ERROR;
368             }
369 #else
370             std::string host("");
371             std::string query("");
372
373             if (m_wifiOnboardingconn.isSecured)
374             {
375                 host.append("coaps://");
376             }
377             else
378             {
379                 host.append("coap://");
380             }
381
382             if (m_ProvConfig.connType == CT_ADAPTER_IP)
383             {
384                 // TODO : RemoteEnrollee is current handling easysetup on IP transport.
385                 // WiFiRemoteEnrollee need to extend RemoteEnrollee for providing IP specific
386                 // Enrollee easysetup.
387
388                 host.append(m_wifiOnboardingconn.ipAddress);
389             }
390
391             query.append(ES_BASE_RES_URI);
392             query.append("?rt=");
393             query.append(ES_PROV_RES_TYPE);
394
395             OIC_LOG(DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "Before OCPlatform::constructResourceObject");
396
397             OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "host = %s",
398                     host.c_str());
399             OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "query = %s", query.c_str());
400             OIC_LOG_V (DEBUG, ES_REMOTE_ENROLLEE_RES_TAG, "m_connectivityType = %d",
401                     m_ProvConfig.connType);
402
403             m_discoveryResponse = false;
404             std::function< void (std::shared_ptr<OC::OCResource>) > onDeviceDiscoveredCb =
405                     std::bind(&RemoteEnrolleeResource::onDeviceDiscovered, this,
406                                                     std::placeholders::_1);
407             OCStackResult result = OC::OCPlatform::findResource("", query, CT_DEFAULT,
408                     onDeviceDiscoveredCb);
409
410             if (result != OCStackResult::OC_STACK_OK)
411             {
412                 OIC_LOG(ERROR,ES_REMOTE_ENROLLEE_RES_TAG,
413                         "Failed to create device using constructResourceObject");
414                 return ES_ERROR;
415             }
416
417
418             ESResult foundResponse = ESDiscoveryTimeout (DISCOVERY_TIMEOUT);
419
420             if (foundResponse ==ES_ERROR || !m_discoveryResponse)
421             {
422                 OIC_LOG(ERROR,ES_REMOTE_ENROLLEE_RES_TAG,
423                         "Failed to create device using constructResourceObject");
424                 return ES_ERROR;
425             }
426
427             return ES_OK;
428 #endif
429         }
430
431         void RemoteEnrolleeResource::provisionEnrollee()
432
433         {
434             if (m_ocResource == nullptr)
435             {
436                 throw ESBadRequestException("Resource is not initialized");
437             }
438
439             OC::QueryParamsMap query;
440             OC::OCRepresentation rep;
441
442             std::function< OCStackResult(void) > getProvisioingStatus = [&]
443             {   return m_ocResource->get(m_ocResource->getResourceTypes().at(0),
444                         m_ocResource->getResourceInterfaces().at(0), query,
445                         std::function<
446                         void(const HeaderOptions& headerOptions, const OCRepresentation& rep,
447                                 const int eCode) >(
448                                 std::bind(&RemoteEnrolleeResource::getProvStatusResponse, this,
449                                         std::placeholders::_1, std::placeholders::_2,
450                                         std::placeholders::_3)));
451             };
452
453             OCStackResult result = getProvisioingStatus();
454
455             if (result != OCStackResult::OC_STACK_OK)
456             {
457                 std::shared_ptr< ProvisioningStatus > provStatus = std::make_shared<
458                         ProvisioningStatus >(ESResult::ES_ERROR, ESState::ES_PROVISIONING_ERROR);
459                 m_provStatusCb(provStatus);
460                 return;
461             }
462         }
463
464         void RemoteEnrolleeResource::unprovisionEnrollee()
465         {
466             if (m_ocResource == nullptr)
467             {
468                 throw ESBadRequestException("Resource is not initialized");
469             }
470
471             OCRepresentation provisioningRepresentation;
472
473             provisioningRepresentation.setValue(OC_RSRVD_ES_TNN, "");
474             provisioningRepresentation.setValue(OC_RSRVD_ES_CD, "");
475
476             m_ocResource->post(provisioningRepresentation, QueryParamsMap(),
477                     std::function<
478                             void(const HeaderOptions& headerOptions, const OCRepresentation& rep,
479                                     const int eCode) >(
480                     std::bind(&RemoteEnrolleeResource::checkProvInformationCb, this,
481                     std::placeholders::_1, std::placeholders::_2,
482                     std::placeholders::_3)));
483         }
484     }
485 }