Merge "[CA Integration] Updated C sample apps to accept connectivity type param"...
[platform/upstream/iotivity.git] / resource / csdk / stack / samples / linux / SimpleClientServer / 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 //      http://www.apache.org/licenses/LICENSE-2.0
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <signal.h>
24 #include <unistd.h>
25
26 #include <set>
27 #include <string>
28 #include <sstream>
29 #include <iostream>
30 #include <algorithm>
31
32 #include "ocstack.h"
33 #include "logger.h"
34 #include "occlientbasicops.h"
35
36 static int UNICAST_DISCOVERY = 0;
37 static int TEST_CASE = 0;
38
39 static const char * TEST_APP_UNICAST_DISCOVERY_QUERY = "coap://0.0.0.0:5683/oc/core";
40
41 static std::string putPayload = "{\"state\":\"off\",\"power\":10}";
42
43 static std::string coapServerIP = "255.255.255.255";
44 static std::string coapServerPort = "5683";
45 static std::string coapServerResource = "/a/led";
46
47 int gQuitFlag = 0;
48
49 namespace {
50     typedef std::pair<bool, std::string>    extract_result_t;
51     typedef std::string                     sid_t;
52     typedef std::set<std::string>           SID_set_t;
53 } // namespace
54
55 void collateSIDs(const OCClientResponse * clientResponse, const std::string& server_ip);
56
57 /* SIGINT handler: set gQuitFlag to 1 for graceful termination */
58 void handleSigInt(int signum)
59 {
60     if (signum == SIGINT)
61     {
62         gQuitFlag = 1;
63     }
64 }
65
66 static void PrintUsage()
67 {
68     OC_LOG(INFO, TAG, "Usage : occlient -u <0|1> -t <1|2|3>");
69     OC_LOG(INFO, TAG, "-u <0|1> : Perform multicast/unicast discovery of resources");
70     OC_LOG(INFO, TAG, "-t 1 : Discover Resources");
71     OC_LOG(INFO, TAG, "-t 2 : Discover Resources and"
72             " Initiate Nonconfirmable Get/Put/Post Requests");
73     OC_LOG(INFO, TAG, "-t 3 : Discover Resources and Initiate Confirmable Get/Put/Post Requests");
74 }
75
76 OCStackResult InvokeOCDoResource(std::ostringstream &query,
77         OCMethod method, OCQualityOfService qos,
78         OCClientResponseHandler cb, OCHeaderOption * options, uint8_t numOptions)
79 {
80     OCStackResult ret;
81     OCCallbackData cbData;
82     OCDoHandle handle;
83
84     cbData.cb = cb;
85     cbData.context = (void*)DEFAULT_CONTEXT_VALUE;
86     cbData.cd = NULL;
87
88 #ifdef CA_INT
89     // TODO-CA: The adapter type is set to WiFi but should be configurable - add as API param
90     ret = OCDoResource(&handle, method, query.str().c_str(), 0,
91             (method == OC_REST_PUT || method == OC_REST_POST) ? putPayload.c_str() : NULL,
92             OC_WIFI, qos, &cbData, options, numOptions);
93 #else
94     ret = OCDoResource(&handle, method, query.str().c_str(), 0,
95             (method == OC_REST_PUT || method == OC_REST_POST) ? putPayload.c_str() : NULL,
96             qos, &cbData, options, numOptions);
97 #endif
98
99     if (ret != OC_STACK_OK)
100     {
101         OC_LOG_V(ERROR, TAG, "OCDoResource returns error %d with method %d", ret, method);
102     }
103
104     return ret;
105 }
106
107 OCStackApplicationResult putReqCB(void* ctx, OCDoHandle handle, OCClientResponse * clientResponse)
108 {
109     if(ctx == (void*)DEFAULT_CONTEXT_VALUE)
110     {
111         OC_LOG(INFO, TAG, "Callback Context for PUT recvd successfully");
112     }
113
114     if(clientResponse)
115     {
116         OC_LOG_V(INFO, TAG, "StackResult: %s",  getResult(clientResponse->result));
117         OC_LOG_V(INFO, TAG, "JSON = %s =============> Put Response", clientResponse->resJSONPayload);
118     }
119     return OC_STACK_DELETE_TRANSACTION;
120 }
121
122 OCStackApplicationResult postReqCB(void *ctx, OCDoHandle handle, OCClientResponse *clientResponse)
123 {
124     if(ctx == (void*)DEFAULT_CONTEXT_VALUE)
125     {
126         OC_LOG(INFO, TAG, "Callback Context for POST recvd successfully");
127     }
128
129     if(clientResponse)
130     {
131         OC_LOG_V(INFO, TAG, "StackResult: %s",  getResult(clientResponse->result));
132         OC_LOG_V(INFO, TAG, "JSON = %s =============> Post Response",
133                 clientResponse->resJSONPayload);
134     }
135     return OC_STACK_DELETE_TRANSACTION;
136 }
137
138 OCStackApplicationResult getReqCB(void* ctx, OCDoHandle handle, OCClientResponse * clientResponse)
139 {
140     if(ctx == (void*)DEFAULT_CONTEXT_VALUE)
141     {
142         OC_LOG(INFO, TAG, "Callback Context for GET query recvd successfully");
143     }
144
145     if(clientResponse)
146     {
147         OC_LOG_V(INFO, TAG, "StackResult: %s",  getResult(clientResponse->result));
148         OC_LOG_V(INFO, TAG, "SEQUENCE NUMBER: %d", clientResponse->sequenceNumber);
149         OC_LOG_V(INFO, TAG, "JSON = %s =============> Get Response",
150                 clientResponse->resJSONPayload);
151     }
152     if(clientResponse->rcvdVendorSpecificHeaderOptions &&
153             clientResponse->numRcvdVendorSpecificHeaderOptions)
154     {
155         OC_LOG (INFO, TAG, "Received vendor specific options");
156         uint8_t i = 0;
157         OCHeaderOption * rcvdOptions = clientResponse->rcvdVendorSpecificHeaderOptions;
158         for( i = 0; i < clientResponse->numRcvdVendorSpecificHeaderOptions; i++)
159         {
160             if(((OCHeaderOption)rcvdOptions[i]).protocolID == OC_COAP_ID)
161             {
162                 OC_LOG_V(INFO, TAG, "Received option with OC_COAP_ID and ID %u with",
163                         ((OCHeaderOption)rcvdOptions[i]).optionID );
164                 OC_LOG_BUFFER(INFO, TAG, ((OCHeaderOption)rcvdOptions[i]).optionData,
165                         ((OCHeaderOption)rcvdOptions[i]).optionLength);
166             }
167         }
168     }
169     return OC_STACK_DELETE_TRANSACTION;
170 }
171
172 // This is a function called back when a device is discovered
173 OCStackApplicationResult discoveryReqCB(void* ctx, OCDoHandle handle,
174         OCClientResponse * clientResponse)
175 {
176     uint8_t remoteIpAddr[4];
177     uint16_t remotePortNu;
178
179     if (ctx == (void*)DEFAULT_CONTEXT_VALUE)
180     {
181         OC_LOG(INFO, TAG, "Callback Context for DISCOVER query recvd successfully");
182     }
183
184     if (clientResponse)
185     {
186         OC_LOG_V(INFO, TAG, "StackResult: %s", getResult(clientResponse->result));
187
188         OCDevAddrToIPv4Addr((OCDevAddr *) clientResponse->addr, remoteIpAddr,
189                 remoteIpAddr + 1, remoteIpAddr + 2, remoteIpAddr + 3);
190         OCDevAddrToPort((OCDevAddr *) clientResponse->addr, &remotePortNu);
191
192         OC_LOG_V(INFO, TAG,
193                 "Device =============> Discovered %s @ %d.%d.%d.%d:%d",
194                 clientResponse->resJSONPayload, remoteIpAddr[0], remoteIpAddr[1],
195                 remoteIpAddr[2], remoteIpAddr[3], remotePortNu);
196
197         parseClientResponse(clientResponse);
198
199         collateSIDs(clientResponse, getIPAddrTBServer(clientResponse));
200
201         switch(TEST_CASE)
202         {
203             case TEST_NON_CON_OP:
204                 InitGetRequest(OC_LOW_QOS);
205                 InitPutRequest();
206                 InitPostRequest(OC_LOW_QOS);
207                 break;
208             case TEST_CON_OP:
209                 InitGetRequest(OC_HIGH_QOS);
210                 InitPutRequest();
211                 InitPostRequest(OC_HIGH_QOS);
212                 break;
213             default:
214                 PrintUsage();
215                 break;
216         }
217     }
218
219     return (UNICAST_DISCOVERY) ? OC_STACK_DELETE_TRANSACTION : OC_STACK_KEEP_TRANSACTION ;
220 }
221
222 int InitPutRequest()
223 {
224     OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
225     std::ostringstream query;
226     query << "coap://" << coapServerIP << ":" << coapServerPort << coapServerResource;
227     return (InvokeOCDoResource(query, OC_REST_PUT, OC_LOW_QOS, putReqCB, NULL, 0));
228 }
229
230 int InitPostRequest(OCQualityOfService qos)
231 {
232     OCStackResult result;
233     OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
234     std::ostringstream query;
235     query << "coap://" << coapServerIP << ":" << coapServerPort << coapServerResource;
236
237     // First POST operation (to create an LED instance)
238     result = InvokeOCDoResource(query, OC_REST_POST,
239             ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS: OC_LOW_QOS),
240             postReqCB, NULL, 0);
241     if (OC_STACK_OK != result)
242     {
243         // Error can happen if for example, network connectivity is down
244         OC_LOG(INFO, TAG, "First POST call did not succeed");
245     }
246
247     // Second POST operation (to create an LED instance)
248     result = InvokeOCDoResource(query, OC_REST_POST,
249             ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS: OC_LOW_QOS),
250             postReqCB, NULL, 0);
251     if (OC_STACK_OK != result)
252     {
253         OC_LOG(INFO, TAG, "Second POST call did not succeed");
254     }
255
256     // This POST operation will update the original resourced /a/led
257     return (InvokeOCDoResource(query, OC_REST_POST,
258                 ((qos == OC_HIGH_QOS) ? OC_HIGH_QOS: OC_LOW_QOS),
259                 postReqCB, NULL, 0));
260 }
261
262 int InitGetRequest(OCQualityOfService qos)
263 {
264     OC_LOG_V(INFO, TAG, "\n\nExecuting %s", __func__);
265     std::ostringstream query;
266     query << "coap://" << coapServerIP << ":" << coapServerPort << coapServerResource;
267
268     return (InvokeOCDoResource(query, OC_REST_GET, (qos == OC_HIGH_QOS)?
269             OC_HIGH_QOS:OC_LOW_QOS, getReqCB, NULL, 0));
270 }
271
272 int InitDiscovery()
273 {
274     OCStackResult ret;
275     OCCallbackData cbData;
276     OCDoHandle handle;
277     /* Start a discovery query*/
278     char szQueryUri[64] = { 0 };
279     if (UNICAST_DISCOVERY)
280     {
281         strcpy(szQueryUri, TEST_APP_UNICAST_DISCOVERY_QUERY);
282     }
283     else
284     {
285         strcpy(szQueryUri, OC_WELL_KNOWN_QUERY);
286     }
287     cbData.cb = discoveryReqCB;
288     cbData.context = (void*)DEFAULT_CONTEXT_VALUE;
289     cbData.cd = NULL;
290 #ifdef CA_INT
291     // TODO-CA: The adapter type is set to all but should be configurable - add as API param
292     ret = OCDoResource(&handle, OC_REST_GET, szQueryUri, 0, 0, (OC_ETHERNET | OC_WIFI | OC_LE),
293                         OC_LOW_QOS, &cbData, NULL, 0);
294 #else
295     ret = OCDoResource(&handle, OC_REST_GET, szQueryUri, 0, 0, OC_LOW_QOS, &cbData, NULL, 0);
296 #endif
297     if (ret != OC_STACK_OK)
298     {
299         OC_LOG(ERROR, TAG, "OCStack resource error");
300     }
301     return ret;
302 }
303
304 int main(int argc, char* argv[])
305 {
306     uint8_t addr[20] = {0};
307     uint8_t* paddr = NULL;
308     uint16_t port = USE_RANDOM_PORT;
309     uint8_t ifname[] = "eth0";
310     int opt;
311
312     while ((opt = getopt(argc, argv, "u:t:")) != -1)
313     {
314         switch(opt)
315         {
316             case 'u':
317                 UNICAST_DISCOVERY = atoi(optarg);
318                 break;
319             case 't':
320                 TEST_CASE = atoi(optarg);
321                 break;
322             default:
323                 PrintUsage();
324                 return -1;
325         }
326     }
327
328     if ((UNICAST_DISCOVERY != 0 && UNICAST_DISCOVERY != 1) ||
329             (TEST_CASE < TEST_DISCOVER_REQ || TEST_CASE >= MAX_TESTS) )
330     {
331         PrintUsage();
332         return -1;
333     }
334
335
336     /*Get Ip address on defined interface and initialize coap on it with random port number
337      * this port number will be used as a source port in all coap communications*/
338     if ( OCGetInterfaceAddress(ifname, sizeof(ifname), AF_INET, addr,
339                 sizeof(addr)) == ERR_SUCCESS)
340     {
341         OC_LOG_V(INFO, TAG, "Starting occlient on address %s",addr);
342         paddr = addr;
343     }
344
345     /* Initialize OCStack*/
346     if (OCInit((char *) paddr, port, OC_CLIENT) != OC_STACK_OK)
347     {
348         OC_LOG(ERROR, TAG, "OCStack init error");
349         return 0;
350     }
351
352     InitDiscovery();
353
354     // Break from loop with Ctrl+C
355     signal(SIGINT, handleSigInt);
356
357     while (!gQuitFlag)
358     {
359         if (OCProcess() != OC_STACK_OK)
360         {
361             OC_LOG(ERROR, TAG, "OCStack process error");
362             return 0;
363         }
364
365         sleep(2);
366     }
367     OC_LOG(INFO, TAG, "Exiting occlient main loop...");
368
369     if (OCStop() != OC_STACK_OK)
370     {
371         OC_LOG(ERROR, TAG, "OCStack stop error");
372     }
373
374     return 0;
375 }
376
377 std::string getIPAddrTBServer(OCClientResponse * clientResponse)
378 {
379     if(!clientResponse) return "";
380     if(!clientResponse->addr) return "";
381     uint8_t a, b, c, d = 0;
382     if(0 != OCDevAddrToIPv4Addr(clientResponse->addr, &a, &b, &c, &d) ) return "";
383
384     char ipaddr[16] = {'\0'};
385     // ostringstream not working correctly here, hence snprintf
386     snprintf(ipaddr,  sizeof(ipaddr), "%d.%d.%d.%d", a,b,c,d);
387     return std::string (ipaddr);
388 }
389
390
391 std::string getPortTBServer(OCClientResponse * clientResponse)
392 {
393     if(!clientResponse) return "";
394     if(!clientResponse->addr) return "";
395     uint16_t p = 0;
396     if(0 != OCDevAddrToPort(clientResponse->addr, &p) ) return "";
397     std::ostringstream ss;
398     ss << p;
399     return ss.str();
400 }
401
402 std::string getQueryStrForGetPut(OCClientResponse * clientResponse)
403 {
404     return "/a/led";
405 }
406
407
408 /* You could do this with the JSON parser of your choice, a regular expression, PEG
409 grammar, etc.. This "sample" version does not handle cases like escaping strings,
410 but shows a simple way you might approach the task with just the C++98 library: */
411 extract_result_t extract_value(const std::string& search_key, const std::string& input)
412 {
413     const std::string key('\"' + search_key + "\":\"");
414
415     const size_t key_mark = input.find(key);
416     const size_t key_edge = key_mark + key.length();
417     const size_t val_mark = input.find_first_of("\"", key_edge);
418
419     if(std::string::npos == key_mark || std::string::npos == val_mark) {
420        std::ostringstream os;
421
422        os << "extract_value(): \"" << search_key << "\" not found in input";
423        OC_LOG(ERROR, TAG, os.str().c_str());
424
425        return std::make_pair(false, "");
426      }
427
428     return std::make_pair(true, input.substr(key_edge, val_mark - key_edge));
429 }
430
431 extract_result_t parseSID(const OCClientResponse * const clientResponse)
432 {
433     const char* const& pl_in = reinterpret_cast<const char *>(clientResponse->resJSONPayload);
434
435     const std::string pl(pl_in, strlen(pl_in));
436
437     const extract_result_t sid = extract_value("sid", pl);
438     const extract_result_t uri = extract_value("href", pl);
439     //TODO-CA: It should be just enough to send the SID alone and not the combination
440     // of it.
441     return std::make_pair(sid.first and uri.first, sid.second + ':' + uri.second);
442 }
443
444 void parseClientResponse(OCClientResponse * clientResponse)
445 {
446     coapServerIP = getIPAddrTBServer(clientResponse);
447     coapServerPort = getPortTBServer(clientResponse);
448     coapServerResource = getQueryStrForGetPut(clientResponse);
449 }
450
451 void collateSIDs(const OCClientResponse * clientResponse, const std::string& server_ip)
452 {
453     static SID_set_t sids;
454
455     const extract_result_t sid_result = parseSID(clientResponse);
456
457     if(false == sid_result.first)
458      return;
459
460     const sid_t& sid = sid_result.second;
461
462     /* ...there's no need for an application to take any special action, but we can tell
463     if we've seen a resource before, regardless of the transport it arrive on: */
464     std::ostringstream msg;
465
466     if(not sids.insert(sid).second)
467      msg << "SID " << sid << " has been seen before.\n";
468     else
469      msg << "SID " << sid << " is new.\n";
470
471     OC_LOG(INFO, TAG, msg.str().c_str());
472 }