--- parser3/src/classes/file.C 2007/05/03 10:40:45 1.157 +++ parser3/src/classes/file.C 2008/02/14 09:10:23 1.166 @@ -5,7 +5,7 @@ Author: Alexandr Petrosian (http://paf.design.ru) */ -static const char * const IDENT_FILE_C="$Date: 2007/05/03 10:40:45 $"; +static const char * const IDENT_FILE_C="$Date: 2008/02/14 09:10:23 $"; #include "pa_config_includes.h" @@ -36,9 +36,6 @@ static const char * const IDENT_FILE_C=" #define NAME_NAME "name" -#define FILE_NAME_MUST_BE_STRING "file name must be string" -#define FILE_NAME_MUST_NOT_BE_CODE "file name must not be code" - // externs extern String sql_limit_name; @@ -117,6 +114,10 @@ static const String::Body cdate_name("cd // methods +static bool is_valid_mode (const String& mode) { + return (mode==TEXT_MODE_NAME || mode==BINARY_MODE_NAME); +} + static bool is_text_mode(const String& mode) { if(mode==TEXT_MODE_NAME) return true; @@ -128,7 +129,7 @@ static bool is_text_mode(const String& m } static void _save(Request& r, MethodParams& params) { - Value& vmode_name=params. as_no_junction(0, "mode must not be code"); + Value& vmode_name=params.as_no_junction(0, MODE_MUST_NOT_BE_CODE); Value& vfile_name=params.as_no_junction(1, FILE_NAME_MUST_NOT_BE_CODE); // save @@ -199,7 +200,7 @@ static void _load_pass_param( dest->put(key, value); } static void _load(Request& r, MethodParams& params) { - Value& vmode_name=params. as_no_junction(0, "mode must not be code"); + Value& vmode_name=params.as_no_junction(0, MODE_MUST_NOT_BE_CODE); const String& lfile_name=r.absolute(params.as_no_junction(1, FILE_NAME_MUST_NOT_BE_CODE).as_string()); Value* third_param=params.count()>2?¶ms.as_no_junction(2, "filename or options must not be code") :0; @@ -246,7 +247,7 @@ static void _load(Request& r, MethodPara } static void _create(Request& r, MethodParams& params) { - Value& vmode_name=params. as_no_junction(0, "mode must not be code"); + Value& vmode_name=params.as_no_junction(0, MODE_MUST_NOT_BE_CODE); if(!is_text_mode(vmode_name.as_string())) throw Exception(PARSER_RUNTIME, 0, @@ -350,11 +351,32 @@ static void append_to_argv(Request& r, A } } +inline size_t strpos(const char *s1, const char *s2) { + const char *p = strstr(s1, s2); + return (p==0)?STRING_NOT_FOUND:p-s1; +} + /// @todo fix `` in perl - they produced flipping consoles and no output to perl static void _exec_cgi(Request& r, MethodParams& params, bool cgi) { - Value& vfile_name=params.as_no_junction(0, FILE_NAME_MUST_NOT_BE_CODE); + Value& first_param=params.as_no_junction(0, FIRST_ARG_MUST_NOT_BE_CODE); + + bool is_mode_specified=is_valid_mode(first_param.as_string()); + const String& mode_name=(is_mode_specified) ? first_param.as_string() : *new String(TEXT_MODE_NAME); + + size_t param_index=1; + if(!is_mode_specified){ + --param_index; + } + + if(param_index>=params.count()) + throw Exception(PARSER_RUNTIME, + 0, + "file name must be specified"); + + + Value& vfile_name=params.as_no_junction(param_index++, FILE_NAME_MUST_NOT_BE_CODE); const String& script_name=r.absolute(vfile_name.as_string()); @@ -395,8 +417,8 @@ static void _exec_cgi(Request& r, Method // environment & stdin from param String *in=new String(); Charset *charset=0; // default script works raw_in 'source' charset = no transcoding needed - if(params.count()>1) { - Value& venv=params.as_no_junction(1, "env must not be code"); + if(param_index < params.count()) { + Value& venv=params.as_no_junction(param_index++, "env must not be code"); if(HashStringValue* user_env=venv.get_hash()) { // $.charset [previewing to handle URI pieces] if(Value* vcharset=user_env->get(CHARSET_EXEC_PARAM_NAME)) @@ -429,13 +451,13 @@ static void _exec_cgi(Request& r, Method // argv from params ArrayString argv; - if(params.count()>2) { + if(param_index < params.count()) { // influence tainting // main target -- URLencoding of tainted pieces to String::L_URI lang Temp_client_charset temp(r.charsets, charset? *charset: r.charsets.source()); - for(size_t i=2; ilength && is_text_mode(mode_name)){ + fix_line_breaks(file_out->str, file_out->length); + // treat output as string + String *real_out = new String(file_out->str, file_out->length); + + // transcode out if necessary + if(charset) + real_out=&Charset::transcode(*real_out, *charset, r.charsets.source()); + + // FIXME: unsafe cast + file_out->str=const_cast(real_out->cstr()); // hacking a little + file_out->length = real_out->length(); } VFile& self=GET_SELF(r, VFile); - const String* body=real_out; // ^file:exec - const char* eol_marker=0; size_t eol_marker_size; - const String* header=0; - if(cgi) { // ^file:cgi + if(cgi) { // ^file::cgi + const char* eol_marker=0; + size_t eol_marker_size; + // construct with 'out' body and header - size_t dos_pos=real_out->pos("\r\n\r\n", 4); - size_t unix_pos=real_out->pos("\n\n", 2); + size_t dos_pos=(file_out->length)?strpos(file_out->str, "\r\n\r\n"):STRING_NOT_FOUND; + size_t unix_pos=(file_out->length)?strpos(file_out->str, "\n\n"):STRING_NOT_FOUND; bool unix_header_break; switch((dos_pos!=STRING_NOT_FOUND?10:00) + (unix_pos!=STRING_NOT_FOUND?01:00)) { - case 10: // dos - unix_header_break=false; - break; - case 01: // unix - unix_header_break=true; - break; - case 11: // dos & unix - unix_header_break=unix_poslength(), real_out->cstr(), - (uint)real_err->length(), real_err->cstr()); - break; //never reached + case 10: // dos + unix_header_break=false; + break; + case 01: // unix + unix_header_break=true; + break; + case 11: // dos & unix + unix_header_break=unix_poslength, (file_out->length) ? (file_out->str) : "", + (size_t)real_err->length(), real_err->cstr()); + break; //never reached } - int header_break_pos; + size_t header_break_pos; if(unix_header_break) { header_break_pos=unix_pos; - eol_marker="\n"; eol_marker_size=1; + eol_marker="\n"; + eol_marker_size=1; } else { header_break_pos=dos_pos; - eol_marker="\r\n"; eol_marker_size=2; + eol_marker="\r\n"; + eol_marker_size=2; } - header=&real_out->mid(0, header_break_pos); - body=&real_out->mid(header_break_pos+eol_marker_size*2, real_out->length()); - } - // body - self.set(false/*not tainted*/, body->cstr(), body->length()); - - // $fields << header - if(header && eol_marker) { - ArrayString rows; - size_t pos_after=0; - header->split(rows, pos_after, eol_marker); - Pass_cgi_header_attribute_info info={0, 0, 0}; - info.charset=&r.charsets.source(); - info.fields=&self.fields(); - rows.for_each(pass_cgi_header_attribute, &info); - if(info.content_type) - self.fields().put(content_type_name, info.content_type); + file_out->str[header_break_pos] = 0; + String *header=new String(file_out->str, header_break_pos); + unsigned long headersize = header_break_pos+eol_marker_size*2; + file_out->str += headersize; + file_out->length -= headersize; + + // $body + self.set(false/*not tainted*/, file_out->str, file_out->length); + + // $fields << header + if(header && eol_marker) { + ArrayString rows; + size_t pos_after=0; + header->split(rows, pos_after, eol_marker); + Pass_cgi_header_attribute_info info={0, 0, 0}; + info.charset=&r.charsets.source(); + info.fields=&self.fields(); + rows.for_each(pass_cgi_header_attribute, &info); + if(info.content_type) + self.fields().put(content_type_name, info.content_type); + } + } else { // ^file::exec + // $body + self.set(false/*not tainted*/, file_out->str, file_out->length); } // $status @@ -639,7 +682,11 @@ static void _lock(Request& r, MethodPara ¶ms.as_junction(1, "body must be code") }; - file_write_action_under_lock(file_spec, "lock", lock_execute_body, &info); + file_write_action_under_lock( + file_spec, + "lock", + lock_execute_body, + &info); } static int lastposafter(const String& s, size_t after, const char* substr, size_t substr_size, bool beforelast=false) { @@ -818,7 +865,7 @@ static void _sql(Request& r, MethodParam if(params.count()>1) if(HashStringValue* options= - params.as_no_junction(1, "param must not be code").get_hash()) { + params.as_no_junction(1, PARAM_MUST_NOT_BE_CODE).get_hash()) { int valid_options=0; if(Value* vfilename=options->get(NAME_NAME)) { valid_options++; @@ -864,7 +911,7 @@ static void _base64(Request& r, MethodPa VFile& self=GET_SELF(r, VFile); if(params.count()) { // decode - const char* cstr=params.as_string(0, "parameter must be string").cstr(); + const char* cstr=params.as_string(0, PARAMETER_MUST_BE_STRING).cstr(); char* decoded_cstr=0; size_t decoded_size=0; pa_base64_decode(cstr, strlen(cstr), decoded_cstr, decoded_size); @@ -989,15 +1036,15 @@ MFile::MFile(): Methoded("file") { // ^file::stat[disk-name] add_native_method("stat", Method::CT_DYNAMIC, _stat, 1, 1); - // ^file::cgi[file-name] - // ^file::cgi[file-name;env hash] - // ^file::cgi[file-name;env hash;1cmd;2line;3ar;4g;5s] - add_native_method("cgi", Method::CT_DYNAMIC, _cgi, 1, 2+50); - - // ^file::exec[file-name] - // ^file::exec[file-name;env hash] - // ^file::exec[file-name;env hash;1cmd;2line;3ar;4g;5s] - add_native_method("exec", Method::CT_DYNAMIC, _exec, 1, 2+50); + // ^file::cgi[mode;file-name] + // ^file::cgi[mode;file-name;env hash] + // ^file::cgi[mode;file-name;env hash;1cmd;2line;3ar;4g;5s] + add_native_method("cgi", Method::CT_DYNAMIC, _cgi, 1, 3+50); + + // ^file::exec[mode;file-name] + // ^file::exec[mode;file-name;env hash] + // ^file::exec[mode;file-name;env hash;1cmd;2line;3ar;4g;5s] + add_native_method("exec", Method::CT_DYNAMIC, _exec, 1, 3+50); // ^file:list[path] // ^file:list[path][regexp]