123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- /*
- * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <pjsua2/json.hpp>
- #include <pjlib-util/errno.h>
- #include <pj/file_io.h>
- #include "util.hpp"
- #define THIS_FILE "json.cpp"
- using namespace pj;
- using namespace std;
- /* Json node operations */
- static bool jsonNode_hasUnread(const ContainerNode*);
- static string jsonNode_unreadName(const ContainerNode*n)
- PJSUA2_THROW(Error);
- static float jsonNode_readNumber(const ContainerNode*,
- const string&)
- PJSUA2_THROW(Error);
- static bool jsonNode_readBool(const ContainerNode*,
- const string&)
- PJSUA2_THROW(Error);
- static string jsonNode_readString(const ContainerNode*,
- const string&)
- PJSUA2_THROW(Error);
- static StringVector jsonNode_readStringVector(const ContainerNode*,
- const string&)
- PJSUA2_THROW(Error);
- static ContainerNode jsonNode_readContainer(const ContainerNode*,
- const string &)
- PJSUA2_THROW(Error);
- static ContainerNode jsonNode_readArray(const ContainerNode*,
- const string &)
- PJSUA2_THROW(Error);
- static void jsonNode_writeNumber(ContainerNode*,
- const string &name,
- float num)
- PJSUA2_THROW(Error);
- static void jsonNode_writeBool(ContainerNode*,
- const string &name,
- bool value)
- PJSUA2_THROW(Error);
- static void jsonNode_writeString(ContainerNode*,
- const string &name,
- const string &value)
- PJSUA2_THROW(Error);
- static void jsonNode_writeStringVector(ContainerNode*,
- const string &name,
- const StringVector &value)
- PJSUA2_THROW(Error);
- static ContainerNode jsonNode_writeNewContainer(ContainerNode*,
- const string &name)
- PJSUA2_THROW(Error);
- static ContainerNode jsonNode_writeNewArray(ContainerNode*,
- const string &name)
- PJSUA2_THROW(Error);
- static container_node_op json_op =
- {
- &jsonNode_hasUnread,
- &jsonNode_unreadName,
- &jsonNode_readNumber,
- &jsonNode_readBool,
- &jsonNode_readString,
- &jsonNode_readStringVector,
- &jsonNode_readContainer,
- &jsonNode_readArray,
- &jsonNode_writeNumber,
- &jsonNode_writeBool,
- &jsonNode_writeString,
- &jsonNode_writeStringVector,
- &jsonNode_writeNewContainer,
- &jsonNode_writeNewArray
- };
- ///////////////////////////////////////////////////////////////////////////////
- JsonDocument::JsonDocument()
- : root(NULL)
- {
- pj_caching_pool_init(&cp, NULL, 0);
- pool = pj_pool_create(&cp.factory, "jsondoc", 512, 512, NULL);
- if (!pool)
- PJSUA2_RAISE_ERROR(PJ_ENOMEM);
- }
- JsonDocument::~JsonDocument()
- {
- if (pool)
- pj_pool_release(pool);
- pj_caching_pool_destroy(&cp);
- }
- void JsonDocument::initRoot() const
- {
- rootNode.op = &json_op;
- rootNode.data.doc = (void*)this;
- rootNode.data.data1 = (void*)root;
- rootNode.data.data2 = root->value.children.next;
- }
- void JsonDocument::loadFile(const string &filename) PJSUA2_THROW(Error)
- {
- if (root)
- PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()",
- "Document already initialized");
- if (!pj_file_exists(filename.c_str()))
- PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
- pj_ssize_t size = (pj_ssize_t)pj_file_size(filename.c_str());
- if (size <= 0)
- PJSUA2_RAISE_ERROR(PJ_ETOOSMALL);
- pj_status_t status;
- char *buffer = (char*)pj_pool_alloc(pool, size+1);
- pj_oshandle_t fd = 0;
- unsigned parse_size;
- char err_msg[120];
- pj_json_err_info err_info;
- err_msg[0] = '\0';
- status = pj_file_open(pool, filename.c_str(), PJ_O_RDONLY, &fd);
- if (status != PJ_SUCCESS)
- goto on_error;
- status = pj_file_read(fd, buffer, &size);
- if (status != PJ_SUCCESS)
- goto on_error;
- pj_file_close(fd);
- fd = NULL;
- if (size <= 0) {
- status = PJ_EEOF;
- goto on_error;
- }
- parse_size = (unsigned)size;
- root = pj_json_parse(pool, buffer, &parse_size, &err_info);
- if (root == NULL) {
- pj_ansi_snprintf(err_msg, sizeof(err_msg),
- "JSON parsing failed: syntax error in file '%s' at "
- "line %d column %d",
- filename.c_str(), err_info.line, err_info.col);
- PJ_LOG(1,(THIS_FILE, "%s", err_msg));
- status = PJLIB_UTIL_EINJSON;
- goto on_error;
- }
- initRoot();
- return;
- on_error:
- if (fd)
- pj_file_close(fd);
- if (err_msg[0])
- PJSUA2_RAISE_ERROR3(status, "loadFile()", err_msg);
- else
- PJSUA2_RAISE_ERROR(status);
- }
- void JsonDocument::loadString(const string &input) PJSUA2_THROW(Error)
- {
- if (root)
- PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()",
- "Document already initialized");
- unsigned size = (unsigned)input.size();
- char *buffer = (char*)pj_pool_alloc(pool, size+1);
- unsigned parse_size = (unsigned)size;
- pj_json_err_info err_info;
- pj_memcpy(buffer, input.c_str(), size);
- root = pj_json_parse(pool, buffer, &parse_size, &err_info);
- if (root == NULL) {
- char err_msg[80];
- pj_ansi_snprintf(err_msg, sizeof(err_msg),
- "JSON parsing failed at line %d column %d",
- err_info.line, err_info.col);
- PJ_LOG(1,(THIS_FILE, "%s", err_msg));
- PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "loadString()", err_msg);
- }
- initRoot();
- }
- struct save_file_data
- {
- pj_oshandle_t fd;
- };
- static pj_status_t json_file_writer(const char *s,
- unsigned size,
- void *user_data)
- {
- save_file_data *sd = (save_file_data*)user_data;
- pj_ssize_t ssize = (pj_ssize_t)size;
- return pj_file_write(sd->fd, s, &ssize);
- }
- void JsonDocument::saveFile(const string &filename) PJSUA2_THROW(Error)
- {
- struct save_file_data sd;
- pj_status_t status;
- /* Make sure root container has been created */
- getRootContainer();
- status = pj_file_open(pool, filename.c_str(), PJ_O_WRONLY, &sd.fd);
- if (status != PJ_SUCCESS)
- PJSUA2_RAISE_ERROR(status);
- status = pj_json_writef(root, &json_file_writer, &sd.fd);
- pj_file_close(sd.fd);
- if (status != PJ_SUCCESS)
- PJSUA2_RAISE_ERROR(status);
- }
- struct save_string_data
- {
- string output;
- };
- static pj_status_t json_string_writer(const char *s,
- unsigned size,
- void *user_data)
- {
- save_string_data *sd = (save_string_data*)user_data;
- sd->output.append(s, size);
- return PJ_SUCCESS;
- }
- string JsonDocument::saveString() PJSUA2_THROW(Error)
- {
- struct save_string_data sd;
- pj_status_t status;
- /* Make sure root container has been created */
- getRootContainer();
- status = pj_json_writef(root, &json_string_writer, &sd);
- if (status != PJ_SUCCESS)
- PJSUA2_RAISE_ERROR(status);
- return sd.output;
- }
- ContainerNode & JsonDocument::getRootContainer() const
- {
- if (!root) {
- root = allocElement();
- pj_json_elem_obj(root, NULL);
- initRoot();
- }
- return rootNode;
- }
- pj_json_elem* JsonDocument::allocElement() const
- {
- return (pj_json_elem*)pj_pool_alloc(pool, sizeof(pj_json_elem));
- }
- pj_pool_t *JsonDocument::getPool()
- {
- return pool;
- }
- ///////////////////////////////////////////////////////////////////////////////
- struct json_node_data
- {
- JsonDocument *doc;
- pj_json_elem *jnode;
- pj_json_elem *childPtr;
- };
- static bool jsonNode_hasUnread(const ContainerNode *node)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- return jdat->childPtr != (pj_json_elem*)&jdat->jnode->value.children;
- }
- static void json_verify(struct json_node_data *jdat,
- const char *op,
- const string &name,
- pj_json_val_type type)
- {
- if (jdat->childPtr == (pj_json_elem*)&jdat->jnode->value.children)
- PJSUA2_RAISE_ERROR3(PJ_EEOF, op, "No unread element");
- /* If name is specified, then check if the names match, except
- * when the node name itself is empty and the parent node is
- * an array, then ignore the checking (JSON doesn't allow array
- * elements to have name).
- */
- if (jdat->jnode->type != PJ_JSON_VAL_ARRAY &&
- name.size() && jdat->childPtr->name.slen &&
- name.compare(0, name.size(), jdat->childPtr->name.ptr,
- jdat->childPtr->name.slen))
- {
- char err_msg[80];
- pj_ansi_snprintf(err_msg, sizeof(err_msg),
- "Name mismatch: expecting '%s' got '%.*s'",
- name.c_str(), (int)jdat->childPtr->name.slen,
- jdat->childPtr->name.ptr);
- PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg);
- }
- if (type != PJ_JSON_VAL_NULL && jdat->childPtr->type != type) {
- char err_msg[80];
- pj_ansi_snprintf(err_msg, sizeof(err_msg),
- "Type mismatch: expecting %d got %d",
- type, jdat->childPtr->type);
- PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg);
- }
- }
- static string jsonNode_unreadName(const ContainerNode *node)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- json_verify(jdat, "unreadName()", "", PJ_JSON_VAL_NULL);
- return pj2Str(jdat->childPtr->name);
- }
- static float jsonNode_readNumber(const ContainerNode *node,
- const string &name)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- json_verify(jdat, "readNumber()", name, PJ_JSON_VAL_NUMBER);
- jdat->childPtr = jdat->childPtr->next;
- return jdat->childPtr->prev->value.num;
- }
- static bool jsonNode_readBool(const ContainerNode *node,
- const string &name)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- json_verify(jdat, "readBool()", name, PJ_JSON_VAL_BOOL);
- jdat->childPtr = jdat->childPtr->next;
- return PJ2BOOL(jdat->childPtr->prev->value.is_true);
- }
- static string jsonNode_readString(const ContainerNode *node,
- const string &name)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- json_verify(jdat, "readString()", name, PJ_JSON_VAL_STRING);
- jdat->childPtr = jdat->childPtr->next;
- return pj2Str(jdat->childPtr->prev->value.str);
- }
- static StringVector jsonNode_readStringVector(const ContainerNode *node,
- const string &name)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- json_verify(jdat, "readStringVector()", name, PJ_JSON_VAL_ARRAY);
- StringVector result;
- pj_json_elem *child = jdat->childPtr->value.children.next;
- while (child != (pj_json_elem*)&jdat->childPtr->value.children) {
- if (child->type != PJ_JSON_VAL_STRING) {
- char err_msg[80];
- pj_ansi_snprintf(err_msg, sizeof(err_msg),
- "Elements not string but type %d",
- child->type);
- PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "readStringVector()",
- err_msg);
- }
- result.push_back(pj2Str(child->value.str));
- child = child->next;
- }
- jdat->childPtr = jdat->childPtr->next;
- return result;
- }
- static ContainerNode jsonNode_readContainer(const ContainerNode *node,
- const string &name)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- json_verify(jdat, "readContainer()", name, PJ_JSON_VAL_OBJ);
- ContainerNode json_node;
- json_node.op = &json_op;
- json_node.data.doc = (void*)jdat->doc;
- json_node.data.data1 = (void*)jdat->childPtr;
- json_node.data.data2 = (void*)jdat->childPtr->value.children.next;
- jdat->childPtr = jdat->childPtr->next;
- return json_node;
- }
- static ContainerNode jsonNode_readArray(const ContainerNode *node,
- const string &name)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- json_verify(jdat, "readArray()", name, PJ_JSON_VAL_ARRAY);
- ContainerNode json_node;
- json_node.op = &json_op;
- json_node.data.doc = (void*)jdat->doc;
- json_node.data.data1 = (void*)jdat->childPtr;
- json_node.data.data2 = (void*)jdat->childPtr->value.children.next;
- jdat->childPtr = jdat->childPtr->next;
- return json_node;
- }
- static pj_str_t alloc_name(JsonDocument *doc, const string &name)
- {
- pj_str_t new_name;
- pj_strdup2(doc->getPool(), &new_name, name.c_str());
- return new_name;
- }
- static void jsonNode_writeNumber(ContainerNode *node,
- const string &name,
- float num)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- pj_json_elem *el = jdat->doc->allocElement();
- pj_str_t nm = alloc_name(jdat->doc, name);
- pj_json_elem_number(el, &nm, num);
- pj_json_elem_add(jdat->jnode, el);
- }
- static void jsonNode_writeBool(ContainerNode *node,
- const string &name,
- bool value)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- pj_json_elem *el = jdat->doc->allocElement();
- pj_str_t nm = alloc_name(jdat->doc, name);
- pj_json_elem_bool(el, &nm, value);
- pj_json_elem_add(jdat->jnode, el);
- }
- static void jsonNode_writeString(ContainerNode *node,
- const string &name,
- const string &value)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- pj_json_elem *el = jdat->doc->allocElement();
- pj_str_t nm = alloc_name(jdat->doc, name);
- pj_str_t new_val;
- pj_str_t str_val;
- pj_strset(&str_val, (char*)value.data(), value.size());
- pj_strdup(jdat->doc->getPool(), &new_val, &str_val);
- pj_json_elem_string(el, &nm, &new_val);
- pj_json_elem_add(jdat->jnode, el);
- }
- static void jsonNode_writeStringVector(ContainerNode *node,
- const string &name,
- const StringVector &value)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- pj_json_elem *el = jdat->doc->allocElement();
- pj_str_t nm = alloc_name(jdat->doc, name);
- pj_json_elem_array(el, &nm);
- for (unsigned i=0; i<value.size(); ++i) {
- pj_str_t new_val;
- pj_str_t str_val;
- pj_strset(&str_val, (char*)value[i].data(), value[i].size());
- pj_strdup(jdat->doc->getPool(), &new_val, &str_val);
- pj_json_elem *child = jdat->doc->allocElement();
- pj_json_elem_string(child, NULL, &new_val);
- pj_json_elem_add(el, child);
- }
- pj_json_elem_add(jdat->jnode, el);
- }
- static ContainerNode jsonNode_writeNewContainer(ContainerNode *node,
- const string &name)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- pj_json_elem *el = jdat->doc->allocElement();
- pj_str_t nm = alloc_name(jdat->doc, name);
- pj_json_elem_obj(el, &nm);
- pj_json_elem_add(jdat->jnode, el);
- ContainerNode json_node;
- json_node.op = &json_op;
- json_node.data.doc = (void*)jdat->doc;
- json_node.data.data1 = (void*)el;
- json_node.data.data2 = (void*)el->value.children.next;
- return json_node;
- }
- static ContainerNode jsonNode_writeNewArray(ContainerNode *node,
- const string &name)
- PJSUA2_THROW(Error)
- {
- json_node_data *jdat = (json_node_data*)&node->data;
- pj_json_elem *el = jdat->doc->allocElement();
- pj_str_t nm = alloc_name(jdat->doc, name);
- pj_json_elem_array(el, &nm);
- pj_json_elem_add(jdat->jnode, el);
- ContainerNode json_node;
- json_node.op = &json_op;
- json_node.data.doc = (void*)jdat->doc;
- json_node.data.data1 = (void*)el;
- json_node.data.data2 = (void*)el->value.children.next;
- return json_node;
- }
|