|
|
| version 1.282, 2024/10/02 22:54:02 | version 1.296, 2025/05/26 00:52:15 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: @b file parser class. | Parser: @b file parser class. |
| Copyright (c) 2001-2023 Art. Lebedev Studio (http://www.artlebedev.com) | Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com) |
| Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> | Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> |
| */ | */ |
| Line 19 | Line 19 |
| #include "pa_vdate.h" | #include "pa_vdate.h" |
| #include "pa_dir.h" | #include "pa_dir.h" |
| #include "pa_vtable.h" | #include "pa_vtable.h" |
| #include "pa_varray.h" | |
| #include "pa_charset.h" | #include "pa_charset.h" |
| #include "pa_charsets.h" | #include "pa_charsets.h" |
| #include "pa_sql_connection.h" | #include "pa_sql_connection.h" |
| Line 56 public: | Line 57 public: |
| } | } |
| }; | }; |
| Table file_list_table_template(new File_list_table_template_columns); | static Table &file_list_table_template(){ |
| static Table *singleton=NULL; | |
| if(!singleton) | |
| singleton=new Table(new File_list_table_template_columns); | |
| return *singleton; | |
| } | |
| // class | // class |
| Line 127 static const String::Body cdate_name("cd | Line 133 static const String::Body cdate_name("cd |
| // methods | // methods |
| static void _save(Request& r, MethodParams& params) { | static void _save(Request& r, MethodParams& params) { |
| bool is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_NOT_BE_CODE)); | bool is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_BE_STRING)); |
| Value& vfile_name=params.as_no_junction(1, FILE_NAME_MUST_NOT_BE_CODE); | const String& file_name=params.as_file_name(1); |
| Charset* asked_charset=0; | Charset* asked_charset=0; |
| if(params.count()>2) | if(params.count()>2) |
| Line 143 static void _save(Request& r, MethodPara | Line 149 static void _save(Request& r, MethodPara |
| } | } |
| // save | // save |
| GET_SELF(r, VFile).save(r.charsets, r.full_disk_path(vfile_name.as_string()), is_text, asked_charset); | GET_SELF(r, VFile).save(r.charsets, r.full_disk_path(file_name), is_text, asked_charset); |
| } | } |
| static void _delete(Request& r, MethodParams& params) { | static void _delete(Request& r, MethodParams& params) { |
| const String& file_name=params.as_string(0, FILE_NAME_MUST_NOT_BE_CODE); | const String& file_name=params.as_file_name(0); |
| bool keep_empty_dirs=false; | bool keep_empty_dirs=false; |
| bool fail_on_problem=true; | bool fail_on_problem=true; |
| Line 202 static void copy_process_source(struct s | Line 208 static void copy_process_source(struct s |
| nCount = file_block_read(from_file, buffer, sizeof(buffer)); | nCount = file_block_read(from_file, buffer, sizeof(buffer)); |
| int written=write(to_file, buffer, nCount); | int written=write(to_file, buffer, nCount); |
| if( written < 0 ) | if( written < 0 ) |
| throw Exception("file.access", | throw Exception("file.write", 0, "write failed: %s (%d)", strerror(errno), errno); |
| 0, | |
| "write failed: %s (%d)", strerror(errno), errno); | |
| } while(nCount > 0); | } while(nCount > 0); |
| } | } |
| Line 250 static void _load_pass_param( | Line 254 static void _load_pass_param( |
| } | } |
| static void _load(Request& r, MethodParams& params) { | static void _load(Request& r, MethodParams& params) { |
| bool as_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_NOT_BE_CODE)); | bool as_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_BE_STRING)); |
| const String& lfile_name=r.full_disk_path(params.as_string(1, FILE_NAME_MUST_NOT_BE_CODE)); | const String& lfile_name=r.full_disk_path(params.as_file_name(1)); |
| size_t param_index=params.count()-1; | size_t param_index=params.count()-1; |
| Value* param_value=param_index>1?¶ms.as_no_junction(param_index, "file name or options must not be code"):0; | Value* param_value=param_index>1?¶ms.as_no_junction(param_index, "file name or options must not be code"):0; |
| Line 310 static void _create(Request& r, MethodPa | Line 314 static void _create(Request& r, MethodPa |
| if(params.count()>=3){ | if(params.count()>=3){ |
| // old format: ^file::create[text|binary;file-name;string-or-file-content[;options]] | // old format: ^file::create[text|binary;file-name;string-or-file-content[;options]] |
| mode=¶ms.as_string(0, MODE_MUST_NOT_BE_CODE); | mode=¶ms.as_string(0, MODE_MUST_BE_STRING); |
| is_text=VFile::is_text_mode(*mode); | is_text=VFile::is_text_mode(*mode); |
| file_name=¶ms.as_string(1, FILE_NAME_MUST_NOT_BE_CODE); | file_name=¶ms.as_string(1, FILE_NAME_MUST_BE_STRING); |
| content_index=2; | content_index=2; |
| options_index=3; | options_index=3; |
| extended_options=false; | extended_options=false; |
| Line 384 static void _create(Request& r, MethodPa | Line 388 static void _create(Request& r, MethodPa |
| } | } |
| static void _stat(Request& r, MethodParams& params) { | static void _stat(Request& r, MethodParams& params) { |
| const String& lfile_name=params.as_string(0, FILE_NAME_MUST_NOT_BE_CODE); | const String& lfile_name=params.as_file_name(0); |
| uint64_t size; | uint64_t size; |
| time_t atime, mtime, ctime; | time_t atime, mtime, ctime; |
| Line 465 static void pass_cgi_header_attribute( | Line 469 static void pass_cgi_header_attribute( |
| } | } |
| static void append_to_argv(Request& r, ArrayString& argv, const String* str){ | 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 | /// @todo fix `` in perl - they produced flipping consoles and no output to perl |
| static void _exec_cgi(Request& r, MethodParams& params, bool cgi) { | static void _exec_cgi(Request& r, MethodParams& params, bool cgi) { |
| bool is_text=true; | bool is_text=true; |
| size_t param_index=0; | size_t param_index=0; |
| const String& mode=params.as_string(0, FIRST_ARG_MUST_NOT_BE_CODE); | const String& mode=params.as_string(0, "mode must be string"); |
| if(VFile::is_valid_mode(mode)) { | if(VFile::is_valid_mode(mode)) { |
| is_text=VFile::is_text_mode(mode); | is_text=VFile::is_text_mode(mode); |
| param_index++; | param_index++; |
| Line 482 static void _exec_cgi(Request& r, Method | Line 485 static void _exec_cgi(Request& r, Method |
| if(param_index>=params.count()) | if(param_index>=params.count()) |
| throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); | throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); |
| const String& script_name=r.full_disk_path(params.as_string(param_index++, FILE_NAME_MUST_NOT_BE_CODE)); | const String& script_name=r.full_disk_path(params.as_file_name(param_index++)); |
| HashStringString env; | HashStringString env; |
| #define ECSTR(name, value_cstr) if(value_cstr) env.put(#name, value_cstr); | #define ECSTR(name, value_cstr) if(value_cstr) env.put(#name, String::Body(value_cstr)); |
| // passing environment | // passing environment |
| for(SAPI::Env::Iterator i(r.sapi_info); i; i.next() ) | for(SAPI::Env::Iterator i(r.sapi_info); i; i.next() ) |
| env.put(i.key(), i.value() ); | env.put(i.key(), String::Body(i.value()) ); |
| // const | // const |
| ECSTR(GATEWAY_INTERFACE, "CGI/1.1"); | ECSTR(GATEWAY_INTERFACE, "CGI/1.1"); |
| Line 545 static void _exec_cgi(Request& r, Method | Line 548 static void _exec_cgi(Request& r, Method |
| for(size_t i=param_index; i<params.count(); i++) { | for(size_t i=param_index; i<params.count(); i++) { |
| Value& param=params.as_no_junction(i, PARAM_MUST_NOT_BE_CODE); | Value& param=params.as_no_junction(i, PARAM_MUST_NOT_BE_CODE); |
| if(param.is_defined()){ | if(const String *string=param.get_string()){ |
| if(param.is_string()){ | append_to_argv(r, argv, string); |
| append_to_argv(r, argv, param.get_string()); | } else if(Table* table=param.get_table()){ |
| } else { | for(size_t j=0; j<table->count(); j++) |
| Table* table=param.get_table(); | append_to_argv(r, argv, table->get(j)->get(0)); |
| if(table){ | } else if(VArray* array=dynamic_cast<VArray*>(¶m)){ |
| for(size_t j=0; j<table->count(); j++) | for(ArrayValue::Iterator i(array->array()); i; i.next()){ |
| append_to_argv(r, argv, table->get(j)->get(0)); | if(i.value()){ |
| } else { | const String *string=i.value()->get_string(); |
| throw Exception(PARSER_RUNTIME, 0, "param must be string or table"); | 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"); | |
| } | |
| } | |
| // remove trailing empty arguments for backward compatibility | |
| for(ArrayString::ReverseIterator i(argv); i;){ | |
| if(i.prev()->is_empty()){ // here for correct i.index() | |
| argv.remove(i.index()); | |
| } else { | |
| break; | |
| } | } |
| } | } |
| } | } |
| Line 724 static void _list(Request& r, MethodPara | Line 740 static void _list(Request& r, MethodPara |
| const char* absolute_path_cstr=r.full_disk_path(relative_path.as_string()).taint_cstr(String::L_FILE_SPEC); | const char* absolute_path_cstr=r.full_disk_path(relative_path.as_string()).taint_cstr(String::L_FILE_SPEC); |
| Table::Action_options table_options; | Table::Action_options table_options; |
| Table& table=*new Table(file_list_table_template, table_options); | Table& table=*new Table(file_list_table_template(), table_options); |
| const int ovector_size=(1/*match*/)*3; | const int ovector_size=(1/*match*/)*3; |
| int ovector[ovector_size]; | int ovector[ovector_size]; |
| Line 765 static void lock_execute_body(int , void | Line 781 static void lock_execute_body(int , void |
| } | } |
| static void _lock(Request& r, MethodParams& params) { | static void _lock(Request& r, MethodParams& params) { |
| const String& file_spec=r.full_disk_path(params.as_string(0, FILE_NAME_MUST_BE_STRING)); | const String& file_spec=r.full_disk_path(params.as_file_name(0)); |
| Lock_execute_body_info info={ | Lock_execute_body_info info={ |
| &r, | &r, |
| ¶ms.as_junction(1, "body must be code") | ¶ms.as_junction(1, "body must be code") |
| Line 789 static size_t afterlastslash(const Strin | Line 805 static size_t afterlastslash(const Strin |
| } | } |
| static void _find(Request& r, MethodParams& params) { | static void _find(Request& r, MethodParams& params) { |
| const String& file_name=params.as_string(0, FILE_NAME_MUST_NOT_BE_CODE); | const String& file_name=params.as_string(0, FILE_NAME_MUST_BE_STRING); |
| Value* not_found_code=(params.count()==2)?¶ms.as_junction(1, "not-found param must be code"):0; | Value* not_found_code=(params.count()==2)?¶ms.as_junction(1, "not-found param must be code"):0; |
| Line 831 static void _find(Request& r, MethodPara | Line 847 static void _find(Request& r, MethodPara |
| } | } |
| static void _dirname(Request& r, MethodParams& params) { | static void _dirname(Request& r, MethodParams& params) { |
| const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); | const String& file_spec=params.as_file_spec(0); |
| // works as *nix dirname | // works as *nix dirname |
| // empty > . | // empty > . |
| Line 865 static void _dirname(Request& r, MethodP | Line 881 static void _dirname(Request& r, MethodP |
| } | } |
| static void _basename(Request& r, MethodParams& params) { | static void _basename(Request& r, MethodParams& params) { |
| const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); | const String& file_spec=params.as_file_spec(0); |
| // works as *nix basename | // works as *nix basename |
| // empty > . | // empty > . |
| Line 891 static void _basename(Request& r, Method | Line 907 static void _basename(Request& r, Method |
| } | } |
| static void _justname(Request& r, MethodParams& params) { | static void _justname(Request& r, MethodParams& params) { |
| const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); | const String& file_spec=params.as_file_spec(0); |
| // /a/some.tar.gz > some.tar | // /a/some.tar.gz > some.tar |
| // /a/b.c/ > empty | // /a/b.c/ > empty |
| // /a/b.c > b | // /a/b.c > b |
| Line 901 static void _justname(Request& r, Method | Line 917 static void _justname(Request& r, Method |
| } | } |
| static void _justext(Request& r, MethodParams& params) { | static void _justext(Request& r, MethodParams& params) { |
| const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); | const String& file_spec=params.as_file_spec(0); |
| // /a/some.tar.gz > gz | // /a/some.tar.gz > gz |
| // /a/b.c/ > empty | // /a/b.c/ > empty |
| size_t pos=afterlastslash(file_spec); | size_t pos=afterlastslash(file_spec); |
| Line 911 static void _justext(Request& r, MethodP | Line 927 static void _justext(Request& r, MethodP |
| } | } |
| static void _fullpath(Request& r, MethodParams& params) { | static void _fullpath(Request& r, MethodParams& params) { |
| const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); | const String& file_spec=params.as_file_spec(0); |
| const String* result; | const String* result; |
| if(file_spec.first_char()=='/') | if(file_spec.first_char()=='/') |
| result=&file_spec; | result=&file_spec; |
| Line 1093 static void _base64(Request& r, MethodPa | Line 1109 static void _base64(Request& r, MethodPa |
| if(params.count() < 3) | if(params.count() < 3) |
| throw Exception(PARSER_RUNTIME, 0, "constructor cannot have less than 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)); | is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_BE_STRING)); |
| user_file_name=¶ms.as_string(1, FILE_NAME_MUST_BE_STRING); | user_file_name=¶ms.as_string(1, FILE_NAME_MUST_BE_STRING); |
| if(params.count() == 4) | if(params.count() == 4) |
| Line 1119 static void _base64(Request& r, MethodPa | Line 1135 static void _base64(Request& r, MethodPa |
| if(params.count() > 2) | if(params.count() > 2) |
| throw Exception(PARSER_RUNTIME, 0, "accepts maximum 2 parameter(s) (has %d parameters)", params.count()); | throw Exception(PARSER_RUNTIME, 0, "accepts maximum 2 parameter(s) (has %d parameters)", params.count()); |
| const String& file_spec = params.as_string(0, FILE_NAME_MUST_BE_STRING); | const String& file_spec = params.as_file_name(0); |
| File_read_result data = file_read_binary(r.full_disk_path(file_spec), true /*fail on problem*/); | File_read_result data = file_read_binary(r.full_disk_path(file_spec), true /*fail on problem*/); |
| Base64Options options = base64_encode_options(r, params.count() > 1 ? params.as_hash(1) : NULL); | Base64Options options = base64_encode_options(r, params.count() > 1 ? params.as_hash(1) : NULL); |
| Line 1133 static void _crc32(Request& r, MethodPar | Line 1149 static void _crc32(Request& r, MethodPar |
| if(&r.get_self() == file_class) { | if(&r.get_self() == file_class) { |
| // ^file:crc32[file-name] | // ^file:crc32[file-name] |
| if(params.count()) { | if(params.count()) { |
| const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); | const String& file_spec=params.as_file_name(0); |
| crc32=pa_crc32(r.full_disk_path(file_spec)); | crc32=pa_crc32(r.full_disk_path(file_spec)); |
| } else { | } else { |
| throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); | throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); |
| Line 1189 static void _md5(Request& r, MethodParam | Line 1205 static void _md5(Request& r, MethodParam |
| if(&r.get_self() == file_class) { | if(&r.get_self() == file_class) { |
| // ^file:md5[file-name] | // ^file:md5[file-name] |
| if(params.count()) { | if(params.count()) { |
| const String& file_spec=params.as_string(0, FILE_NAME_MUST_BE_STRING); | const String& file_spec=params.as_file_name(0); |
| md5=pa_md5(r.full_disk_path(file_spec)); | md5=pa_md5(r.full_disk_path(file_spec)); |
| } else { | } else { |
| throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); | throw Exception(PARSER_RUNTIME, 0, FILE_NAME_MUST_BE_SPECIFIED); |