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