Imported Upstream version 0.9.2
[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, const HeaderOptions& headerOptions, const OCRepresentation& rep, const int eCode, const int sequenceNumber)
355 {
356     if (!m_observer)
357         return;
358     cout << "cb " << eCode << " " << sequenceNumber << '\n';
359     //cout << rep.getJSONRepresentation() << "\n";
360 }
361
362 ParseState LineInput::finishElem(char*& e, elements_t& elems)
363 {
364     *e = '\0';
365     elems.push_back(m_elem);
366     e = m_elem;
367     return PS_Between;
368 }
369
370 ParseState LineInput::putCharInElem(char c, char *& e, ParseState newState)
371 {
372     *e++ = c;
373     if (size_t(e - m_elem) >= sizeof (m_elem))
374         throw 20;    // hightly unlikely exception
375     return newState;
376 }
377
378 /*
379  *     See processHelp() above for line format
380  */
381 LineResult LineInput::parseLine(string lineIn, elements_t& elems)
382 {
383     const char *d;
384     char c, *e, delim = 0;
385     bool isSep1, isSep2;
386     size_t len = lineIn.size();
387     ParseState state = PS_Between;
388     const char *line = lineIn.c_str();
389
390     d = line;
391     e = m_elem;
392     while (true) {
393         if (size_t(d - line) >= len) {
394             if (e != m_elem) {
395                 if (state == PS_Infirst || state == PS_Endsecond || (state == PS_Insecond && !delim)) {
396                     state = finishElem(e, elems);
397                     return LR_OK;
398                 }
399             }
400             return LR_Syntax;
401         }
402         c = *d++;
403         if (c == '\n')
404             continue;
405         isSep1 = c == ' ' || c == '\t';
406         isSep2 = c == '=' || c == ':';
407
408         switch (state) {
409         case PS_Between:
410             if (isSep1)
411                 continue;
412             if (isSep2)
413                 return LR_Syntax;
414             state = putCharInElem(c, e, PS_Infirst);
415             break;
416         case PS_Infirst:
417             if (isSep1) {
418                 state = finishElem(e, elems);
419                 continue;
420             }
421             if (isSep2) {
422                 delim = 0;
423                 state = PS_Startsecond;
424             }
425             putCharInElem(c, e, state);
426             break;
427         case PS_Startsecond:
428             if (isSep1 || isSep2)
429                 return LR_Syntax;
430             if (c == '\'' || c == '"' || c == '|')
431                 delim = c;
432             state = putCharInElem(c, e, PS_Insecond);
433             break;
434         case PS_Insecond:
435             if (isSep1 && delim == 0) {
436                 state = finishElem(e, elems);
437                 continue;
438             }
439             if (c == delim) {
440                 state = PS_Endsecond;
441             }
442             *e++ = c;
443             break;
444         case PS_Endsecond:
445             if (isSep1) {
446                 state = finishElem(e, elems);
447                 continue;
448             }
449             return LR_Syntax;
450         case PS_None:
451             return LR_Syntax;
452         }
453     }
454     return LR_OK;
455 }
456
457