1 /*
2  * Copyright (C) 2010 TelTech Systems Inc.
3  *
4  * This file is part of SEMS, a free SIP media server.
5  *
6  * SEMS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version. This program is released under
10  * the GPL with the additional exemption that compiling, linking,
11  * and/or using OpenSSL is allowed.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 
28 #include "AmArg.h"
29 #include "AmUtils.h"
30 #include "log.h"
31 
32 #include "jsonArg.h"
33 using std::string;
34 
35 #include "jsonxx.h"
36 using namespace jsonxx;
37 
38 #include <sstream>
39 
40 const char *hex_chars = "0123456789abcdef";
41 
str2json(const char * str)42 string str2json(const char* str)
43 {
44   return str2json(str,strlen(str));
45 }
46 
str2json(const string & str)47 string str2json(const string& str)
48 {
49   return str2json(str.c_str(),str.length());
50 }
51 
str2json(const char * str,size_t len)52 string str2json(const char* str, size_t len)
53 {
54     // borrowed from jsoncpp
55     // Not sure how to handle unicode...
56     if (strpbrk(str, "\"\\\b\f\n\r\t") == NULL)
57       return string("\"") + str + "\"";
58     // We have to walk value and escape any special characters.
59     // Appending to std::string is not efficient, but this should be rare.
60     // (Note: forward slashes are *not* rare, but I am not escaping them.)
61     unsigned maxsize = len*2 + 3; // allescaped+quotes+NULL
62     std::string result;
63     result.reserve(maxsize); // to avoid lots of mallocs
64     result += "\"";
65     const char* end = str + len;
66     for (const char* c = str; (c != end) && (*c != 0); ++c){
67       switch(*c){
68       case '\"':
69 	result += "\\\"";
70 	break;
71       case '\\':
72 	result += "\\\\";
73 	break;
74       case '\b':
75 	result += "\\b";
76 	break;
77       case '\f':
78 	 result += "\\f";
79 	 break;
80       case '\n':
81 	result += "\\n";
82 	break;
83       case '\r':
84 	result += "\\r";
85 	break;
86       case '\t':
87 	result += "\\t";
88 	break;
89       case '/':
90 	// Even though \/ is considered a legal escape in JSON, a bare
91 	// slash is also legal, so I see no reason to escape it.
92 	// (I hope I am not misunderstanding something.)
93       default:{
94 	if (*c < ' ')
95 	  result += "\\u00" + string() + hex_chars[*c >> 4] + string() + hex_chars[*c & 0xf];
96 	else
97 	  result += *c;
98       } break;
99       }
100     }
101     result += "\"";
102     return result;
103 }
104 
arg2json(const AmArg & a)105 string arg2json(const AmArg &a) {
106   // TODO: optimize to avoid lots of mallocs
107   // TODO: how to get a bool?
108   string s;
109   switch (a.getType()) {
110   case AmArg::Undef:
111     return "null";
112 
113   case AmArg::Int:
114     return a.asInt()<0?"-"+int2str(abs(a.asInt())):int2str(abs(a.asInt()));
115 
116   case AmArg::LongLong:
117     return longlong2str(a.asLongLong());
118 
119   case AmArg::Bool:
120     return a.asBool()?"true":"false";
121 
122   case AmArg::Double:
123     return double2str(a.asDouble());
124 
125   case AmArg::CStr:
126     return str2json(a.asCStr());
127 
128   case AmArg::Array:
129     s = "[";
130     for (size_t i = 0; i < a.size(); i ++)
131       s += arg2json(a[i]) + ", ";
132     if (1 < s.size())
133       s.resize(s.size() - 2); // strip last ", "
134     s += "]";
135     return s;
136 
137   case AmArg::Struct:
138     s = "{";
139     for (AmArg::ValueStruct::const_iterator it = a.asStruct()->begin();
140 	 it != a.asStruct()->end(); it ++) {
141       s += '"'+it->first + "\": ";
142       s += arg2json(it->second);
143       s += ", ";
144     }
145     if (1 < s.size())
146       s.resize(s.size() - 2); // strip last ", "
147     s += "}";
148     return s;
149   default: break;
150   }
151 
152   return "{}";
153 }
154 
155 // based on jsonxx
array_parse(std::istream & input,AmArg & res)156 bool array_parse(std::istream& input, AmArg& res) {
157   if (!match("[", input)) {
158     return false;
159   }
160 
161   res.assertArray();
162 
163   if (match("]", input)) {
164     return true;
165   }
166 
167   do {
168     res.push(AmArg());
169     AmArg v;
170     if (!json2arg(input, res.get(res.size()-1))) {
171       res.clear();
172       return false;
173       res.pop_back();
174       break; // TODO: return false????
175     }
176   } while (match(",", input));
177 
178   if (!match("]", input)) {
179     res.clear();
180     return false;
181   }
182   return true;
183 }
184 
object_parse(std::istream & input,AmArg & res)185 bool object_parse(std::istream& input, AmArg& res) {
186   if (!match("{", input)) {
187     return false;
188   }
189 
190   res.assertStruct();
191 
192   if (match("}", input)) {
193     return true;
194   }
195 
196   do {
197     std::string key;
198     if (!parse_string(input, &key)) {
199       if (match("}",input,true)) {
200 	return true;
201       }
202       res.clear();
203       return false;
204     }
205     if (!match(":", input)) {
206       res.clear();
207       return false;
208     }
209     res[key] = AmArg(); // using the reference
210     if (!json2arg(input, res[key])) {
211       res.clear();
212       return false;
213     }
214   } while (match(",", input));
215 
216   if (!match("}", input)) {
217     res.clear();
218     return false;
219   }
220 
221   return true;
222 }
223 
json2arg(const std::string & input,AmArg & res)224 bool json2arg(const std::string& input, AmArg& res) {
225   std::istringstream iss(input);
226   return json2arg(iss, res);
227 }
228 
json2arg(const char * input,AmArg & res)229 bool json2arg(const char* input, AmArg& res) {
230   std::istringstream iss(input);
231   return json2arg(iss, res);
232 }
233 
json2arg(std::istream & input,AmArg & res)234 bool json2arg(std::istream& input, AmArg& res) {
235 
236   res.clear();
237 
238   std::string string_value;
239   if (parse_string(input, &string_value)) {
240     res = string_value; // todo: unnecessary value copy here
241     return true;
242   }
243 
244   if (parse_float(input, &res.v_double)) {
245     res.type = AmArg::Double;
246     return true;
247   }
248 
249   if (parse_number(input, &res.v_int)) {
250     res.type = AmArg::Int;
251     return true;
252   }
253 
254   if (parse_bool(input, &res.v_bool)) {
255     res.type = AmArg::Bool;
256     return true;
257   }
258 
259   if (parse_null(input)) { // AmArg::Undef
260     return true;
261   }
262 
263   if (array_parse(input, res)) {
264     return true;
265   }
266 
267   if (object_parse(input, res)) {
268      return true;
269   }
270 
271   res.clear();
272   return false;
273 }
274