examples: Add notice about using experimental API
[iotivity.git] / resource / csdk / stack / samples / linux / secure / occlientbasicops.cpp
1 //******************************************************************
2 //
3 // Copyright 2014 Intel Mobile Communications GmbH 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 "iotivity_config.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <signal.h>
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #ifdef HAVE_WINDOWS_H
30 #include <windows.h>
31 /** @todo stop-gap for naming issue. Windows.h does not like us to use ERROR */
32 #ifdef ERROR
33 #undef ERROR
34 #endif
35 #endif
36 #include <iostream>
37 #include <sstream>
38 #include <getopt.h>
39 #include "ocstack.h"
40 #include "occlientbasicops.h"
41 #include "ocpayload.h"
42 #include "oic_string.h"
43 #include "common.h"
44
45 /// This example is using experimental API, so there is no guarantee of support for future release,
46 /// nor any there any guarantee that breaking changes will not occur across releases.
47 #include "experimental/logger.h"
48 #include "experimental/payload_logging.h"
49
50 #define TAG "occlientbasicops"
51 static int UnicastDiscovery = 0;
52 static int TestCase = 0;
53 static int ConnType = 0;
54 static int DevOwner = 0;
55 static int WithTcp = 0;
56
57 static char DISCOVERY_QUERY[] = "%s/oic/res";
58 OCConnectivityType discoveryReqConnType = CT_ADAPTER_IP;
59
60 static std::string coapServerResource;
61 static int coapSecureResource;
62 static OCConnectivityType ocConnType;
63
64 //Secure Virtual Resource database for Iotivity Client application
65 //It contains Client's Identity and the PSK credentials
66 //of other devices which the client trusts
67 static char CRED_FILE_DEVOWNER[] = "oic_svr_db_client_devowner.dat";
68 static char CRED_FILE_NONDEVOWNER[] = "oic_svr_db_client_nondevowner.dat";
69
70 //Standard uri prefix for secure virtual resources
71 const char * OIC_STD_URI_PREFIX = "/oic/";
72
73 const char * COAPS_STR = "coaps";
74
75 #ifdef __WITH_TLS__
76 const char * COAPS_TCP_STR = "coaps+tcp";
77 #endif
78
79 int gQuitFlag = 0;
80 static const char *gResourceUri = "/a/led";
81
82 /* SIGINT handler: set gQuitFlag to 1 for graceful termination */
83 void handleSigInt(int signum)
84 {
85     if (signum == SIGINT)
86     {
87         gQuitFlag = 1;
88     }
89 }
90
91 OCPayload *putPayload()
92 {
93     OCRepPayload *payload = OCRepPayloadCreate();
94
95     if (!payload)
96     {
97         std::cout << "Failed to create put payload object" << std::endl;
98         std::exit(1);
99     }
100
101     OCRepPayloadSetPropInt(payload, "power", 15);
102     OCRepPayloadSetPropBool(payload, "state", true);
103
104     return (OCPayload *) payload;
105 }
106
107 static void PrintUsage()
108 {
109     OIC_LOG(INFO, TAG, "Usage : occlient -u <0|1> -t <1|2|3> -c <0|1>");
110     OIC_LOG(INFO, TAG, "-u <0|1> : Perform multicast/unicast discovery of resources");
111     OIC_LOG(INFO, TAG, "-t 1 : Discover Resources");
112     OIC_LOG(INFO, TAG, "-t 2 : Discover Resources and"
113             " Initiate Nonconfirmable Get/Put/Post Requests");
114     OIC_LOG(INFO, TAG, "-t 3 : Discover Resources and Initiate Confirmable Get/Put/Post Requests");
115     OIC_LOG(INFO, TAG, "-c 0 : Default auto-selection");
116     OIC_LOG(INFO, TAG, "-c 1 : IP Connectivity Type");
117     OIC_LOG(INFO, TAG, "-d 0 : Client as Non Device Owner");
118     OIC_LOG(INFO, TAG, "-d 1 : Client as Device Owner");
119     OIC_LOG(INFO, TAG, "-p 0 : Use UDP protocol");
120     OIC_LOG(INFO, TAG, "-p 1 : Use TCP protocol");
121 }
122
123 OCStackResult InvokeOCDoResource(std::ostringstream &query,
124                                  OCMethod method,
125                                  const OCDevAddr *dest,
126                                  OCQualityOfService qos,
127                                  OCClientResponseHandler cb,
128                                  OCHeaderOption *options, uint8_t numOptions)
129 {
130     OCStackResult ret;
131     OCCallbackData cbData;
132
133     cbData.cb = cb;
134     cbData.context = NULL;
135     cbData.cd = NULL;
136
137     OCPayload *payload = (method == OC_REST_PUT || method == OC_REST_POST) ? putPayload() : NULL;
138
139     ret = OCDoRequest(NULL, method, query.str().c_str(), dest,
140                       payload, ocConnType, qos, &cbData, options, numOptions);
141
142     OCPayloadDestroy(payload);
143
144     if (ret != OC_STACK_OK)
145     {
146         OIC_LOG_V(ERROR, TAG, "OCDoResource returns error %d with method %d", ret, method);
147     }
148
149     return ret;
150 }
151
152 OCStackApplicationResult putReqCB(void *, OCDoHandle, OCClientResponse *clientResponse)
153 {
154     OIC_LOG(INFO, TAG, "Callback Context for PUT recvd successfully");
155
156     if (clientResponse)
157     {
158         OIC_LOG_V(INFO, TAG, "StackResult: %s",  getResult(clientResponse->result));
159         OIC_LOG_PAYLOAD(INFO, clientResponse->payload);
160         OIC_LOG(INFO, TAG, "=============> Put Response");
161     }
162     return OC_STACK_DELETE_TRANSACTION;
163 }
164
165 OCStackApplicationResult postReqCB(void *, OCDoHandle, OCClientResponse *clientResponse)
166 {
167     OIC_LOG(INFO, TAG, "Callback Context for POST recvd successfully");
168
169     if (clientResponse)
170     {
171         OIC_LOG_V(INFO, TAG, "StackResult: %s",  getResult(clientResponse->result));
172         OIC_LOG_PAYLOAD(INFO, clientResponse->payload);
173         OIC_LOG(INFO, TAG, "=============> Post Response");
174     }
175     return OC_STACK_DELETE_TRANSACTION;
176 }
177
178 OCStackApplicationResult getReqCB(void *, OCDoHandle, OCClientResponse *clientResponse)
179 {
180     OIC_LOG(INFO, TAG, "Callback Context for GET query recvd successfully");
181
182     if (clientResponse)
183     {
184         OIC_LOG_V(INFO, TAG, "StackResult: %s",  getResult(clientResponse->result));
185         OIC_LOG_V(INFO, TAG, "SEQUENCE NUMBER: %d", clientResponse->sequenceNumber);
186         OIC_LOG_PAYLOAD(INFO, clientResponse->payload);
187         OIC_LOG(INFO, TAG, "=============> Get Response");
188     }
189     return OC_STACK_DELETE_TRANSACTION;
190 }
191
192 // This is a function called back when a device is discovered
193 OCStackApplicationResult discoveryReqCB(void *, OCDoHandle,
194                                         OCClientResponse *clientResponse)
195 {
196     OIC_LOG(INFO, TAG, "Callback Context for DISCOVER query recvd successfully");
197
198     if (clientResponse)
199     {
200         OIC_LOG_V(INFO, TAG, "StackResult: %s", getResult(clientResponse->result));
201         OIC_LOG_V(INFO, TAG,
202                   "Device =============> Discovered @ %s:%d",
203                   clientResponse->devAddr.addr,
204                   clientResponse->devAddr.port);
205
206         if (clientResponse->result == OC_STACK_OK)
207         {
208             OIC_LOG_PAYLOAD(INFO, clientResponse->payload);
209
210             ocConnType = clientResponse->connType;
211
212             if (parseClientResponse(clientResponse) != -1)
213             {
214                 OCDiscoveryPayload *payload = (OCDiscoveryPayload *) clientResponse->payload;
215                 OCResourcePayload *resource = (OCResourcePayload *) payload->resources;
216                 for (;resource; resource = resource->next)
217                 {
218                     if ((0 == strcmp(gResourceUri, resource->uri))
219                          && (0 == strcmp(COAPS_STR, resource->eps->tps)))
220                     {
221                         OCDevAddr* endpoint = &clientResponse->devAddr;
222                         strcpy(endpoint->addr, resource->eps->addr);
223                         endpoint->port = resource->eps->port;
224                         endpoint->flags = resource->eps->family;
225
226                         switch (TestCase)
227                         {
228                         case TEST_NON_CON_OP:
229                             InitGetRequest(endpoint, OC_LOW_QOS);
230                             InitPutRequest(endpoint, OC_LOW_QOS);
231                             InitPostRequest(endpoint, OC_LOW_QOS);
232                             break;
233                         case TEST_CON_OP:
234                             InitGetRequest(endpoint, OC_HIGH_QOS);
235                             InitPutRequest(endpoint, OC_HIGH_QOS);
236                             InitPostRequest(endpoint, OC_HIGH_QOS);
237                         break;
238                         }
239                     }
240                 }
241             }
242         }
243     }
244
245     return (UnicastDiscovery) ? OC_STACK_DELETE_TRANSACTION : OC_STACK_KEEP_TRANSACTION ;
246
247 }
248 int InitPutRequest(OCDevAddr *endpoint, OCQualityOfService qos)
249 {
250     OIC_LOG_V(INFO, TAG, "Executing %s", __func__);
251     std::ostringstream query;
252     query << coapServerResource;
253
254     return (InvokeOCDoResource(query, OC_REST_PUT, endpoint,
255                                ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS : OC_LOW_QOS), putReqCB, NULL, 0));
256 }
257
258 int InitPostRequest(OCDevAddr *endpoint, OCQualityOfService qos)
259 {
260     OCStackResult result;
261
262     OIC_LOG_V(INFO, TAG, "Executing %s", __func__);
263     std::ostringstream query;
264     query << coapServerResource;
265
266     // First PUT operation (to create an LED instance)
267     result = InvokeOCDoResource(query, OC_REST_PUT, endpoint,
268                                 ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS : OC_LOW_QOS),
269                                 putReqCB, NULL, 0);
270     if (OC_STACK_OK != result)
271     {
272         // Error can happen if for example, network connectivity is down
273         OIC_LOG(INFO, TAG, "First POST call did not succeed");
274     }
275
276     // Second PUT operation (to create an LED instance)
277     result = InvokeOCDoResource(query, OC_REST_PUT, endpoint,
278                                 ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS : OC_LOW_QOS),
279                                 putReqCB, NULL, 0);
280     if (OC_STACK_OK != result)
281     {
282         OIC_LOG(INFO, TAG, "Second POST call did not succeed");
283     }
284
285     // This POST operation will update the original resourced /a/led (as long as
286     // the server is set to max 2 /lcd resources)
287     result = InvokeOCDoResource(query, OC_REST_POST, endpoint,
288                                 ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS : OC_LOW_QOS),
289                                 postReqCB, NULL, 0);
290     if (OC_STACK_OK != result)
291     {
292         OIC_LOG(INFO, TAG, "Third POST call did not succeed");
293     }
294     return result;
295 }
296
297 int InitGetRequest(OCDevAddr *endpoint, OCQualityOfService qos)
298 {
299     OIC_LOG_V(INFO, TAG, "Executing %s", __func__);
300     std::ostringstream query;
301     query << coapServerResource;
302
303     return (InvokeOCDoResource(query, OC_REST_GET, endpoint,
304                                ((qos == OC_HIGH_QOS) ?  OC_HIGH_QOS : OC_LOW_QOS),
305                                getReqCB, NULL, 0));
306 }
307
308 int InitDiscovery()
309 {
310     OCStackResult ret;
311     OCCallbackData cbData;
312     char queryUri[200];
313     char ipaddr[100] = { '\0' };
314
315     if (UnicastDiscovery)
316     {
317         OIC_LOG(INFO, TAG, "Enter IP address (with optional port) of the Server hosting resource\n");
318         OIC_LOG(INFO, TAG, "IPv4: 192.168.0.15:45454\n");
319         OIC_LOG(INFO, TAG, "IPv6: [fe80::20c:29ff:fe1b:9c5]:45454\n");
320
321         if (fgets(ipaddr, sizeof (ipaddr), stdin))
322         {
323             StripNewLineChar(ipaddr); //Strip newline char from ipaddr
324         }
325         else
326         {
327             OIC_LOG(ERROR, TAG, "!! Bad input for IP address. !!");
328             return OC_STACK_INVALID_PARAM;
329         }
330     }
331     snprintf(queryUri, sizeof (queryUri), DISCOVERY_QUERY, ipaddr);
332
333     cbData.cb = discoveryReqCB;
334     cbData.context = NULL;
335     cbData.cd = NULL;
336
337     /* Start a discovery query*/
338     OIC_LOG_V(INFO, TAG, "Initiating %s Resource Discovery : %s\n",
339               (UnicastDiscovery) ? "Unicast" : "Multicast",
340               queryUri);
341
342     ret = OCDoRequest(NULL, OC_REST_DISCOVER, queryUri, 0, 0, CT_DEFAULT,
343                       OC_LOW_QOS, &cbData, NULL, 0);
344     if (ret != OC_STACK_OK)
345     {
346         OIC_LOG(ERROR, TAG, "OCStack resource error");
347     }
348     return ret;
349 }
350
351 FILE *client_fopen_devowner(const char *path, const char *mode)
352 {
353     if (0 == strcmp(path, OC_SECURITY_DB_DAT_FILE_NAME))
354     {
355         return fopen(CRED_FILE_DEVOWNER, mode);
356     }
357     else
358     {
359         return fopen(path, mode);
360     }
361 }
362
363 FILE *client_fopen_nondevowner(const char *path, const char *mode)
364 {
365     if (0 == strcmp(path, OC_SECURITY_DB_DAT_FILE_NAME))
366     {
367         return fopen(CRED_FILE_NONDEVOWNER, mode);
368     }
369     else
370     {
371         return fopen(path, mode);
372     }
373 }
374 int main(int argc, char *argv[])
375 {
376     int opt;
377     struct timespec timeout;
378     OCPersistentStorage ps;
379
380     while ((opt = getopt(argc, argv, "u:t:c:d:p:")) != -1)
381     {
382         switch (opt)
383         {
384             case 'u':
385                 UnicastDiscovery = atoi(optarg);
386                 break;
387             case 't':
388                 TestCase = atoi(optarg);
389                 break;
390             case 'c':
391                 ConnType = atoi(optarg);
392                 break;
393             case 'd':
394                 DevOwner = atoi(optarg);
395                 break;
396             case 'p':
397                 {
398                     WithTcp = atoi(optarg);
399                     if (WithTcp > 1)
400                     {
401                         PrintUsage();
402                         return -1;
403                     }
404                 }
405                 break;
406             default:
407                 PrintUsage();
408                 return -1;
409         }
410     }
411
412     if ((UnicastDiscovery != 0 && UnicastDiscovery != 1) ||
413         (TestCase < TEST_DISCOVER_REQ || TestCase >= MAX_TESTS) ||
414         (ConnType < CT_ADAPTER_DEFAULT || ConnType >= MAX_CT))
415     {
416         PrintUsage();
417         return -1;
418     }
419
420
421     if (ConnType == CT_ADAPTER_DEFAULT || ConnType ==  CT_IP)
422     {
423         discoveryReqConnType = CT_DEFAULT;
424     }
425     else
426     {
427         OIC_LOG(INFO, TAG, "Using Default Connectivity type");
428         PrintUsage();
429     }
430
431
432     // Initialize Persistent Storage for SVR database
433     if (DevOwner)
434         ps = { client_fopen_devowner, fread, fwrite, fclose, unlink };
435     else
436         ps = { client_fopen_nondevowner, fread, fwrite, fclose, unlink };
437     OCRegisterPersistentStorageHandler(&ps);
438
439     /* Initialize OCStack*/
440     if (OCInit(NULL, 0, OC_CLIENT_SERVER) != OC_STACK_OK)
441     {
442         OIC_LOG(ERROR, TAG, "OCStack init error");
443         return 0;
444     }
445
446     InitDiscovery();
447
448     timeout.tv_sec  = 0;
449     timeout.tv_nsec = 100000000L;
450
451     // Break from loop with Ctrl+C
452     OIC_LOG(INFO, TAG, "Entering occlient main loop...");
453     signal(SIGINT, handleSigInt);
454     while (!gQuitFlag)
455     {
456         if (OCProcess() != OC_STACK_OK)
457         {
458             OIC_LOG(ERROR, TAG, "OCStack process error");
459             return 0;
460         }
461
462         nanosleep(&timeout, NULL);
463     }
464     OIC_LOG(INFO, TAG, "Exiting occlient main loop...");
465
466     if (OCStop() != OC_STACK_OK)
467     {
468         OIC_LOG(ERROR, TAG, "OCStack stop error");
469     }
470
471     return 0;
472 }
473
474 int parseClientResponse(OCClientResponse *clientResponse)
475 {
476     OCResourcePayload *res = ((OCDiscoveryPayload *)clientResponse->payload)->resources;
477
478     // Initialize all global variables
479     coapServerResource.clear();
480     coapSecureResource = 0;
481
482     while (res)
483     {
484         coapServerResource.assign(res->uri);
485         OIC_LOG_V(INFO, TAG, "Uri -- %s", coapServerResource.c_str());
486
487         if (0 == strncmp(coapServerResource.c_str(), OIC_STD_URI_PREFIX, strlen(OIC_STD_URI_PREFIX)) ||
488             0 == strncmp(coapServerResource.c_str(), "/introspection", strlen("/introspection")))
489         {
490             OIC_LOG(INFO, TAG, "Skip resource");
491             res = res->next;
492             continue;
493         }
494
495         OCDevAddr *endpoint = &clientResponse->devAddr;
496         if (res && res->eps)
497         {
498             endpoint->port = 0;
499             OCEndpointPayload* eps = res->eps;
500             while (NULL != eps)
501             {
502                 if (eps->family & OC_FLAG_SECURE)
503                 {
504 #ifdef __WITH_TLS__
505                     if (WithTcp && 0 == strcmp(eps->tps, COAPS_TCP_STR))
506                     {
507                         strncpy(endpoint->addr, eps->addr, sizeof(endpoint->addr));
508                         endpoint->port = eps->port;
509                         endpoint->flags = (OCTransportFlags)(eps->family | OC_SECURE);
510                         endpoint->adapter = OC_ADAPTER_TCP;
511                         coapSecureResource = 1;
512                         OIC_LOG_V(INFO, TAG, "TLS port: %d", endpoint->port);
513                         break;
514                     }
515 #endif
516                     if (!WithTcp && 0 == strcmp(eps->tps, COAPS_STR))
517                     {
518                         strncpy(endpoint->addr, eps->addr, sizeof(endpoint->addr));
519                         endpoint->port = eps->port;
520                         endpoint->flags = (OCTransportFlags)(eps->family | OC_SECURE);
521                         endpoint->adapter = OC_ADAPTER_IP;
522                         coapSecureResource = 1;
523                         OIC_LOG_V(INFO, TAG, "DTLS port: %d", endpoint->port);
524                     }
525                 }
526                 eps = eps->next;
527             }
528             if (!endpoint->port)
529             {
530                 OIC_LOG(INFO, TAG, "Can not find secure port information.");
531             }
532         }
533
534         //old servers support
535         if (0 == coapSecureResource && res->secure)
536         {
537 #ifdef __WITH_TLS__
538             if (WithTcp)
539             {
540                 endpoint->flags = (OCTransportFlags)(endpoint->flags | OC_SECURE);
541                 endpoint->adapter = OC_ADAPTER_TCP;
542                 endpoint->port = res->tcpPort;
543                 OIC_LOG_V(INFO, TAG, "TLS port: %d", endpoint->port);
544             }
545             else
546 #endif
547             {
548                 endpoint->port = res->port;
549                 endpoint->flags = (OCTransportFlags)(endpoint->flags | OC_SECURE);
550                 endpoint->adapter = OC_ADAPTER_IP;
551                 OIC_LOG_V(INFO, TAG, "DTLS port: %d", endpoint->port);
552             }
553             coapSecureResource = 1;
554         }
555
556         OIC_LOG_V(INFO, TAG, "Secure -- %s", coapSecureResource == 1 ? "YES" : "NO");
557
558         // If we discovered a secure resource, exit from here
559         if (coapSecureResource)
560         {
561             break;
562         }
563
564         res = res->next;
565     }
566
567     return 0;
568 }