Diff for /parser3/src/classes/json.C between versions 1.23 and 1.35

version 1.23, 2012/06/04 13:46:18 version 1.35, 2015/07/22 18:29:09
Line 12 Line 12
   
 #include "pa_charset.h"  #include "pa_charset.h"
 #include "pa_charsets.h"  #include "pa_charsets.h"
 #include "JSON_parser.h"  #include "pa_json.h"
   
 #ifdef XML  #ifdef XML
 #include "pa_vxdoc.h"  #include "pa_vxdoc.h"
Line 47  struct Json { Line 47  struct Json {
         String::Language taint;          String::Language taint;
   
         bool handle_double;          bool handle_double;
           bool handle_int;
         enum Distinct { D_EXCEPTION, D_FIRST, D_LAST, D_ALL } distinct;          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),           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){}                  request(NULL), charset(acharset), taint(String::L_TAINTED), handle_double(true), handle_int(true), 
                   distinct(D_EXCEPTION){}
   
         bool set_distinct(const String &value){          bool set_distinct(const String &value){
                 if (value == "first") distinct = D_FIRST;                  if (value == "first") distinct = D_FIRST;
Line 91  static void set_json_value(Json *json, V Line 93  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 ?           String::C result = json->charset !=NULL ? 
                 Charset::transcode(String::C(value->vu.str.value, value->vu.str.length), UTF8_charset, *json->charset) :                  Charset::transcode(String::C(value, length), UTF8_charset, *json->charset) :
                 String::C(pa_strdup(value->vu.str.value, value->vu.str.length), value->vu.str.length);                  String::C(pa_strdup(value, length), length);
         return new String(result.str, json->taint, result.length);          return new String(result.str, json->taint, result.length);
 }  }
   
Line 108  static Value *json_hook(Request &r, Junc Line 110  static Value *json_hook(Request &r, Junc
         return &frame.result().as_value();          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) {          switch(type) {
                 case JSON_T_OBJECT_BEGIN:{                  case JSON_OBJECT_BEGIN:{
                         VHash *v = new VHash();                          VHash *v = new VHash();
                         if (json->hook_object){                          if (json->hook_object){
                                 json->key_stack.push(json->key);                                  json->key_stack.push(json->key);
Line 122  static int json_callback(Json *json, int Line 124  static int json_callback(Json *json, int
                         json->stack.push(v);                          json->stack.push(v);
                         break;                          break;
                 }                  }
                 case JSON_T_OBJECT_END:{                  case JSON_OBJECT_END:{
                         if (json->hook_object){                          if (json->hook_object){
                                 String* key = json->key_stack.pop();                                  String* key = json->key_stack.pop();
                                 json->result = json_hook(*json->request, json->hook_object, key, json->stack.pop());                                  json->result = json_hook(*json->request, json->hook_object, key, json->stack.pop());
Line 136  static int json_callback(Json *json, int Line 138  static int json_callback(Json *json, int
                         }                          }
                         break;                          break;
                 }                  }
                 case JSON_T_ARRAY_BEGIN:{                  case JSON_ARRAY_BEGIN:{
                         VHash *v = new VHash();                          VHash *v = new VHash();
                         if (json->hook_array){                          if (json->hook_array){
                                 json->key_stack.push(json->key);                                  json->key_stack.push(json->key);
Line 147  static int json_callback(Json *json, int Line 149  static int json_callback(Json *json, int
                         json->stack.push(v);                          json->stack.push(v);
                         break;                          break;
                 }                  }
                 case JSON_T_ARRAY_END:                  case JSON_ARRAY_END:
                         // libjson supports array at top level, we too                          // libjson supports array at top level, we too
                         if (json->hook_array){                          if (json->hook_array){
                                 String* key = json->key_stack.pop();                                  String* key = json->key_stack.pop();
Line 161  static int json_callback(Json *json, int Line 163  static int json_callback(Json *json, int
                                 json->result = json->stack.pop();                                  json->result = json->stack.pop();
                         }                          }
                         break;                          break;
                 case JSON_T_KEY:                  case JSON_KEY:
                         json->key = json_string(json, value);                          json->key = json_string(json, value, length);
                         break;                          break;
                 case JSON_T_INTEGER:                  case JSON_INT:
                         set_json_value(json, new VDouble((double)value->vu.integer_value));                          if (json->handle_int){
                                   set_json_value(json, new VDouble( json_string(json, value, length)->as_double() ));
                           } else {
                                   // JSON_STRING
                                   set_json_value(json, new VString(*json_string(json, value, length)));
                           }
                         break;                          break;
                 case JSON_T_FLOAT:                  case JSON_FLOAT:
                         if (json->handle_double){                          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;                                  break;
                         } // else is JSON_T_STRING                          } // else is JSON_STRING
                 case JSON_T_STRING:                  case JSON_STRING:
                         set_json_value(json, new VString(*json_string(json, value)));                          set_json_value(json, new VString(*json_string(json, value, length)));
                         break;                          break;
                 case JSON_T_NULL:                  case JSON_NULL:
                         set_json_value(json, VVoid::get());                          set_json_value(json, VVoid::get());
                         break;                          break;
                 case JSON_T_TRUE:                  case JSON_TRUE:
                         set_json_value(json, &VBool::get(true));                          set_json_value(json, &VBool::get(true));
                         break;                          break;
                 case JSON_T_FALSE:                  case JSON_FALSE:
                         set_json_value(json, &VBool::get(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* json_error_message(int error_code){
         static const char* error_messages[] = {          static const char* error_messages[] = {
                 NULL,                  NULL,
                 "invalid char",                  "out of memory",
                 "invalid keyword",                  "bad character",
                 "invalid escape sequence",                  "stack empty",
                 "invalid unicode sequence",                  "pop unexpected mode",
                 "invalid number",                  "nesting limit",
                 "nesting depth reached",                  "data limit",
                 "unbalanced collection",                  "comment not allowed by config",
                 "expected key",                  "unexpected character",
                 "expected colon",                  "missing unicode low surrogate",
                 "out of memory"                  "unexpected unicode low surrogate",
                   "error comma out of structure",
                   "error in a callback"
         };          };
         return error_messages[error_code];          return error_messages[error_code];
 }  }
   
 extern String::Language get_untaint_lang(const String& lang_name);  extern String::Language get_untaint_lang(const String& lang_name);
   
   #define SOURCE_MAX_LEN 60
   
   void json_exception_with_source(Request& r, const char* msg, const char* json, int offset){
           int i;
   
           int line=0;
           int start=0;
           int end=strlen(json);
   
           if(offset>end)
                   offset=end;
   
           for(i = 0; i < offset; i++){
                   if(json[i]=='\n'){
                           line++;
                   }
           }
   
           if(offset > SOURCE_MAX_LEN/2)
                   start = offset - SOURCE_MAX_LEN/2;
   
           for(i = offset-1; i>=start; i--){
                   if(json[i]=='\n'){
                           start=i+1;
                           break;
                   }
           }
   
           if(start+SOURCE_MAX_LEN < end)
                   end=start+SOURCE_MAX_LEN;
   
           for(i = offset+1; i<end; i++){
                   if(json[i]=='\n'){
                           end=i;
                           break;
                   }
           }
   
           char *source = pa_strdup(json+start, end-start);
           int source_offset = offset-start;
   
           if(source[source_offset]=='\n')
                   source[source_offset]=' ';
   
           for(i = 0; i < source_offset; i++){
                   if(source[i]=='\t'){
                           source[i]=' ';
                   }
           }
   
           if(r.charsets.source().isUTF8()){
                   source=(char *)fixUTF8(source);
                   if(source_offset>0){
                           String s_source(pa_strdup(source,source_offset));
                           source_offset=s_source.length(r.charsets.source());
                   }
           }
   
           throw Exception("json.parse", 0, "%s at line %d\n%s\n%*s", msg, line+1, source, source_offset+1, "^");
   }
   
 static void _parse(Request& r, MethodParams& params) {  static void _parse(Request& r, MethodParams& params) {
         const String& json_string=params.as_string(0, "json must be string");          const String& json_string=params.as_string(0, "json must be string");
   
         Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source()));          Json json(r.charsets.source().isUTF8() ? NULL : &(r.charsets.source()));
   
         JSON_config config;          json_config config = {
         init_JSON_config(&config);                  0,              // buffer_initial_size
                   128,            // max_nesting
         config.depth                  = 19;                  0,              // max_data
         config.callback               = (JSON_parser_callback)&json_callback;                  1,              // allow_c_comments
         config.allow_comments         = 1;                  1,              // allow_yaml_comments
         config.handle_floats_manually = 1;                  pa_malloc,
         config.callback_ctx           = &json;                  pa_realloc,
                   pa_free
           };
   
         if(params.count() == 2)          if(params.count() == 2)
                 if(HashStringValue* options=params.as_hash(1)) {                  if(HashStringValue* options=params.as_hash(1)) {
                         int valid_options=0;                          int valid_options=0;
                         if(Value* value=options->get("depth")) {                          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++;                                  valid_options++;
                         }                          }
                         if(Value* value=options->get("double")) {                          if(Value* value=options->get("double")) {
                                 json.handle_double=r.process_to_value(*value).as_bool();                                  json.handle_double=r.process_to_value(*value).as_bool();
                                 valid_options++;                                  valid_options++;
                         }                          }
                           if(Value* value=options->get("int")) {
                                   json.handle_int=r.process_to_value(*value).as_bool();
                                   valid_options++;
                           }
                         if(Value* value=options->get("distinct")) {                          if(Value* value=options->get("distinct")) {
                                 const String& sdistinct=value->as_string();                                  const String& sdistinct=value->as_string();
                                 if (!json.set_distinct(sdistinct))                                  if (!json.set_distinct(sdistinct))
Line 260  static void _parse(Request& r, MethodPar Line 336  static void _parse(Request& r, MethodPar
                                 throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);                                  throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                 }                  }
   
         const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, 0, &(r.charsets));          const String::Body json_body = json_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets);
         const char *json_cstr = json.charset != NULL ? Charset::transcode(json_body, *json.charset, UTF8_charset).cstr() : json_body.cstr();          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);          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))
                   json_exception_with_source(r, json_error_message(result), json_cstr, processed);
   
         for (const char *c=json_cstr; *c; c++){          if (!json_parser_is_done(&parser))
                 if (!JSON_parser_char(jc, *((const unsigned char *)c))) {                  json_exception_with_source(r, "unexpected end of json data", json_cstr, processed);
                         throw Exception("json.parse", 0, "%s at byte %d", json_error_message(JSON_parser_get_last_error(jc)), c-json_cstr);  
                 }  
         }  
   
         if (!JSON_parser_done(jc)) {          json_parser_free(&parser);
                 throw Exception("json.parse", 0, "%s at the end", json_error_message(JSON_parser_get_last_error(jc)));  
         }  
           
         delete_JSON_parser(jc);  
   
         if (json.result) r.write_no_lang(*json.result);          if (json.result) r.write_no_lang(*json.result);
 }  }
   
   const uint ANTI_ENDLESS_JSON_STRING_RECOURSION=128;
   
 char *get_indent(uint level){  char *get_indent(uint level){
         static char* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={};          static char* cache[ANTI_ENDLESS_JSON_STRING_RECOURSION]={};
         if (!cache[level]){          if (!cache[level]){
Line 291  char *get_indent(uint level){ Line 368  char *get_indent(uint level){
         return cache[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& value_json_string(String::Body key, Value& v, Json_options& options);
   
 const String* Json_options::hash_json_string(HashStringValue &hash) {  const String* Json_options::hash_json_string(HashStringValue &hash) {
         if(!hash.count())          if(!hash.count())
                 return new String("{}", String::L_AS_IS);                  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);          String& result = *new String("{\n", String::L_AS_IS);
   
         if (indent){          if (indent){
   
                 String *delim=NULL;                  String *delim=NULL;
                 indent=get_indent(level);                  indent=get_indent(json_string_recoursion);
                 for(HashStringValue::Iterator i(hash); i; i.next() ){                  for(HashStringValue::Iterator i(hash); i; i.next() ){
                         if (delim){                          if (delim){
                                 result << *delim;                                  result << *delim;
Line 314  const String* Json_options::hash_json_st Line 404  const String* Json_options::hash_json_st
                         }                          }
                         result << String(i.key(), String::L_JSON) << "\":" << value_json_string(i.key(), *i.value(), *this);                          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 {          } else {
   
Line 328  const String* Json_options::hash_json_st Line 418  const String* Json_options::hash_json_st
   
         }          }
   
         r->json_string_recoursion_go_up();  
         return &result;          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());          return v->is(key.cstr());
 }  }
                                           
 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) {
         if(options.methods) {          if(options.methods) {
                 Value* method=options.methods->get(v.type());                  Value* method=options.methods->get(v.type());
                 if(!method){                  if(!method){
                         method=options.methods->first_that<Value*>(based_on, &v);                          method=options.methods->first_that<Value*>(based_on, &v);
                         options.methods->put(key, method ? method : VVoid::get());                          options.methods->put(v.type(), method ? method : VVoid::get());
                 }                  }
                 if(method && !method->is_void()) {                  if(method && !method->is_void()) {
                         Junction* junction=method->get_junction();                          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);
   
                           HashStringValue* params_hash=options.params && options.indent ? options.params->get_hash() : NULL;
                           Temp_hash_value<HashStringValue, 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()};                          Value *params[]={new VString(*new String(key, String::L_JSON)), &v, options.params ? options.params : VVoid::get()};
                         frame.store_params(params, 3);                          frame.store_params(params, 3);
   
Line 381  static void _string(Request& r, MethodPa Line 473  static void _string(Request& r, MethodPa
                                                 throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string' or 'unix-timestamp'");                                                  throw Exception(PARSER_RUNTIME, &svalue, "must be 'sql-string', 'gmt-string' or 'unix-timestamp'");
                                         valid_options++;                                          valid_options++;
                                 } else if(key == "indent"){                                  } 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++;                                          valid_options++;
                                 } else if(key == "table" && value->is_string()){                                  } else if(key == "table" && value->is_string()){
                                         const String& svalue=value->as_string();                                          const String& svalue=value->as_string();
Line 393  static void _string(Request& r, MethodPa Line 488  static void _string(Request& r, MethodPa
                                         if(!json.set_file_format(svalue))                                          if(!json.set_file_format(svalue))
                                                 throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'");                                                  throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'");
                                         valid_options++;                                          valid_options++;
                                   } else if(key == "void" && value->is_string()){
                                           const String& svalue=value->as_string();
                                           if(!json.set_void_format(svalue))
                                                   throw Exception(PARSER_RUNTIME, &svalue, "must be 'string' or 'null'");
                                           valid_options++;
 #ifdef XML  #ifdef XML
                                 } else if(key == "xdoc" && (vvalue = value->get_hash())){                                  } 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++;                                          valid_options++;
 #endif  #endif
                                 } else if(Junction* junction=value->get_junction()){                                  } else if(Junction* junction=value->get_junction()){
Line 412  static void _string(Request& r, MethodPa Line 513  static void _string(Request& r, MethodPa
                         // special handling for $._default                           // special handling for $._default 
                         if(VHash* vhash=static_cast<VHash*>(params[1].as(VHASH_TYPE)))                          if(VHash* vhash=static_cast<VHash*>(params[1].as(VHASH_TYPE)))
                                 if(Value* value=vhash->get_default()) {                                  if(Value* value=vhash->get_default()) {
                                           if(!value->is_string()){
                                         Junction* junction=value->get_junction();                                          Junction* junction=value->get_junction();
                                         if(!junction || !junction->method || !junction->method->params_names || junction->method->params_names->count() != 3)                                          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);                                                          throw Exception(PARSER_RUNTIME, 0, "$.%s must be string or parser method with 3 parameters", HASH_DEFAULT_ELEMENT_NAME);
                                           }
                                         json.default_method=value;                                          json.default_method=value;
                                 }                                  }
   
Line 422  static void _string(Request& r, MethodPa Line 525  static void _string(Request& r, MethodPa
                                 json.methods=methods;                                  json.methods=methods;
                 }                  }
   
         const String& result_string=value_json_string(String::Body(), params[0], json);          const String& result_string=value_json_string(String::Body(), r.process_to_value(params[0]), json);
         String::Body result_body=result_string.cstr_to_string_body_untaint(String::L_JSON, 0, &r.charsets);          String::Body result_body=result_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets);
         r.write_pass_lang(*new String(result_body, String::L_AS_IS));          r.write_pass_lang(*new String(result_body, String::L_AS_IS));
  }   }
   

Removed from v.1.23  
changed lines
  Added in v.1.35


E-mail: