--- parser3/src/classes/json.C 2012/05/29 21:57:01 1.22 +++ parser3/src/classes/json.C 2013/08/21 14:47:16 1.27 @@ -12,13 +12,13 @@ #include "pa_charset.h" #include "pa_charsets.h" -#include "JSON_parser.h" +#include "json.h" #ifdef XML #include "pa_vxdoc.h" #endif -volatile const char * IDENT_JSON_C="$Id: json.C,v 1.22 2012/05/29 21:57:01 moko Exp $"; +volatile const char * IDENT_JSON_C="$Id: json.C,v 1.27 2013/08/21 14:47:16 moko Exp $"; // class @@ -44,10 +44,13 @@ struct Json { Request* request; Charset *charset; + String::Language taint; + bool handle_double; enum Distinct { D_EXCEPTION, D_FIRST, D_LAST, D_ALL } distinct; - Json(Charset* acharset): stack(), key_stack(), key(NULL), result(NULL), hook_object(NULL), hook_array(NULL), request(NULL), charset(acharset), handle_double(true), distinct(D_EXCEPTION){} + Json(Charset* acharset): stack(), key_stack(), key(NULL), result(NULL), hook_object(NULL), hook_array(NULL), + request(NULL), charset(acharset), taint(String::L_TAINTED), handle_double(true), distinct(D_EXCEPTION){} bool set_distinct(const String &value){ if (value == "first") distinct = D_FIRST; @@ -88,11 +91,11 @@ static void set_json_value(Json *json, V } } -String* json_string(Json *json, const JSON_value* value){ +String* json_string(Json *json, const char *value, uint32_t length){ String::C result = json->charset !=NULL ? - Charset::transcode(String::C(value->vu.str.value, value->vu.str.length), UTF8_charset, *json->charset) : - String::C(pa_strdup(value->vu.str.value, value->vu.str.length), value->vu.str.length); - return new String(result.str, String::L_TAINTED, result.length); + Charset::transcode(String::C(value, length), UTF8_charset, *json->charset) : + String::C(pa_strdup(value, length), length); + return new String(result.str, json->taint, result.length); } static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){ @@ -105,10 +108,10 @@ static Value *json_hook(Request &r, Junc return &frame.result().as_value(); } -static int json_callback(Json *json, int type, const JSON_value* value) +static int json_callback(Json *json, int type, const char *value, uint32_t length) { switch(type) { - case JSON_T_OBJECT_BEGIN:{ + case JSON_OBJECT_BEGIN:{ VHash *v = new VHash(); if (json->hook_object){ json->key_stack.push(json->key); @@ -119,7 +122,7 @@ static int json_callback(Json *json, int json->stack.push(v); break; } - case JSON_T_OBJECT_END:{ + case JSON_OBJECT_END:{ if (json->hook_object){ String* key = json->key_stack.pop(); json->result = json_hook(*json->request, json->hook_object, key, json->stack.pop()); @@ -133,7 +136,7 @@ static int json_callback(Json *json, int } break; } - case JSON_T_ARRAY_BEGIN:{ + case JSON_ARRAY_BEGIN:{ VHash *v = new VHash(); if (json->hook_array){ json->key_stack.push(json->key); @@ -144,7 +147,7 @@ static int json_callback(Json *json, int json->stack.push(v); break; } - case JSON_T_ARRAY_END: + case JSON_ARRAY_END: // libjson supports array at top level, we too if (json->hook_array){ String* key = json->key_stack.pop(); @@ -158,69 +161,75 @@ static int json_callback(Json *json, int json->result = json->stack.pop(); } break; - case JSON_T_KEY: - json->key = json_string(json, value); + case JSON_KEY: + json->key = json_string(json, value, length); break; - case JSON_T_INTEGER: - set_json_value(json, new VDouble((double)value->vu.integer_value)); + case JSON_INT: + set_json_value(json, new VDouble( json_string(json, value, length)->as_double() )); break; - case JSON_T_FLOAT: + case JSON_FLOAT: if (json->handle_double){ - set_json_value(json, new VDouble( json_string(json, value)->as_double() )); + set_json_value(json, new VDouble( json_string(json, value, length)->as_double() )); break; - } // else is JSON_T_STRING - case JSON_T_STRING: - set_json_value(json, new VString(*json_string(json, value))); + } // else is JSON_STRING + case JSON_STRING: + set_json_value(json, new VString(*json_string(json, value, length))); break; - case JSON_T_NULL: + case JSON_NULL: set_json_value(json, VVoid::get()); break; - case JSON_T_TRUE: + case JSON_TRUE: set_json_value(json, &VBool::get(true)); break; - case JSON_T_FALSE: + case JSON_FALSE: set_json_value(json, &VBool::get(false)); - break; + break; } - return 1; + return 0; } static const char* json_error_message(int error_code){ static const char* error_messages[] = { NULL, - "invalid char", - "invalid keyword", - "invalid escape sequence", - "invalid unicode sequence", - "invalid number", - "nesting depth reached", - "unbalanced collection", - "expected key", - "expected colon", - "out of memory" + "out of memory", + "bad character", + "stack empty", + "pop unexpected mode", + "nesting limit", + "data limit", + "comment not allowed by config", + "unexpected char", + "missing unicode low surrogate", + "unexpected unicode low surrogate", + "error comma out of structure", + "error in a callback" }; return error_messages[error_code]; } +extern String::Language get_untaint_lang(const String& lang_name); + static void _parse(Request& r, MethodParams& params) { const String& json_string=params.as_string(0, "json must be string"); Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source())); - JSON_config config; - init_JSON_config(&config); - - config.depth = 19; - config.callback = (JSON_parser_callback)&json_callback; - config.allow_comments = 1; - config.handle_floats_manually = 1; - config.callback_ctx = &json; + json_config config = { + 0, // buffer_initial_size + 128, // max_nesting + 0, // max_data + 1, // allow_c_comments + 1, // allow_yaml_comments + pa_malloc, + pa_realloc, + pa_free + }; if(params.count() == 2) if(HashStringValue* options=params.as_hash(1)) { int valid_options=0; if(Value* value=options->get("depth")) { - config.depth=r.process_to_value(*value).as_int(); + config.max_nesting=r.process_to_value(*value).as_int(); valid_options++; } if(Value* value=options->get("double")) { @@ -233,6 +242,10 @@ static void _parse(Request& r, MethodPar throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'first', 'last' or 'all'"); valid_options++; } + if(Value* value=options->get("taint")) { + json.taint=get_untaint_lang(value->as_string()); + valid_options++; + } if(Value* value=options->get("object")) { json.hook_object=value->get_junction(); json.request=&r; @@ -254,23 +267,24 @@ static void _parse(Request& r, MethodPar const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, 0, &(r.charsets)); const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, UTF8_charset).cstr() : json_body.cstr(); - struct JSON_parser_struct* jc = new_JSON_parser(&config); - - for (const char *c=json_cstr; *c; c++){ - if (!JSON_parser_char(jc, *((const unsigned char *)c))) { - throw Exception("json.parse", 0, "%s at byte %d", json_error_message(JSON_parser_get_last_error(jc)), c-json_cstr); - } - } + json_parser parser; + if(int result = json_parser_init(&parser, &config, (json_parser_callback)&json_callback, &json)) + throw Exception("json.parse", 0, "%s", json_error_message(result)); + + uint32_t processed; + if(int result = json_parser_string(&parser, json_cstr, strlen(json_cstr), &processed)) + throw Exception("json.parse", 0, "%s at byte %d", json_error_message(result), processed); - if (!JSON_parser_done(jc)) { - throw Exception("json.parse", 0, "%s at the end", json_error_message(JSON_parser_get_last_error(jc))); - } + if (!json_parser_is_done(&parser)) + throw Exception("json.parse", 0, "unexpected end of json data"); - delete_JSON_parser(jc); + json_parser_free(&parser); if (json.result) r.write_no_lang(*json.result); } +const uint ANTI_ENDLESS_JSON_STRING_RECOURSION=128; + char *get_indent(uint level){ static char* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={}; if (!cache[level]){ @@ -282,20 +296,33 @@ char *get_indent(uint level){ return cache[level]; } +class Json_string_recoursion { + Json_options& foptions; +public: + Json_string_recoursion(Json_options& aoptions) : foptions(aoptions) { + if(++foptions.json_string_recoursion==ANTI_ENDLESS_JSON_STRING_RECOURSION) + throw Exception(PARSER_RUNTIME, 0, "call canceled - endless json recursion detected"); + } + ~Json_string_recoursion() { + if(foptions.json_string_recoursion) + foptions.json_string_recoursion--; + } +}; + const String& value_json_string(String::Body key, Value& v, Json_options& options); const String* Json_options::hash_json_string(HashStringValue &hash) { if(!hash.count()) return new String("{}", String::L_AS_IS); - uint level = r->json_string_recoursion_go_down(); + Json_string_recoursion go_down(*this); String& result = *new String("{\n", String::L_AS_IS); if (indent){ String *delim=NULL; - indent=get_indent(level); + indent=get_indent(json_string_recoursion); for(HashStringValue::Iterator i(hash); i; i.next() ){ if (delim){ result << *delim; @@ -305,7 +332,7 @@ const String* Json_options::hash_json_st } result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this); } - result << "\n" << (indent=get_indent(level-1)) << "}"; + result << "\n" << (indent=get_indent(json_string_recoursion-1)) << "}"; } else { @@ -319,14 +346,13 @@ const String* Json_options::hash_json_st } - r->json_string_recoursion_go_up(); return &result; } static bool based_on(HashStringValue::key_type key, HashStringValue::value_type /*value*/, Value* v) { return v->is(key.cstr()); } - + const String& value_json_string(String::Body key, Value& v, Json_options& options) { if(options.methods) { Value* method=options.methods->get(v.type()); @@ -338,6 +364,9 @@ const String& value_json_string(String:: Junction* junction=method->get_junction(); VMethodFrame frame(*junction->method, options.r->method_frame, junction->self); + HashStringValue* params_hash=options.params && options.indent ? options.params->get_hash() : NULL; + Temp_hash_value indent(params_hash, "indent", new VString(*new String(options.indent, String::L_AS_IS))); + Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()}; frame.store_params(params, 3); @@ -372,7 +401,10 @@ static void _string(Request& r, MethodPa throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string' or 'unix-timestamp'"); valid_options++; } else if(key == "indent"){ - json.indent=r.process_to_value(*value).as_bool() ? "":NULL; + if(value->is_string()){ + json.indent=value->as_string().cstr(); + json.json_string_recoursion=strlen(json.indent); + } else json.indent=r.process_to_value(*value).as_bool() ? "" : NULL; valid_options++; } else if(key == "table" && value->is_string()){ const String& svalue=value->as_string(); @@ -386,7 +418,8 @@ static void _string(Request& r, MethodPa valid_options++; #ifdef XML } else if(key == "xdoc" && (vvalue = value->get_hash())){ - json.xdoc_options=new XDocOutputOptions(r, vvalue); + json.xdoc_options=new XDocOutputOptions(); + json.xdoc_options->append(r, vvalue); valid_options++; #endif } else if(Junction* junction=value->get_junction()){