Diff for /parser3/src/classes/file.C between versions 1.293 and 1.303

version 1.293, 2024/12/11 02:30:24 version 1.303, 2026/10/02 15:49:42
Line 1 Line 1
 /** @file  /** @file
         Parser: @b file parser class.          Parser: @b file parser class.
   
         Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com)          Copyright (c) 2001-2026 Art. Lebedev Studio (https://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 35  volatile const char * IDENT_FILE_C="$Id$ Line 35  volatile const char * IDENT_FILE_C="$Id$
 #define CHARSET_EXEC_PARAM_NAME "charset"  #define CHARSET_EXEC_PARAM_NAME "charset"
   
 #define NAME_NAME "name"  #define NAME_NAME "name"
   #define MODE_APPEND "append"
 #define KEEP_EMPTY_DIRS_NAME "keep-empty-dirs"  #define KEEP_EMPTY_DIRS_NAME "keep-empty-dirs"
 #define SUPPRESS_EXCEPTION_NAME "exception"  #define SUPPRESS_EXCEPTION_NAME "exception"
   
Line 133  static const String::Body cdate_name("cd Line 134  static const String::Body cdate_name("cd
 // methods  // methods
   
 static void _save(Request& r, MethodParams& params) {  static void _save(Request& r, MethodParams& params) {
           bool do_append=false;
         bool is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_BE_STRING));          bool is_text=VFile::is_text_mode(params.as_string(0, MODE_MUST_BE_STRING));
         const String& file_name=params.as_file_name(1);          const String& file_name=params.as_file_name(1);
   
Line 144  static void _save(Request& r, MethodPara Line 146  static void _save(Request& r, MethodPara
                                 asked_charset=&pa_charsets.get(vcharset_name->as_string());                                  asked_charset=&pa_charsets.get(vcharset_name->as_string());
                                 valid_options++;                                  valid_options++;
                         }                          }
                           if(Value* vappend=options->get(MODE_APPEND)){
                                   do_append=vappend->as_bool();
                                   valid_options++;
                           }
                         if(valid_options != options->count())                          if(valid_options != options->count())
                                 throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);                                  throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                 }                  }
   
         // save          // save
         GET_SELF(r, VFile).save(r.charsets, r.full_disk_path(file_name), is_text, asked_charset);          GET_SELF(r, VFile).save(r.charsets, r.full_disk_path(file_name), is_text, do_append, asked_charset);
 }  }
   
 static void _delete(Request& r, MethodParams& params) {  static void _delete(Request& r, MethodParams& params) {
Line 173  static void _delete(Request& r, MethodPa Line 179  static void _delete(Request& r, MethodPa
                 }                  }
   
         // unlink          // unlink
         file_delete(r.full_disk_path(file_name), fail_on_problem, keep_empty_dirs);          const String* path=fail_on_problem ? &r.full_disk_path(file_name) : r.real_disk_path(file_name);
           if(path)
                   file_delete(*path, fail_on_problem, keep_empty_dirs);
 }  }
   
 static void _move(Request& r, MethodParams& params) {  static void _move(Request& r, MethodParams& params) {
Line 208  static void copy_process_source(struct s Line 216  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 228  static void _copy(Request& r, MethodPara Line 234  static void _copy(Request& r, MethodPara
         if(params.count()>2)          if(params.count()>2)
                 if(HashStringValue* options=params.as_hash(2)){                  if(HashStringValue* options=params.as_hash(2)){
                         int valid_options=0;                          int valid_options=0;
                         if(Value* vappend=options->get("append")){                          if(Value* vappend=options->get(MODE_APPEND)){
                                 append=r.process(*vappend).as_bool();                                  append=r.process(*vappend).as_bool();
                                 valid_options++;                                  valid_options++;
                         }                          }
Line 257  static void _load_pass_param( Line 263  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_BE_STRING));          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_file_name(1));          const String& lfile_name=r.full_disk_path(params.as_file_name(1), PA_ALLOW_HTTP);
   
         size_t param_index=params.count()-1;          size_t param_index=params.count()-1;
         Value* param_value=param_index>1?&params.as_no_junction(param_index, "file name or options must not be code"):0;          Value* param_value=param_index>1?&params.as_no_junction(param_index, "file name or options must not be code"):0;
Line 478  static void append_to_argv(Request& r, A Line 484  static void append_to_argv(Request& r, A
 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 490  static void _exec_cgi(Request& r, Method Line 496  static void _exec_cgi(Request& r, Method
         const String& script_name=r.full_disk_path(params.as_file_name(param_index++));          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 806  static size_t afterlastslash(const Strin Line 812  static size_t afterlastslash(const Strin
         return pos!=STRING_NOT_FOUND?pos+1:0;          return pos!=STRING_NOT_FOUND?pos+1:0;
 }  }
   
   // 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, 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_path;
           size_t document_root_length=strlen(document_root);
   
           const String& result=disk_path->mid(document_root_length, disk_path->length());
   
           // 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) {  static void _find(Request& r, MethodParams& params) {
         const String& file_name=params.as_string(0, FILE_NAME_MUST_BE_STRING);          const String& file_name=params.as_string(0, FILE_NAME_MUST_BE_STRING);
   
         Value* not_found_code=(params.count()==2)?&params.as_junction(1, "not-found param must be code"):0;          Value* not_found_code=(params.count()==2)?&params.as_junction(1, "not-found param must be code"):0;
   
         const String* file_spec;          const String* file_spec;
         if(file_name.first_char()=='/')          if(path_scheme(file_name) || is_os_absolute_path(file_name))
                 file_spec=&file_name;                  file_spec=web_path(r, file_name, false); // do not throw
         else           else {
                 file_spec=&r.relative(r.request_info.uri, file_name);                  const String& name=backslashes_to_slashes(file_name);
                   file_spec=name.first_char()=='/' ? &name : &r.relative(r.request_info.uri, name);
         // easy way  
         if(file_exist(r.full_disk_path(*file_spec))) {  
                 r.write(*file_spec);  
                 return;  
         }          }
   
         // monkey way          if(file_spec) {
         size_t last_slash=file_spec->strrpbrk("/\\");                  // easy way
         const String& dirname=file_spec->mid(0, last_slash!=STRING_NOT_FOUND?last_slash:0);                  if(file_exist(r.full_disk_path(*file_spec))) {
         const String& basename=file_spec->mid(last_slash!=STRING_NOT_FOUND?last_slash+1:0, file_spec->length());                          r.write(*file_spec);
   
         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);  
                 if(slash==STRING_NOT_FOUND)  
                         break;  
                 String test_name;  
                 test_name << dirname.mid(0, slash+1);  
                 test_name << basename;  
                 if(file_exist(r.full_disk_path(test_name))) {  
                         r.write(test_name);  
                         return;                          return;
                 }                  }
                 rpos=slash;  
                   // monkey way
                   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.strrchr('/', rpos);
                           if(slash==STRING_NOT_FOUND)
                                   break;
                           String test_name;
                           test_name << dirname.mid(0, slash+1);
                           test_name << basename;
                           if(file_exist(r.full_disk_path(test_name))) {
                                   r.write(test_name);
                                   return;
                           }
                           rpos=slash;
                   }
         }          }
   
         // no way, not found          // no way, not found
Line 930  static void _justext(Request& r, MethodP Line 960  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_file_spec(0);          const String& file_spec=params.as_file_spec(0);
         const String* result;          if(const String* result=web_path(r, file_spec, true))
         if(file_spec.first_char()=='/')                  r.write(*result);
                 result=&file_spec;          else
         else {                  throw Exception(PARSER_RUNTIME, &file_spec, "is outside of $request:document-root [%s]", r.request_info.document_root);
                 // /some/page.html: ^file:fullpath[a.gif] => /some/a.gif  }
                 const String& full_disk_path=r.full_disk_path(file_spec);  
                 size_t document_root_length=strlen(r.request_info.document_root);  static void _diskpath(Request& r, MethodParams& params) {
           r.write(r.full_disk_path(params.as_file_spec(0)));
                 if(document_root_length>0) {  
                         char last_char=r.request_info.document_root[document_root_length-1];  
                         if(last_char == '/' || last_char == '\\')  
                                 --document_root_length;  
                 }  
                 result=&full_disk_path.mid(document_root_length,  full_disk_path.length());  
         }  
         r.write(*result);  
 }  }
   
 static void _sql_string(Request& r, MethodParams&) {  static void _sql_string(Request& r, MethodParams&) {
Line 1066  extern Base64Options base64_encode_optio Line 1088  extern Base64Options base64_encode_optio
 Base64Options base64_decode_options(Request& r, HashStringValue* options, VString** vcontent_type) {  Base64Options base64_decode_options(Request& r, HashStringValue* options, VString** vcontent_type) {
         Base64Options result;          Base64Options result;
         if(options) {          if(options) {
                 int valid_options=0;  
                 for(HashStringValue::Iterator i(*options); i; i.next() ) {                  for(HashStringValue::Iterator i(*options); i; i.next() ) {
                         String::Body key=i.key();                          String::Body key=i.key();
                         Value* value=i.value();                          Value* value=i.value();
                         if(key == "pad") {                          if(key == "pad") {
                                 result.pad=r.process(*value).as_bool();                                  result.pad=r.process(*value).as_bool();
                                 valid_options++;  
                         } else if(key == "strict") {                          } else if(key == "strict") {
                                 result.strict=r.process(*value).as_bool();                                  result.strict=r.process(*value).as_bool();
                                 valid_options++;  
                         } else if(key == CONTENT_TYPE_NAME) {                          } else if(key == CONTENT_TYPE_NAME) {
                                 *vcontent_type=new VString(value->as_string());                                  *vcontent_type=new VString(value->as_string());
                                 valid_options++;  
                         } else if(key == "url-safe") {                          } else if(key == "url-safe") {
                                 if(r.process(*value).as_bool())                                  if(r.process(*value).as_bool())
                                         result.set_url_safe_abc();                                          result.set_url_safe_abc();
                                 valid_options++;                          } else
                         }                                  throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                 }                  }
   
                 if(valid_options != options->count())  
                         throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);  
         }          }
         return result;          return result;
 }  }
Line 1229  MFile::MFile(): Methoded("file") { Line 1244  MFile::MFile(): Methoded("file") {
         add_native_method("create", Method::CT_DYNAMIC, _create, 1, 4);          add_native_method("create", Method::CT_DYNAMIC, _create, 1, 4);
   
         // ^file.save[mode;file-name]          // ^file.save[mode;file-name]
         // ^file.save[mode;file-name;$.charset[...]]          // ^file.save[mode;file-name;$.charset[...]$.append(false)]
         add_native_method("save", Method::CT_DYNAMIC, _save, 2, 3);          add_native_method("save", Method::CT_DYNAMIC, _save, 2, 3);
   
         // ^file:delete[file-name]          // ^file:delete[file-name]
Line 1282  MFile::MFile(): Methoded("file") { Line 1297  MFile::MFile(): Methoded("file") {
         add_native_method("justext", Method::CT_STATIC, _justext, 1, 1);          add_native_method("justext", Method::CT_STATIC, _justext, 1, 1);
         // /some/page.html: ^file:fullpath[a.gif] => /some/a.gif          // /some/page.html: ^file:fullpath[a.gif] => /some/a.gif
         add_native_method("fullpath", Method::CT_STATIC, _fullpath, 1, 1);          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[]          // ^file.sql-string[]
         add_native_method("sql-string", Method::CT_DYNAMIC, _sql_string, 0, 0);          add_native_method("sql-string", Method::CT_DYNAMIC, _sql_string, 0, 0);

Removed from v.1.293  
changed lines
  Added in v.1.303


E-mail: