Update Snapshot(2018-03-21)
[platform/upstream/iotivity.git] / examples / OICMiddle / LineInput.cpp
1 //******************************************************************
2 //
3 // Copyright 2014 Intel Corporation.
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 <map>
22 #include <iostream>
23
24 #include <stdio.h>
25
26 #include "WrapResource.h"
27 #include "LineInput.h"
28
29 #define NEED_CLIENT { if (!m_client) return LR_NoClient; }
30
31 LineInput::LineInput(MiddleClient *client)
32     : m_client(client), m_server(nullptr),
33       m_obsCB(nullptr), m_observer(nullptr)
34 {
35     m_obsCB = std::bind(&LineInput::obsCB, this,
36                         placeholders::_1,
37                         placeholders::_2,
38                         placeholders::_3,
39                         placeholders::_4,
40                         placeholders::_5);
41 }
42
43 void LineInput::setServer(MiddleServer *server) {
44     m_server = server;
45 }
46
47 int LineInput::run()
48 {
49     size_t len;
50     char *line = nullptr;
51
52     while (true) {
53         fputs(">", stdout);
54         len = 0;
55         const ssize_t n = getline(&line, &len, stdin);
56         if (n <= 0)
57             continue;
58         if (m_observer) {
59             m_observer->cancelObserve();
60             m_observer = nullptr;
61         }
62         if (line[n - 1] == '\n') {
63             if (n == 1)
64                 continue;
65             line[n - 1] = '\0';
66         }
67         stringstream result;
68         LineResult lr = processLine(line, result, m_obsCB);
69         if (lr == LR_Quit)
70             break;
71         cout << result.str();
72     }
73     free(line);
74     return true;
75 }
76
77 LineResult LineInput::processLine(string command, stringstream& result, observecb_t cb)
78 {
79     elements_t elems;
80
81     if (parseLine(command, elems) != LR_OK) {
82         cerr << "syntax error" << endl;
83         return LR_Syntax;
84     }
85     if (!elems.size())
86         return LR_NoCommand;
87
88     if (elems[0] == "quit" || elems[0] == "exit")
89         return LR_Quit;
90
91     if (elems.size() == 1) {
92         if (elems[0] == "help") {
93             return processHelp(elems, result);
94         } else if (elems[0] == "find") {
95             NEED_CLIENT return processFind(elems, result);
96         } else if (elems[0] == "show") {
97             NEED_CLIENT return processShow(elems, result);
98         }
99     } else if (elems.size() == 2) {
100         if (elems[0] == "details") {
101             NEED_CLIENT return processDetails(elems, result);
102         } else if (elems[0] == "get") {
103             NEED_CLIENT return processGet(elems, result);
104         } else if (elems[0] == "observe") {
105             NEED_CLIENT return processObserve(elems, result, cb);
106         } else if (elems[0] == "cancel") {
107             NEED_CLIENT return processCancel(elems, result);
108         }
109     } else {
110         if (elems[0] == "put") {
111             NEED_CLIENT return processPut(elems, result);
112         }
113     }
114
115     return processUnrecognized(elems, result);
116 }
117
118 LineResult LineInput::processHelp(elements_t& /*elems*/, stringstream& ss)
119 {
120     ss << "\nUsage:\n"
121                 "\tfind\t\tFind resources\n"
122                 "\tshow\t\tShow resources\n"
123                 "\tdetails n\tShow details of resource n\n"
124                 "\tget n\t\tGet value(s) of resource n\n"
125                 "\tput n v\t\tPut value(s) to resource n\n"
126                 "\tobserve n\tObserve value(s) of resource n\n"
127                 "\thelp\t\tThis usage message\n"
128                 "\nResource can be identified by Resource ID or Show index\n"
129                 "\nValue in 'put' can be key=value or key:value\n\n"
130                 ;
131     return LR_OK;
132 }
133
134 LineResult LineInput::processUnrecognized(elements_t& elems, stringstream& ss)
135 {
136     ss << "Command not recognized\n";
137     processHelp(elems, ss);
138     return LR_Unrecognized;
139 }
140
141 LineResult LineInput::processFind(elements_t& /*elems*/, stringstream& /*ss*/)
142 {
143     m_client->findResources();
144     return LR_OK;
145 }
146
147 void LineInput::registerResourceWithServer(std::string & url) {
148     string type;
149     std::size_t index = url.rfind("/");
150     if (index != std::string::npos) {
151         type = url.substr(index+1);
152     }
153     const std::string resType = type;
154     const std::string iface = "MB_INTERFACE";
155     m_server->registerResource(url, resType, iface);
156 }
157
158 LineResult LineInput::processShow(elements_t& /*elems*/, stringstream& ss)
159 {
160     int index = 0;
161     m_resourceList.clear();
162     resourcemap_t& pmap = m_client->m_resourceMap;
163
164     for (resourcemap_t::iterator it = pmap.begin(); it != pmap.end(); it++) {
165         string resID = it->first;
166         ss << index++ << '\t' << resID << '\n';
167         m_resourceList.push_back(resID);
168         if (m_server) {
169             registerResourceWithServer(resID);
170         }
171     }
172
173     return LR_OK;
174 }
175
176 LineResult LineInput::processDetails(elements_t& elems, stringstream& ss)
177 {
178     WrapResource *wres = resolveResource(elems[1], ss);
179     if (!wres)
180         return LR_NoResource;
181
182     ss << wres->getResourceID() + " [ ";
183     for (auto &types : wres->getResourceTypes()) {
184         ss << types + ' ';
185     }
186     ss << "] ";
187     for (auto &ifs : wres->getResourceInterfaces()) {
188         ss << ifs << " ";
189     }
190     ss << '\n';
191     return LR_OK;
192 }
193
194 void printJSONAsTable(std::string &jsonString) {
195     std::string str = jsonString;
196     std::string key, value;
197     size_t found = str.find("rep");
198     if (found == std::string::npos) { // not found
199         return;
200     }
201     str = str.substr(found+5);
202     while (true) {
203         found = str.find(":");
204         if (found == std::string::npos) {
205             return;
206         }
207         key = str.substr(1, found-1);
208         str = str.substr(found);
209         found = str.find(",");
210         if (found != std::string::npos) {
211             value = str.substr(1, found-1);
212             str = str.substr(found);
213         } else {
214             found = str.find("}");
215             if (found != std::string::npos) {
216                 value = str.substr(1, found-1);
217                 str = str.substr(found);
218             }
219         }
220         cout << key << "\t:" << value << endl;
221     }
222 }
223
224 LineResult LineInput::processGet(elements_t& elems, stringstream& ss)
225 {
226     WrapResource *wres = resolveResource(elems[1], ss);
227     if (!wres)
228         return LR_NoResource;
229
230     token_t token = wres->getResource();
231
232     WrapRequest *wreq = wres->waitResource(token);
233     if (!wreq) {
234         ss << "Get timed out\n";
235         return LR_Timeout;
236     }
237
238     std::string jsonRep ;//= wreq->m_rep.getJSONRepresentation();
239     //ss << jsonRep << endl;
240     printJSONAsTable(jsonRep);
241         return LR_OK;
242 }
243
244 LineResult LineInput::processPut(elements_t& elems, stringstream& ss)
245 {
246     WrapResource *wres = resolveResource(elems[1], ss);
247     if (!wres)
248         return LR_NoResource;
249
250     string format;
251     OCRepresentation rep;
252
253     bool error = false;
254     for (size_t i = 2; i < elems.size(); i++) {
255         string elem = elems[i];
256         char *s = (char *)elem.c_str();    // elem string is intentionally damaged
257         char *key = strtok(s, "=:");
258         char *value = strtok(nullptr, "");
259         if (!value) {
260             ss << "missing separator in element starting with " << key << '\n';
261             error = true;
262             continue;
263         }
264         char delim = value[0];
265         size_t len = strlen(value);
266         if (delim == '\'' || delim == '"') {
267             if (len > 1 && delim == value[len - 1]) {
268                 value[len - 1] = '\0';
269                 value++;
270             }
271         }
272         string v(value, len);
273         stringmap_t formats = wres->getFormats();
274         try {
275             format = formats.at(key);
276         } catch (...) {
277             cerr << "element in arg " << i << " has no format\n";
278             continue;
279         }
280         if (format == "bool") {
281             bool b = v != "0" && v != "false";
282             rep.setValue(key, b);
283         } else if (format == "number") {
284             char *end;
285             int n = (int)strtol(value, &end, 10);
286             if (size_t(end - value) != len) {
287                 double d = atof(value);
288                 rep.setValue(key, d);
289             } else {
290                 rep.setValue(key, n);
291             }
292         } else {    // assume string
293             rep.setValue(key, v);
294         }
295     }
296     if (error)
297         return LR_Param;
298
299     token_t token = wres->putResource(rep);
300
301     WrapRequest *wreq = wres->waitResource(token);
302     if (!wreq) {
303         ss << "Get timed out\n";
304         return LR_Timeout;
305     }
306
307     return LR_OK;
308 }
309
310 LineResult LineInput::processObserve(elements_t& elems, stringstream& ss, observecb_t cb)
311 {
312     WrapResource *wres = resolveResource(elems[1], ss);
313     if (!wres)
314         return LR_NoResource;
315     m_observer = wres;
316     wres->observeResource(cb);
317     return LR_OK;
318 }
319
320 LineResult LineInput::processCancel(elements_t& elems, stringstream& ss)
321 {
322     WrapResource *wres = resolveResource(elems[1], ss);
323     if (!wres)
324         return LR_NoResource;
325
326     wres->cancelObserve();
327     m_observer = nullptr;
328     return LR_OK;
329 }
330
331 WrapResource *LineInput::resolveResource(string resID, stringstream& /*ss*/)
332 {
333     size_t len;
334     string useID = resID;
335     int index = std::stoi(useID, &len);
336
337     if (len == resID.size()) {            // it's an index, not a uri
338         if (size_t(index) >= m_resourceList.size()) {
339             cout << "Resource index out of range (use 'show')\n";
340             return nullptr;
341         }
342         useID = m_resourceList[index];  // now it's a uri
343     }
344
345     resourcemap_t::iterator it = m_client->m_resourceMap.find(useID);
346     if (it == m_client->m_resourceMap.end()) {
347         cout << resID << " is currently not available\n";
348         return nullptr;
349     }
350
351     return it->second;
352 }
353
354 void LineInput::obsCB(token_t /*token*/,
355                       const HeaderOptions& /*headerOptions*/,
356                       const OCRepresentation& /*rep*/,
357                       const int eCode,
358                       const int sequenceNumber)
359 {
360     if (!m_observer)
361         return;
362     cout << "cb " << eCode << " " << sequenceNumber << '\n';
363     //cout << rep.getJSONRepresentation() << "\n";
364 }
365
366 ParseState LineInput::finishElem(char*& e, elements_t& elems)
367 {
368     *e = '\0';
369     elems.push_back(m_elem);
370     e = m_elem;
371     return PS_Between;
372 }
373
374 ParseState LineInput::putCharInElem(char c, char *& e, ParseState newState)
375 {
376     *e++ = c;
377     if (size_t(e - m_elem) >= sizeof (m_elem))
378         throw 20;    // hightly unlikely exception
379     return newState;
380 }
381
382 /*
383  *     See processHelp() above for line format
384  */
385 LineResult LineInput::parseLine(string lineIn, elements_t& elems)
386 {
387     const char *d;
388     char c, *e, delim = 0;
389     bool isSep1, isSep2;
390     size_t len = lineIn.size();
391     ParseState state = PS_Between;
392     const char *line = lineIn.c_str();
393
394     d = line;
395     e = m_elem;
396     while (true) {
397         if (size_t(d - line) >= len) {
398             if (e != m_elem) {
399                 if (state == PS_Infirst || state == PS_Endsecond || (state == PS_Insecond && !delim)) {
400                     state = finishElem(e, elems);
401                     return LR_OK;
402                 }
403             }
404             return LR_Syntax;
405         }
406         c = *d++;
407         if (c == '\n')
408             continue;
409         isSep1 = c == ' ' || c == '\t';
410         isSep2 = c == '=' || c == ':';
411
412         switch (state) {
413         case PS_Between:
414             if (isSep1)
415                 continue;
416             if (isSep2)
417                 return LR_Syntax;
418             state = putCharInElem(c, e, PS_Infirst);
419             break;
420         case PS_Infirst:
421             if (isSep1) {
422                 state = finishElem(e, elems);
423                 continue;
424             }
425             if (isSep2) {
426                 delim = 0;
427                 state = PS_Startsecond;
428             }
429             putCharInElem(c, e, state);
430             break;
431         case PS_Startsecond:
432             if (isSep1 || isSep2)
433                 return LR_Syntax;
434             if (c == '\'' || c == '"' || c == '|')
435                 delim = c;
436             state = putCharInElem(c, e, PS_Insecond);
437             break;
438         case PS_Insecond:
439             if (isSep1 && delim == 0) {
440                 state = finishElem(e, elems);
441                 continue;
442             }
443             if (c == delim) {
444                 state = PS_Endsecond;
445             }
446             *e++ = c;
447             break;
448         case PS_Endsecond:
449             if (isSep1) {
450                 state = finishElem(e, elems);
451                 continue;
452             }
453             return LR_Syntax;
454         case PS_None:
455             return LR_Syntax;
456         }
457     }
458     return LR_OK;
459 }
460
461