--- parser3/src/classes/json.C 2011/11/30 06:15:26 1.16 +++ parser3/src/classes/json.C 2012/06/04 13:46:18 1.23 @@ -1,11 +1,9 @@ /** @file Parser: @b json parser class. - Copyright (c) 2010 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2000-2012 Art. Lebedev Studio (http://www.artlebedev.com) */ -static const char * const IDENT_RESPONSE_C="$Date: 2011/11/30 06:15:26 $"; - #include "classes.h" #include "pa_vmethod_frame.h" @@ -20,6 +18,8 @@ static const char * const IDENT_RESPONSE #include "pa_vxdoc.h" #endif +volatile const char * IDENT_JSON_C="$Id: json.C,v 1.23 2012/06/04 13:46:18 moko Exp $"; + // class class MJson: public Methoded { @@ -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; @@ -90,9 +93,9 @@ static void set_json_value(Json *json, V String* json_string(Json *json, const JSON_value* value){ 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->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, json->taint, result.length); } static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){ @@ -162,7 +165,7 @@ static int json_callback(Json *json, int json->key = json_string(json, value); break; case JSON_T_INTEGER: - set_json_value(json, new VInt((int)value->vu.integer_value)); + set_json_value(json, new VDouble((double)value->vu.integer_value)); break; case JSON_T_FLOAT: if (json->handle_double){ @@ -173,7 +176,7 @@ static int json_callback(Json *json, int set_json_value(json, new VString(*json_string(json, value))); break; case JSON_T_NULL: - set_json_value(json, new VVoid()); + set_json_value(json, VVoid::get()); break; case JSON_T_TRUE: set_json_value(json, &VBool::get(true)); @@ -202,6 +205,8 @@ static const char* json_error_message(in 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"); @@ -233,6 +238,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; @@ -282,75 +291,72 @@ char *get_indent(uint level){ return cache[level]; } -const String& value_json_string(String::Body key, Value& v, Json_options* options); +const String& value_json_string(String::Body key, Value& v, Json_options& options); -const String& hash_json_string(HashStringValue &hash, Json_options* options) { +const String* Json_options::hash_json_string(HashStringValue &hash) { if(!hash.count()) - return *new String("{}", String::L_AS_IS); + return new String("{}", String::L_AS_IS); - uint level = options->r->json_string_recoursion_go_down(); + uint level = r->json_string_recoursion_go_down(); String& result = *new String("{\n", String::L_AS_IS); - if (options->indent){ + if (indent){ String *delim=NULL; - options->indent=get_indent(level); + indent=get_indent(level); for(HashStringValue::Iterator i(hash); i; i.next() ){ if (delim){ result << *delim; } else { - result << options->indent << "\""; - delim = new String(",\n", String::L_AS_IS); *delim << options->indent << "\""; + result << indent << "\""; + delim = new String(",\n", String::L_AS_IS); *delim << indent << "\""; } - result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), options); + result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this); } - result << "\n" << (options->indent=get_indent(level-1)) << "}"; + result << "\n" << (indent=get_indent(level-1)) << "}"; } else { bool need_delim=false; for(HashStringValue::Iterator i(hash); i; i.next() ){ result << (need_delim ? ",\n\"" : "\""); - result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), options); + result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this); need_delim=true; } result << "\n}"; } - options->r->json_string_recoursion_go_up(); - return result; + r->json_string_recoursion_go_up(); + return &result; } -static bool based_on( - HashStringValue::key_type key, - HashStringValue::value_type /*value*/, - Value* v) { +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 && options->methods) { - Value* method=options->methods->get(v.type()); - if(!method) - method=options->methods->first_that(based_on, &v); - if(method) { +const String& value_json_string(String::Body key, Value& v, Json_options& options) { + if(options.methods) { + Value* method=options.methods->get(v.type()); + if(!method){ + method=options.methods->first_that(based_on, &v); + options.methods->put(key, method ? method : VVoid::get()); + } + if(method && !method->is_void()) { Junction* junction=method->get_junction(); - VMethodFrame frame(*junction->method, options->r->method_frame, junction->self); + VMethodFrame frame(*junction->method, options.r->method_frame, junction->self); - Value *params[]={new VString(*new String(key, String::L_JSON)), &v, (options->params) ? options->params : VVoid::get()}; + Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()}; frame.store_params(params, 3); - options->r->execute_method(frame); + options.r->execute_method(frame); return frame.result().as_string(); } } - if(HashStringValue* hash=v.get_hash()) - return hash_json_string(*hash, options); - + options.key=key; return *v.get_json_string(options); } @@ -385,7 +391,7 @@ static void _string(Request& r, MethodPa } else if(key == "file" && value->is_string()){ const String& svalue=value->as_string(); if(!json.set_file_format(svalue)) - throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64' or 'text'"); + throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'"); valid_options++; #ifdef XML } else if(key == "xdoc" && (vvalue = value->get_hash())){ @@ -399,13 +405,24 @@ static void _string(Request& r, MethodPa valid_options++; } } + if(valid_options!=options->count()) throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); + + // special handling for $._default + if(VHash* vhash=static_cast(params[1].as(VHASH_TYPE))) + if(Value* value=vhash->get_default()) { + Junction* junction=value->get_junction(); + if(!junction || !junction->method || !junction->method->params_names || junction->method->params_names->count() != 3) + throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", HASH_DEFAULT_ELEMENT_NAME); + json.default_method=value; + } + if(methods->count()) json.methods=methods; } - const String& result_string=value_json_string(String::Body(), params[0], &json); + const String& result_string=value_json_string(String::Body(), params[0], json); String::Body result_body=result_string.cstr_to_string_body_untaint(String::L_JSON, 0, &r.charsets); r.write_pass_lang(*new String(result_body, String::L_AS_IS)); }