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