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