Diff for /parser3/src/classes/json.C between versions 1.28 and 1.41

version 1.28, 2013/08/27 11:27:45 version 1.41, 2016/03/31 21:46:20
Line 1 Line 1
 /** @file  /** @file
         Parser: @b json parser class.          Parser: @b json parser class.
   
         Copyright (c) 2000-2012 Art. Lebedev Studio (http://www.artlebedev.com)          Copyright (c) 2000-2015 Art. Lebedev Studio (http://www.artlebedev.com)
 */  */
   
 #include "classes.h"  #include "classes.h"
Line 12 Line 12
   
 #include "pa_charset.h"  #include "pa_charset.h"
 #include "pa_charsets.h"  #include "pa_charsets.h"
 #include "json.h"  #include "pa_json.h"
   
 #ifdef XML  #ifdef XML
 #include "pa_vxdoc.h"  #include "pa_vxdoc.h"
Line 29  public: Line 29  public:
   
 // global variable  // global variable
   
 DECLARE_CLASS_VAR(json, new MJson, 0);  DECLARE_CLASS_VAR(json, new MJson);
   
 // methods  // methods
 struct Json {  struct Json {
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 95  String* json_string(Json *json, const ch Line 97  String* json_string(Json *json, const ch
         String::C result = json->charset !=NULL ?           String::C result = json->charset !=NULL ? 
                 Charset::transcode(String::C(value, length), UTF8_charset, *json->charset) :                  Charset::transcode(String::C(value, length), UTF8_charset, *json->charset) :
                 String::C(pa_strdup(value, length), length);                  String::C(pa_strdup(value, length), length);
         return new String(result.str, json->taint, result.length);          return new String(result, json->taint);
 }  }
   
 static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){  static Value *json_hook(Request &r, Junction *hook, String* key, Value* value){
Line 165  static int json_callback(Json *json, int Line 167  static int json_callback(Json *json, int
                         json->key = json_string(json, value, length);                          json->key = json_string(json, value, length);
                         break;                          break;
                 case JSON_INT:                  case JSON_INT:
                         set_json_value(json, new VDouble( json_string(json, value, length)->as_double() ));                          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_FLOAT:                  case JSON_FLOAT:
                         if (json->handle_double){                          if (json->handle_double){
Line 198  static const char* json_error_message(in Line 205  static const char* json_error_message(in
                 "nesting limit",                  "nesting limit",
                 "data limit",                  "data limit",
                 "comment not allowed by config",                  "comment not allowed by config",
                 "unexpected char",                  "unexpected character",
                 "missing unicode low surrogate",                  "missing unicode low surrogate",
                 "unexpected unicode low surrogate",                  "unexpected unicode low surrogate",
                 "error comma out of structure",                  "error comma out of structure",
Line 209  static const char* json_error_message(in Line 216  static const char* json_error_message(in
   
 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");
   
Line 236  static void _parse(Request& r, MethodPar Line 304  static void _parse(Request& r, MethodPar
                                 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 271  static void _parse(Request& r, MethodPar Line 343  static void _parse(Request& r, MethodPar
         if(int result = json_parser_init(&parser, &config, (json_parser_callback)&json_callback, &json))          if(int result = json_parser_init(&parser, &config, (json_parser_callback)&json_callback, &json))
                 throw Exception("json.parse", 0, "%s", json_error_message(result));                  throw Exception("json.parse", 0, "%s", json_error_message(result));
   
           if(!*json_cstr)
                   throw Exception("json.parse", 0, "empty string is not valid json");
   
           const char *first_quote=strchr(json_cstr,'"');
           if(first_quote && first_quote>json_cstr && *(--first_quote) == '\\')
                   json_exception_with_source(r, "illegal quote escape, json may be tainted", json_cstr, first_quote-json_cstr);
   
         uint32_t processed;          uint32_t processed;
         if(int result = json_parser_string(&parser, json_cstr, strlen(json_cstr), &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);                  json_exception_with_source(r, json_error_message(result), json_cstr, processed);
   
         if (!json_parser_is_done(&parser))          if (!json_parser_is_done(&parser))
                 throw Exception("json.parse", 0, "unexpected end of json data");                  json_exception_with_source(r, "unexpected end of json data", json_cstr, processed);
           
         json_parser_free(&parser);          json_parser_free(&parser);
   
         if (json.result) r.write_no_lang(*json.result);          if (json.result) r.write_no_lang(*json.result);
Line 311  public: Line 390  public:
   
 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 || !hash->count())
                 return new String("{}", String::L_AS_IS);                  return new String("{}", String::L_AS_IS);
   
         Json_string_recoursion go_down(*this);          Json_string_recoursion go_down(*this);
Line 323  const String* Json_options::hash_json_st Line 402  const String* Json_options::hash_json_st
   
                 String *delim=NULL;                  String *delim=NULL;
                 indent=get_indent(json_string_recoursion);                  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;
                         } else {                          } else {
Line 337  const String* Json_options::hash_json_st Line 416  const String* Json_options::hash_json_st
         } else {          } else {
   
                 bool need_delim=false;                  bool need_delim=false;
                 for(HashStringValue::Iterator i(hash); i; i.next() ){                  for(HashStringValue::Iterator i(*hash); i; i.next() ){
                         result << (need_delim ? ",\n\"" : "\"");                          result << (need_delim ? ",\n\"" : "\"");
                         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);
                         need_delim=true;                          need_delim=true;
Line 358  const String& value_json_string(String:: Line 437  const String& value_json_string(String::
                 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();
Line 398  static void _string(Request& r, MethodPa Line 477  static void _string(Request& r, MethodPa
                                 } else if(key == "date" && value->is_string()){                                  } else if(key == "date" && value->is_string()){
                                         const String& svalue=value->as_string();                                          const String& svalue=value->as_string();
                                         if(!json.set_date_format(svalue))                                          if(!json.set_date_format(svalue))
                                                 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', 'iso-string' or 'unix-timestamp'");
                                         valid_options++;                                          valid_options++;
                                 } else if(key == "indent"){                                  } else if(key == "indent"){
                                         if(value->is_string()){                                          if(value->is_string()){
Line 416  static void _string(Request& r, MethodPa Line 495  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();                                          json.xdoc_options=new XDocOutputOptions();
Line 436  static void _string(Request& r, MethodPa Line 520  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 446  static void _string(Request& r, MethodPa Line 532  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, r.connection(false), &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.28  
changed lines
  Added in v.1.41


E-mail: