--- parser3/src/classes/file.C 2026/09/29 02:56:52 1.300 +++ parser3/src/classes/file.C 2026/10/06 23:57:02 1.306 @@ -5,8 +5,6 @@ Authors: Konstantin Morshnev , Alexandr Petrosian */ -#include "pa_config_includes.h" - #include "classes.h" #include "pa_vmethod_frame.h" #include "pa_base64.h" @@ -27,7 +25,7 @@ #include "pa_vregex.h" #include "pa_version.h" -volatile const char * IDENT_FILE_C="$Id: file.C,v 1.300 2026/09/29 02:56:52 moko Exp $"; +volatile const char * IDENT_FILE_C="$Id: file.C,v 1.306 2026/10/06 23:57:02 moko Exp $"; // defines @@ -707,6 +705,7 @@ static void _list(Request& r, MethodPara Value& relative_path=params.as_no_junction(0, "path must not be code"); bool stat=false; + bool fail_on_problem=false; VRegex* vregex=0; VRegexCleaner vrcleaner; @@ -724,6 +723,10 @@ static void _list(Request& r, MethodPara vfilter=value; valid_options++; } + if(Value* vexception=options->get(SUPPRESS_EXCEPTION_NAME)) { + fail_on_problem=r.process(*vexception).as_bool(); + valid_options++; + } if(valid_options!=options->count()) throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); } else { @@ -745,11 +748,17 @@ 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); - Table::Action_options table_options; Table& table=*new Table(file_list_table_template(), table_options); + // no exception for a uri or an invalid or tainted file:// + const String* path=fail_on_problem ? &r.full_disk_path(relative_path.as_string()) : r.real_disk_path(relative_path.as_string()); + if(!path) { + r.write(*new VTable(&table)); + return; + } + const char* absolute_path_cstr=path->taint_cstr(String::L_FILE_SPEC); + const int ovector_size=(1/*match*/)*3; int ovector[ovector_size]; @@ -769,6 +778,10 @@ static void _list(Request& r, MethodPara } table+=row; } + , + if(fail_on_problem) + throw Exception(errno==EACCES ? "file.access" : (errno==ENOENT || errno==ENOTDIR) ? "file.missing" : 0, + path, "list failed: %s (%d)%s", strerror(errno), errno, actual_filename(*path, absolute_path_cstr)); ); // write out result @@ -814,28 +827,22 @@ static size_t afterlastslash(const Strin // absolute web path or null when outside of document_root static const String* web_path(Request& r, const String& file_spec, bool fail_on_problem) { - if(file_spec.first_char()=='/') // already absolute - return &file_spec; + if(file_spec.first_char()=='/') // already absolute, just changing backslashes + return &backslashes_to_slashes(file_spec); // /some/page.html: ^file:fullpath[a.gif] => /some/a.gif const String* disk_path=fail_on_problem ? &r.full_disk_path(file_spec) : r.real_disk_path(file_spec); if(!disk_path) // a uri or an invalid file:// one return 0; - const char* document_root=r.request_info.document_root; + const char* document_root=r.request_info.document_root_path; size_t document_root_length=strlen(document_root); - if(document_root_length>0) { - char last_char=document_root[document_root_length-1]; - if(last_char == '/' || last_char == '\\') - --document_root_length; - } - const String& result=disk_path->mid(document_root_length, disk_path->length()); - // a disk path may be anywhere, and a relative name too once the script changes document_root; - // first_char checks the segment boundary: /root must not match /rootbeer - return result.first_char()=='/' && !strncmp(disk_path->cstr(), document_root, document_root_length) ? &result : 0; + // checking the result is inside document root; + // first checking the segment boundary: /root must not match /root-but-longer + return result.first_char()=='/' && !path_cmp(disk_path->cstr(), document_root, document_root_length) ? &result : 0; } static void _find(Request& r, MethodParams& params) { @@ -844,12 +851,12 @@ static void _find(Request& r, MethodPara Value* not_found_code=(params.count()==2)?¶ms.as_junction(1, "not-found param must be code"):0; const String* file_spec; - if(file_name.first_char()=='/') - file_spec=&file_name; - else if(path_scheme(file_name) || is_os_absolute_path(file_name)) + if(path_scheme(file_name) || is_os_absolute_path(file_name)) file_spec=web_path(r, file_name, false); // do not throw - else - file_spec=&r.relative(r.request_info.uri, file_name); + else { + const String& name=backslashes_to_slashes(file_name); + file_spec=name.first_char()=='/' ? &name : &r.relative(r.request_info.uri, name); + } if(file_spec) { // easy way @@ -859,13 +866,13 @@ static void _find(Request& r, MethodPara } // monkey way - size_t last_slash=file_spec->strrpbrk("/\\"); + size_t last_slash=file_spec->strrchr('/'); const String& dirname=file_spec->mid(0, last_slash!=STRING_NOT_FOUND?last_slash:0); const String& basename=file_spec->mid(last_slash!=STRING_NOT_FOUND?last_slash+1:0, file_spec->length()); size_t rpos=dirname.is_empty()?0:dirname.length()-1; - while((rpos=dirname.rskipchars("/\\", 0, rpos))!=STRING_NOT_FOUND){ - size_t slash=dirname.strrpbrk("/\\", 0, rpos); + while((rpos=dirname.rskipchars("/", 0, rpos))!=STRING_NOT_FOUND){ + size_t slash=dirname.strrchr('/', rpos); if(slash==STRING_NOT_FOUND) break; String test_name; @@ -972,6 +979,10 @@ static void _fullpath(Request& r, Method throw Exception(PARSER_RUNTIME, &file_spec, "is outside of $request:document-root [%s]", r.request_info.document_root); } +static void _diskpath(Request& r, MethodParams& params) { + r.write(r.full_disk_path(params.as_file_spec(0))); +} + static void _sql_string(Request& r, MethodParams&) { VFile& self=GET_SELF(r, VFile); @@ -1299,6 +1310,8 @@ MFile::MFile(): Methoded("file") { add_native_method("justext", Method::CT_STATIC, _justext, 1, 1); // /some/page.html: ^file:fullpath[a.gif] => /some/a.gif add_native_method("fullpath", Method::CT_STATIC, _fullpath, 1, 1); + // /some/page.html: ^file:diskpath[a.gif] => /document/root/some/a.gif, file:// and C:\ resolved as well + add_native_method("diskpath", Method::CT_STATIC, _diskpath, 1, 1); // ^file.sql-string[] add_native_method("sql-string", Method::CT_DYNAMIC, _sql_string, 0, 0);