--- parser3/src/classes/file.C 2020/12/15 17:10:28 1.274 +++ parser3/src/classes/file.C 2024/11/04 03:53:25 1.285 @@ -1,8 +1,8 @@ /** @file Parser: @b file parser class. - Copyright (c) 2001-2020 Art. Lebedev Studio (http://www.artlebedev.com) - Author: Alexandr Petrosian (http://paf.design.ru) + Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com) + Authors: Konstantin Morshnev , Alexandr Petrosian */ #include "pa_config_includes.h" @@ -19,6 +19,7 @@ #include "pa_vdate.h" #include "pa_dir.h" #include "pa_vtable.h" +#include "pa_varray.h" #include "pa_charset.h" #include "pa_charsets.h" #include "pa_sql_connection.h" @@ -26,7 +27,7 @@ #include "pa_vregex.h" #include "pa_version.h" -volatile const char * IDENT_FILE_C="$Id: file.C,v 1.274 2020/12/15 17:10:28 moko Exp $"; +volatile const char * IDENT_FILE_C="$Id: file.C,v 1.285 2024/11/04 03:53:25 moko Exp $"; // defines @@ -345,7 +346,7 @@ static void _create(Request& r, MethodPa } if(Value* vcharset_name=options->get(PA_CHARSET_NAME)) { if(to_charset) - throw Exception(PARSER_RUNTIME, 0, "charset option can not be used with to-charset"); + throw Exception(PARSER_RUNTIME, 0, "'charset' option cannot be used together with 'to-charset' option"); to_charset=&pa_charsets.get(vcharset_name->as_string()); valid_options++; } @@ -365,7 +366,7 @@ static void _create(Request& r, MethodPa String::Body body=content_str->cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets); // explode content, honor tainting changes self.set(true/*tainted*/, is_text, body.cstrm(), body.length(), file_name, vcontent_type, &r); } else { - VFile& fcontent=*vcontent.as_vfile(String::L_AS_IS); // can't be null + VFile& fcontent=*vcontent.as_vfile(); // can't be null if(mode){ self.set(fcontent, &is_text, file_name, vcontent_type, &r); if(is_text && !fcontent.is_text_mode()) @@ -380,7 +381,7 @@ static void _create(Request& r, MethodPa if(is_text) self.transcode(from_charset ? *from_charset : r.charsets.source(), to_charset ? *to_charset : r.charsets.source()); else - throw Exception(PARSER_RUNTIME, 0, "charset options can not be used with binary content"); + throw Exception(PARSER_RUNTIME, 0, "charset options cannot be used with binary content"); } static void _stat(Request& r, MethodParams& params) { @@ -465,8 +466,7 @@ static void pass_cgi_header_attribute( } static void append_to_argv(Request& r, ArrayString& argv, const String* str){ - if(!str->is_empty()) - argv+=new String(str->cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets), String::L_AS_IS); + argv+=new String(str->cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets), String::L_AS_IS); } /// @todo fix `` in perl - they produced flipping consoles and no output to perl @@ -500,7 +500,7 @@ static void _exec_cgi(Request& r, Method ECSTR(QUERY_STRING, r.request_info.query_string); ECSTR(REQUEST_URI, r.request_info.uri); ECSTR(CONTENT_TYPE, r.request_info.content_type); - ECSTR(CONTENT_LENGTH, format(r.request_info.content_length, "%u")); + ECSTR(CONTENT_LENGTH, pa_uitoa(r.request_info.content_length)); // SCRIPT_* env.put("SCRIPT_NAME", script_name); @@ -528,7 +528,7 @@ static void _exec_cgi(Request& r, Method // untaint stdin in = String::C(sstdin->cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets)); } else - if(VFile* vfile=static_cast(info.vstdin->as("file"))){ + if(VFile* vfile=dynamic_cast(info.vstdin)){ in = String::C((const char* )vfile->value_ptr(), vfile->value_size()); in_is_text_mode = vfile->is_text_mode(); } else @@ -545,18 +545,22 @@ static void _exec_cgi(Request& r, Method for(size_t i=param_index; icount(); j++) - append_to_argv(r, argv, table->get(j)->get(0)); - } else { - throw Exception(PARSER_RUNTIME, 0, "param must be string or table"); + if(const String *string=param.get_string()){ + append_to_argv(r, argv, string); + } else if(Table* table=param.get_table()){ + for(size_t j=0; jcount(); j++) + append_to_argv(r, argv, table->get(j)->get(0)); + } else if(VArray* array=dynamic_cast(¶m)){ + for(ArrayValue::Iterator i(array->array()); i; i.next()){ + if(i.value()){ + const String *string=i.value()->get_string(); + if(!string) + i.value()->bark("array element is '%s', it does not have string value"); + append_to_argv(r, argv, string); } } + } else { + throw Exception(PARSER_RUNTIME, 0, "param must be string or table or array of strings"); } } } @@ -706,8 +710,8 @@ static void _list(Request& r, MethodPara vfilter=&voption; } if(vfilter) { - if(Value* value=vfilter->as(VREGEX_TYPE)) { - vregex=static_cast(value); + if(VRegex* value=dynamic_cast(vfilter)) { + vregex=value; } else if(vfilter->is_string()) { if(!vfilter->get_string()->trim().is_empty()) { vregex=new VRegex(r.charsets.source(), &vfilter->as_string(), 0/*options*/); @@ -736,12 +740,12 @@ static void _list(Request& r, MethodPara if(!vregex || vregex->exec(file_name_cstr, file_name_size, ovector, ovector_size)>=0) { Table::element_type row(new ArrayString); *row+=new String(pa_strdup(file_name_cstr, file_name_size), String::L_TAINTED); - *row+=new String(String::Body::Format(ffblk.is_dir(stat) ? 1 : 0), String::L_CLEAN); + *row+=new String(ffblk.is_dir(stat) ? "1" : "0", String::L_CLEAN); if(stat) { *row+=VDouble(ffblk.size()).get_string(); - *row+=new String(String::Body::Format((int)ffblk.c_timestamp()), String::L_CLEAN); - *row+=new String(String::Body::Format((int)ffblk.m_timestamp()), String::L_CLEAN); - *row+=new String(String::Body::Format((int)ffblk.a_timestamp()), String::L_CLEAN); + *row+=new String(pa_uitoa(ffblk.c_timestamp()), String::L_CLEAN); + *row+=new String(pa_uitoa(ffblk.m_timestamp()), String::L_CLEAN); + *row+=new String(pa_uitoa(ffblk.a_timestamp()), String::L_CLEAN); } table+=row; } @@ -941,6 +945,7 @@ static void _sql_string(Request& r, Meth class File_sql_event_handlers: public SQL_Driver_query_event_handlers { int got_columns; int got_cells; + bool got_row; public: String::C value; const String* user_file_name; @@ -949,18 +954,26 @@ public: File_sql_event_handlers(): got_columns(0), got_cells(0), + got_row(false), user_file_name(0), user_content_type(0) {} bool add_column(SQL_Error& error, const char* /*str*/, size_t /*length*/) { if(got_columns++==3) { - error=SQL_Error("result must contain not more then 3 columns"); + error=SQL_Error("result must contain no more than 3 columns"); return true; } return false; } bool before_rows(SQL_Error& /*error*/ ) { /* ignore */ return false; } - bool add_row(SQL_Error& /*error*/) { /* ignore */ return false; } + bool add_row(SQL_Error& error) { + if(got_row) { + error=SQL_Error("result must contain no more than 1 row"); + return true; + } + got_row=true; + return false; + } bool add_row_cell(SQL_Error& error, const char* str, size_t length) { try { switch(got_cells++) { @@ -976,7 +989,7 @@ public: user_content_type=new String(str, String::L_TAINTED); break; default: - error=SQL_Error("result must not contain more then one row, three columns"); + error=SQL_Error("result must contain no more than 1 row and 3 columns"); return true; } return false; @@ -1082,7 +1095,7 @@ static void _base64(Request& r, MethodPa if(params.count() > 1) { if(params.count() < 3) - throw Exception(PARSER_RUNTIME, 0, "constructor can not have less then 3 parameters (has %d parameters)", params.count()); // actually it accepts 1 parameter (backward) + throw Exception(PARSER_RUNTIME, 0, "constructor cannot have less than 3 parameters (has %d parameters)", params.count()); // actually it accepts 1 parameter (backward) is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_NOT_BE_CODE)); user_file_name=¶ms.as_string(1, FILE_NAME_MUST_BE_STRING); @@ -1120,7 +1133,7 @@ static void _base64(Request& r, MethodPa } static void _crc32(Request& r, MethodParams& params) { - unsigned long crc32 = 0; + uint crc32 = 0; if(&r.get_self() == file_class) { // ^file:crc32[file-name] if(params.count()) { @@ -1134,7 +1147,7 @@ static void _crc32(Request& r, MethodPar VFile& self=GET_SELF(r, VFile); crc32=pa_crc32(self.value_ptr(), self.value_size()); } - r.write(*new VInt(crc32)); + r.write(*new VDouble(crc32)); }