blob: 12bef12af4b4e876031780ccd5295044a0164aa8 [file] [log] [blame]
Alexandre Lision0e143012014-01-22 11:02:46 -05001/* $Id: json.cpp 4704 2014-01-16 05:30:46Z ming $ */
2/*
3 * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsua2/json.hpp>
20#include <pjlib-util/errno.h>
21#include <pj/file_io.h>
22#include "util.hpp"
23
24#define THIS_FILE "json.cpp"
25
26using namespace pj;
27using namespace std;
28
29/* Json node operations */
30static bool jsonNode_hasUnread(const ContainerNode*);
31static string jsonNode_unreadName(const ContainerNode*n)
32 throw(Error);
33static float jsonNode_readNumber(const ContainerNode*,
34 const string&)
35 throw(Error);
36static bool jsonNode_readBool(const ContainerNode*,
37 const string&)
38 throw(Error);
39static string jsonNode_readString(const ContainerNode*,
40 const string&)
41 throw(Error);
42static StringVector jsonNode_readStringVector(const ContainerNode*,
43 const string&)
44 throw(Error);
45static ContainerNode jsonNode_readContainer(const ContainerNode*,
46 const string &)
47 throw(Error);
48static ContainerNode jsonNode_readArray(const ContainerNode*,
49 const string &)
50 throw(Error);
51static void jsonNode_writeNumber(ContainerNode*,
52 const string &name,
53 float num)
54 throw(Error);
55static void jsonNode_writeBool(ContainerNode*,
56 const string &name,
57 bool value)
58 throw(Error);
59static void jsonNode_writeString(ContainerNode*,
60 const string &name,
61 const string &value)
62 throw(Error);
63static void jsonNode_writeStringVector(ContainerNode*,
64 const string &name,
65 const StringVector &value)
66 throw(Error);
67static ContainerNode jsonNode_writeNewContainer(ContainerNode*,
68 const string &name)
69 throw(Error);
70static ContainerNode jsonNode_writeNewArray(ContainerNode*,
71 const string &name)
72 throw(Error);
73
74static container_node_op json_op =
75{
76 &jsonNode_hasUnread,
77 &jsonNode_unreadName,
78 &jsonNode_readNumber,
79 &jsonNode_readBool,
80 &jsonNode_readString,
81 &jsonNode_readStringVector,
82 &jsonNode_readContainer,
83 &jsonNode_readArray,
84 &jsonNode_writeNumber,
85 &jsonNode_writeBool,
86 &jsonNode_writeString,
87 &jsonNode_writeStringVector,
88 &jsonNode_writeNewContainer,
89 &jsonNode_writeNewArray
90};
91
92///////////////////////////////////////////////////////////////////////////////
93JsonDocument::JsonDocument()
94: root(NULL)
95{
96 pj_caching_pool_init(&cp, NULL, 0);
97 pool = pj_pool_create(&cp.factory, "jsondoc", 512, 512, NULL);
98 if (!pool)
99 PJSUA2_RAISE_ERROR(PJ_ENOMEM);
100}
101
102JsonDocument::~JsonDocument()
103{
104 if (pool)
105 pj_pool_release(pool);
106 pj_caching_pool_destroy(&cp);
107}
108
109void JsonDocument::initRoot() const
110{
111 rootNode.op = &json_op;
112 rootNode.data.doc = (void*)this;
113 rootNode.data.data1 = (void*)root;
114 rootNode.data.data2 = root->value.children.next;
115}
116
117void JsonDocument::loadFile(const string &filename) throw(Error)
118{
119 if (root)
120 PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()",
121 "Document already initialized");
122
123 if (!pj_file_exists(filename.c_str()))
124 PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
125
126 pj_ssize_t size = (pj_ssize_t)pj_file_size(filename.c_str());
127 pj_status_t status;
128
129 char *buffer = (char*)pj_pool_alloc(pool, size+1);
130 pj_oshandle_t fd = 0;
131 unsigned parse_size;
132 char err_msg[120];
133 pj_json_err_info err_info;
134
135 err_msg[0] = '\0';
136
137 status = pj_file_open(pool, filename.c_str(), PJ_O_RDONLY, &fd);
138 if (status != PJ_SUCCESS)
139 goto on_error;
140
141 status = pj_file_read(fd, buffer, &size);
142 if (status != PJ_SUCCESS)
143 goto on_error;
144
145 pj_file_close(fd);
146 fd = NULL;
147
148 if (size <= 0) {
149 status = PJ_EEOF;
150 goto on_error;
151 }
152
153 parse_size = (unsigned)size;
154 root = pj_json_parse(pool, buffer, &parse_size, &err_info);
155 if (root == NULL) {
156 pj_ansi_snprintf(err_msg, sizeof(err_msg),
157 "JSON parsing failed: syntax error in file '%s' at "
158 "line %d column %d",
159 filename.c_str(), err_info.line, err_info.col);
160 PJ_LOG(1,(THIS_FILE, err_msg));
161 status = PJLIB_UTIL_EINJSON;
162 goto on_error;
163 }
164
165 initRoot();
166 return;
167
168on_error:
169 if (fd)
170 pj_file_close(fd);
171 if (err_msg[0])
172 PJSUA2_RAISE_ERROR3(status, "loadFile()", err_msg);
173 else
174 PJSUA2_RAISE_ERROR(status);
175}
176
177void JsonDocument::loadString(const string &input) throw(Error)
178{
179 if (root)
180 PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()",
181 "Document already initialized");
182
183 unsigned size = input.size();
184 char *buffer = (char*)pj_pool_alloc(pool, size+1);
185 unsigned parse_size = (unsigned)size;
186 pj_json_err_info err_info;
187
188 pj_memcpy(buffer, input.c_str(), size);
189
190 root = pj_json_parse(pool, buffer, &parse_size, &err_info);
191 if (root == NULL) {
192 char err_msg[80];
193
194 pj_ansi_snprintf(err_msg, sizeof(err_msg),
195 "JSON parsing failed at line %d column %d",
196 err_info.line, err_info.col);
197 PJ_LOG(1,(THIS_FILE, err_msg));
198 PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "loadString()", err_msg);
199 }
200 initRoot();
201}
202
203struct save_file_data
204{
205 pj_oshandle_t fd;
206};
207
208static pj_status_t json_file_writer(const char *s,
209 unsigned size,
210 void *user_data)
211{
212 save_file_data *sd = (save_file_data*)user_data;
213 pj_ssize_t ssize = (pj_ssize_t)size;
214 return pj_file_write(sd->fd, s, &ssize);
215}
216
217void JsonDocument::saveFile(const string &filename) throw(Error)
218{
219 struct save_file_data sd;
220 pj_status_t status;
221
222 /* Make sure root container has been created */
223 getRootContainer();
224
225 status = pj_file_open(pool, filename.c_str(), PJ_O_WRONLY, &sd.fd);
226 if (status != PJ_SUCCESS)
227 PJSUA2_RAISE_ERROR(status);
228
229 status = pj_json_writef(root, &json_file_writer, &sd.fd);
230 pj_file_close(sd.fd);
231
232 if (status != PJ_SUCCESS)
233 PJSUA2_RAISE_ERROR(status);
234}
235
236struct save_string_data
237{
238 string output;
239};
240
241static pj_status_t json_string_writer(const char *s,
242 unsigned size,
243 void *user_data)
244{
245 save_string_data *sd = (save_string_data*)user_data;
246 sd->output.append(s, size);
247 return PJ_SUCCESS;
248}
249
250string JsonDocument::saveString() throw(Error)
251{
252 struct save_string_data sd;
253 pj_status_t status;
254
255 /* Make sure root container has been created */
256 getRootContainer();
257
258 status = pj_json_writef(root, &json_string_writer, &sd);
259 if (status != PJ_SUCCESS)
260 PJSUA2_RAISE_ERROR(status);
261
262 return sd.output;
263}
264
265ContainerNode & JsonDocument::getRootContainer() const
266{
267 if (!root) {
268 root = allocElement();
269 pj_json_elem_obj(root, NULL);
270 initRoot();
271 }
272
273 return rootNode;
274}
275
276pj_json_elem* JsonDocument::allocElement() const
277{
278 return (pj_json_elem*)pj_pool_alloc(pool, sizeof(pj_json_elem));
279}
280
281pj_pool_t *JsonDocument::getPool()
282{
283 return pool;
284}
285
286///////////////////////////////////////////////////////////////////////////////
287struct json_node_data
288{
289 JsonDocument *doc;
290 pj_json_elem *jnode;
291 pj_json_elem *childPtr;
292};
293
294static bool jsonNode_hasUnread(const ContainerNode *node)
295{
296 json_node_data *jdat = (json_node_data*)&node->data;
297 return jdat->childPtr != (pj_json_elem*)&jdat->jnode->value.children;
298}
299
300static void json_verify(struct json_node_data *jdat,
301 const char *op,
302 const string &name,
303 pj_json_val_type type)
304{
305 if (jdat->childPtr == (pj_json_elem*)&jdat->jnode->value.children)
306 PJSUA2_RAISE_ERROR3(PJ_EEOF, op, "No unread element");
307
308 /* If name is specified, then check if the names match, except
309 * when the node name itself is empty and the parent node is
310 * an array, then ignore the checking (JSON doesn't allow array
311 * elements to have name).
312 */
313 if (name.size() && name.compare(0, name.size(),
314 jdat->childPtr->name.ptr,
315 jdat->childPtr->name.slen) &&
316 jdat->childPtr->name.slen &&
317 jdat->jnode->type != PJ_JSON_VAL_ARRAY)
318 {
319 char err_msg[80];
320 pj_ansi_snprintf(err_msg, sizeof(err_msg),
321 "Name mismatch: expecting '%s' got '%.*s'",
322 name.c_str(), (int)jdat->childPtr->name.slen,
323 jdat->childPtr->name.ptr);
324 PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg);
325 }
326
327 if (type != PJ_JSON_VAL_NULL && jdat->childPtr->type != type) {
328 char err_msg[80];
329 pj_ansi_snprintf(err_msg, sizeof(err_msg),
330 "Type mismatch: expecting %d got %d",
331 type, jdat->childPtr->type);
332 PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg);
333 }
334}
335
336static string jsonNode_unreadName(const ContainerNode *node)
337 throw(Error)
338{
339 json_node_data *jdat = (json_node_data*)&node->data;
340 json_verify(jdat, "unreadName()", "", PJ_JSON_VAL_NULL);
341 return pj2Str(jdat->childPtr->name);
342}
343
344static float jsonNode_readNumber(const ContainerNode *node,
345 const string &name)
346 throw(Error)
347{
348 json_node_data *jdat = (json_node_data*)&node->data;
349 json_verify(jdat, "readNumber()", name, PJ_JSON_VAL_NUMBER);
350 jdat->childPtr = jdat->childPtr->next;
351 return jdat->childPtr->prev->value.num;
352}
353
354static bool jsonNode_readBool(const ContainerNode *node,
355 const string &name)
356 throw(Error)
357{
358 json_node_data *jdat = (json_node_data*)&node->data;
359 json_verify(jdat, "readBool()", name, PJ_JSON_VAL_BOOL);
360 jdat->childPtr = jdat->childPtr->next;
361 return PJ2BOOL(jdat->childPtr->prev->value.is_true);
362}
363
364static string jsonNode_readString(const ContainerNode *node,
365 const string &name)
366 throw(Error)
367{
368 json_node_data *jdat = (json_node_data*)&node->data;
369 json_verify(jdat, "readString()", name, PJ_JSON_VAL_STRING);
370 jdat->childPtr = jdat->childPtr->next;
371 return pj2Str(jdat->childPtr->prev->value.str);
372}
373
374static StringVector jsonNode_readStringVector(const ContainerNode *node,
375 const string &name)
376 throw(Error)
377{
378 json_node_data *jdat = (json_node_data*)&node->data;
379 json_verify(jdat, "readStringVector()", name, PJ_JSON_VAL_ARRAY);
380
381 StringVector result;
382 pj_json_elem *child = jdat->childPtr->value.children.next;
383 while (child != (pj_json_elem*)&jdat->childPtr->value.children) {
384 if (child->type != PJ_JSON_VAL_STRING) {
385 char err_msg[80];
386 pj_ansi_snprintf(err_msg, sizeof(err_msg),
387 "Elements not string but type %d",
388 child->type);
389 PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "readStringVector()",
390 err_msg);
391 }
392 result.push_back(pj2Str(child->value.str));
393 child = child->next;
394 }
395
396 jdat->childPtr = jdat->childPtr->next;
397 return result;
398}
399
400static ContainerNode jsonNode_readContainer(const ContainerNode *node,
401 const string &name)
402 throw(Error)
403{
404 json_node_data *jdat = (json_node_data*)&node->data;
405 json_verify(jdat, "readContainer()", name, PJ_JSON_VAL_OBJ);
406
407 ContainerNode json_node;
408
409 json_node.op = &json_op;
410 json_node.data.doc = (void*)jdat->doc;
411 json_node.data.data1 = (void*)jdat->childPtr;
412 json_node.data.data2 = (void*)jdat->childPtr->value.children.next;
413
414 jdat->childPtr = jdat->childPtr->next;
415 return json_node;
416}
417
418static ContainerNode jsonNode_readArray(const ContainerNode *node,
419 const string &name)
420 throw(Error)
421{
422 json_node_data *jdat = (json_node_data*)&node->data;
423 json_verify(jdat, "readArray()", name, PJ_JSON_VAL_ARRAY);
424
425 ContainerNode json_node;
426
427 json_node.op = &json_op;
428 json_node.data.doc = (void*)jdat->doc;
429 json_node.data.data1 = (void*)jdat->childPtr;
430 json_node.data.data2 = (void*)jdat->childPtr->value.children.next;
431
432 jdat->childPtr = jdat->childPtr->next;
433 return json_node;
434}
435
436static pj_str_t alloc_name(JsonDocument *doc, const string &name)
437{
438 pj_str_t new_name;
439 pj_strdup2(doc->getPool(), &new_name, name.c_str());
440 return new_name;
441}
442
443static void jsonNode_writeNumber(ContainerNode *node,
444 const string &name,
445 float num)
446 throw(Error)
447{
448 json_node_data *jdat = (json_node_data*)&node->data;
449 pj_json_elem *el = jdat->doc->allocElement();
450 pj_str_t nm = alloc_name(jdat->doc, name);
451 pj_json_elem_number(el, &nm, num);
452 pj_json_elem_add(jdat->jnode, el);
453}
454
455static void jsonNode_writeBool(ContainerNode *node,
456 const string &name,
457 bool value)
458 throw(Error)
459{
460 json_node_data *jdat = (json_node_data*)&node->data;
461 pj_json_elem *el = jdat->doc->allocElement();
462 pj_str_t nm = alloc_name(jdat->doc, name);
463 pj_json_elem_bool(el, &nm, value);
464 pj_json_elem_add(jdat->jnode, el);
465}
466
467static void jsonNode_writeString(ContainerNode *node,
468 const string &name,
469 const string &value)
470 throw(Error)
471{
472 json_node_data *jdat = (json_node_data*)&node->data;
473 pj_json_elem *el = jdat->doc->allocElement();
474 pj_str_t nm = alloc_name(jdat->doc, name);
475 pj_str_t new_val;
476 pj_strdup2(jdat->doc->getPool(), &new_val, value.c_str());
477 pj_json_elem_string(el, &nm, &new_val);
478
479 pj_json_elem_add(jdat->jnode, el);
480}
481
482static void jsonNode_writeStringVector(ContainerNode *node,
483 const string &name,
484 const StringVector &value)
485 throw(Error)
486{
487 json_node_data *jdat = (json_node_data*)&node->data;
488 pj_json_elem *el = jdat->doc->allocElement();
489 pj_str_t nm = alloc_name(jdat->doc, name);
490
491 pj_json_elem_array(el, &nm);
492 for (unsigned i=0; i<value.size(); ++i) {
493 pj_str_t new_val;
494
495 pj_strdup2(jdat->doc->getPool(), &new_val, value[i].c_str());
496 pj_json_elem *child = jdat->doc->allocElement();
497 pj_json_elem_string(child, NULL, &new_val);
498 pj_json_elem_add(el, child);
499 }
500
501 pj_json_elem_add(jdat->jnode, el);
502}
503
504static ContainerNode jsonNode_writeNewContainer(ContainerNode *node,
505 const string &name)
506 throw(Error)
507{
508 json_node_data *jdat = (json_node_data*)&node->data;
509 pj_json_elem *el = jdat->doc->allocElement();
510 pj_str_t nm = alloc_name(jdat->doc, name);
511
512 pj_json_elem_obj(el, &nm);
513 pj_json_elem_add(jdat->jnode, el);
514
515 ContainerNode json_node;
516
517 json_node.op = &json_op;
518 json_node.data.doc = (void*)jdat->doc;
519 json_node.data.data1 = (void*)el;
520 json_node.data.data2 = (void*)el->value.children.next;
521
522 return json_node;
523}
524
525static ContainerNode jsonNode_writeNewArray(ContainerNode *node,
526 const string &name)
527 throw(Error)
528{
529 json_node_data *jdat = (json_node_data*)&node->data;
530 pj_json_elem *el = jdat->doc->allocElement();
531 pj_str_t nm = alloc_name(jdat->doc, name);
532
533 pj_json_elem_array(el, &nm);
534 pj_json_elem_add(jdat->jnode, el);
535
536 ContainerNode json_node;
537
538 json_node.op = &json_op;
539 json_node.data.doc = (void*)jdat->doc;
540 json_node.data.data1 = (void*)el;
541 json_node.data.data2 = (void*)el->value.children.next;
542
543 return json_node;
544}