From 25ca2a879bcaa6db6f8214ca3753dbc809226188 Mon Sep 17 00:00:00 2001 From: parkedhampster Date: Tue, 26 Mar 2024 11:21:50 -0400 Subject: [PATCH 1/2] Increase filename limit to ext4 character limit Purpose of this PR: - ext4 filesystems permit a filename limit of 255 characters - changes arbitrary 100-character limit to the above limit - many site builders will create minified files with names over 100 characters in length - adds the limit as part of the error return as well - this is hard-coded currently (I am not intimately familiar with ruby) --- app/api.rb | 99 +++++++++++++++++++++------------------------ models/site_file.rb | 59 +++++++++++---------------- 2 files changed, 70 insertions(+), 88 deletions(-) diff --git a/app/api.rb b/app/api.rb index e81fe3df..a20e1ad7 100644 --- a/app/api.rb +++ b/app/api.rb @@ -2,14 +2,14 @@ require 'base64' get '/api' do @title = 'Developers API' - erb :'api' + erb :api end post '/api/upload_hash' do require_api_credentials res = {} files = [] - params.each do |k,v| + params.each do |k, v| res[k] = current_site.sha1_hash_match? k, v end api_success files: res @@ -20,11 +20,11 @@ get '/api/list' do files = [] - if params[:path].nil? || params[:path].empty? - file_list = current_site.site_files - else - file_list = current_site.file_list params[:path] - end + file_list = if params[:path].nil? || params[:path].empty? + current_site.site_files + else + current_site.file_list params[:path] + end file_list.each do |file| new_file = {} @@ -36,7 +36,7 @@ get '/api/list' do files << new_file end - files.each {|f| f[:path].sub!(/^\//, '')} + files.each { |f| f[:path].sub!(%r{^/}, '') } api_success files: files end @@ -46,20 +46,18 @@ def extract_files(params, files = []) if params.is_a?(Array) params.each do |item| # Call extract_files on each item if it's an Array or Hash to handle nested structures - if item.is_a?(Array) || item.is_a?(Hash) - extract_files(item, files) - end + extract_files(item, files) if item.is_a?(Array) || item.is_a?(Hash) end elsif params.is_a?(Hash) - params.each do |key, value| + params.each do |_key, value| # If the value is a Hash and contains a :tempfile key, it's considered an uploaded file. if value.is_a?(Hash) && value.has_key?(:tempfile) && !value[:tempfile].nil? - files << {filename: value[:name], tempfile: value[:tempfile]} + files << { filename: value[:name], tempfile: value[:tempfile] } elsif value.is_a?(Array) value.each do |val| if val.is_a?(Hash) && val.has_key?(:tempfile) && !val[:tempfile].nil? # Directly add the file info if it's an uploaded file within an array - files << {filename: val[:name], tempfile: val[:tempfile]} + files << { filename: val[:name], tempfile: val[:tempfile] } elsif val.is_a?(Hash) || val.is_a?(Array) # Recursively search for more files if the element is a Hash or Array extract_files(val, files) @@ -78,40 +76,38 @@ post '/api/upload' do require_api_credentials files = extract_files params - if !params[:username].blank? + unless params[:username].blank? site = Site[username: params[:username]] - if site.nil? || site.is_deleted - api_error 400, 'site_not_found', "could not find site" - end + api_error 400, 'site_not_found', 'could not find site' if site.nil? || site.is_deleted if site.owned_by?(current_site) @_site = site else - api_error 400, 'site_not_allowed', "not allowed to change this site with your current logged in site" + api_error 400, 'site_not_allowed', 'not allowed to change this site with your current logged in site' end end api_error 400, 'missing_files', 'you must provide files to upload' if files.empty? - uploaded_size = files.collect {|f| f[:tempfile].size}.inject{|sum,x| sum + x } + uploaded_size = files.collect { |f| f[:tempfile].size }.inject { |sum, x| sum + x } if current_site.file_size_too_large? uploaded_size api_error 400, 'too_large', 'files are too large to fit in your space, try uploading smaller (or less) files' end if current_site.too_many_files?(files.length) - api_error 400, 'too_many_files', "cannot exceed the maximum site files limit (#{current_site.plan_feature(:maximum_site_files)})" + api_error 400, 'too_many_files', + "cannot exceed the maximum site files limit (#{current_site.plan_feature(:maximum_site_files)})" end files.each do |file| - if !current_site.okay_to_upload?(file) - api_error 400, 'invalid_file_type', "#{file[:filename]} is not an allowed file type for free sites, supporter required" + unless current_site.okay_to_upload?(file) + api_error 400, 'invalid_file_type', + "#{file[:filename]} is not an allowed file type for free sites, supporter required" end - if File.directory? file[:filename] - api_error 400, 'directory_exists', "#{file[:filename]} being used by a directory" - end + api_error 400, 'directory_exists', "#{file[:filename]} being used by a directory" if File.directory? file[:filename] if current_site.file_size_too_large? file[:tempfile].size api_error 400, 'file_too_large' "#{file[:filename]} is too large" @@ -122,7 +118,7 @@ post '/api/upload' do end if SiteFile.name_too_long? file[:filename] - api_error 400, 'file_name_too_long', "#{file[:filename]} filename is too long" + api_error 400, 'file_name_too_long', "#{file[:filename]} filename is too long. (Longer than 255 characters)" end end @@ -133,24 +129,21 @@ end post '/api/rename' do require_api_credentials - api_error 400, 'missing_arguments', 'you must provide path and new_path' if params[:path].blank? || params[:new_path].blank? + if params[:path].blank? || params[:new_path].blank? + api_error 400, 'missing_arguments', + 'you must provide path and new_path' + end path = current_site.scrubbed_path params[:path] new_path = current_site.scrubbed_path params[:new_path] - unless path.is_a?(String) - api_error 400, 'bad_path', "#{path} is not a valid path, cancelled renaming" - end + api_error 400, 'bad_path', "#{path} is not a valid path, cancelled renaming" unless path.is_a?(String) - unless new_path.is_a?(String) - api_error 400, 'bad_new_path', "#{new_path} is not a valid new_path, cancelled renaming" - end + api_error 400, 'bad_new_path', "#{new_path} is not a valid new_path, cancelled renaming" unless new_path.is_a?(String) - site_file = current_site.site_files.select {|sf| sf.path == path}.first + site_file = current_site.site_files.select { |sf| sf.path == path }.first - if site_file.nil? - api_error 400, 'missing_file', "could not find #{path}" - end + api_error 400, 'missing_file', "could not find #{path}" if site_file.nil? res = site_file.rename new_path @@ -164,23 +157,24 @@ end post '/api/delete' do require_api_credentials - api_error 400, 'missing_filenames', 'you must provide files to delete' if params[:filenames].nil? || params[:filenames].empty? + if params[:filenames].nil? || params[:filenames].empty? + api_error 400, 'missing_filenames', + 'you must provide files to delete' + end paths = [] params[:filenames].each do |path| - unless path.is_a?(String) - api_error 400, 'bad_filename', "#{path} is not a valid filename, canceled deleting" - end + api_error 400, 'bad_filename', "#{path} is not a valid filename, canceled deleting" unless path.is_a?(String) if current_site.files_path(path) == current_site.files_path api_error 400, 'cannot_delete_site_directory', 'cannot delete the root directory of the site' end - if !current_site.file_exists?(path) + unless current_site.file_exists?(path) api_error 400, 'missing_files', "#{path} was not found on your site, canceled deleting" end - if path == 'index.html' || path == '/index.html' + if ['index.html', '/index.html'].include?(path) api_error 400, 'cannot_delete_index', 'you cannot delete your index.html file, canceled deleting' end @@ -220,7 +214,7 @@ def api_info_for(site) created_at: site.created_at.rfc2822, last_updated: site.site_updated_at ? site.site_updated_at.rfc2822 : nil, domain: site.domain, - tags: site.tags.collect {|t| t.name} + tags: site.tags.collect { |t| t.name } } } end @@ -240,7 +234,10 @@ def require_api_credentials if !request.env['HTTP_AUTHORIZATION'].nil? init_api_credentials - api_error(403, 'email_not_validated', 'you need to validate your email address before using the API') if email_not_validated? + if email_not_validated? + api_error(403, 'email_not_validated', + 'you need to validate your email address before using the API') + end else api_error_invalid_auth end @@ -256,7 +253,7 @@ def init_api_credentials else user, pass = Base64.decode64(auth.match(/Basic (.+)/)[1]).split(':') end - rescue + rescue StandardError api_error_invalid_auth end @@ -268,9 +265,7 @@ def init_api_credentials api_error_invalid_auth end - if site.nil? || site.is_banned || site.is_deleted - api_error_invalid_auth - end + api_error_invalid_auth if site.nil? || site.is_banned || site.is_deleted DB['update sites set api_calls=api_calls+1 where id=?', site.id].first @@ -278,7 +273,7 @@ def init_api_credentials end def api_success(message_or_obj) - output = {result: 'success'} + output = { result: 'success' } if message_or_obj.is_a?(String) output[:message] = message_or_obj @@ -290,7 +285,7 @@ def api_success(message_or_obj) end def api_response(status, output) - halt status, JSON.pretty_generate(output)+"\n" + halt status, JSON.pretty_generate(output) + "\n" end def api_error(status, error_type, message) diff --git a/models/site_file.rb b/models/site_file.rb index afef3139..8696d112 100644 --- a/models/site_file.rb +++ b/models/site_file.rb @@ -6,35 +6,33 @@ class SiteFile < Sequel::Model CLASSIFIER_LIMIT = 1_000_000 CLASSIFIER_WORD_LIMIT = 25 FILE_PATH_CHARACTER_LIMIT = 1200 - FILE_NAME_CHARACTER_LIMIT = 100 + FILE_NAME_CHARACTER_LIMIT = 255 unrestrict_primary_key plugin :update_primary_key many_to_one :site def self.path_too_long?(filename) return true if filename.length > FILE_PATH_CHARACTER_LIMIT + false end def self.name_too_long?(filename) return true if filename.length > FILE_NAME_CHARACTER_LIMIT + false end def before_destroy if is_directory - site.site_files_dataset.where(path: /^#{Regexp.quote path}\//, is_directory: true).all.each do |site_file| - begin - site_file.destroy - rescue Sequel::NoExistingObject - end + site.site_files_dataset.where(path: %r{^#{Regexp.quote path}/}, is_directory: true).all.each do |site_file| + site_file.destroy + rescue Sequel::NoExistingObject end - site.site_files_dataset.where(path: /^#{Regexp.quote path}\//, is_directory: false).all.each do |site_file| - begin - site_file.destroy - rescue Sequel::NoExistingObject - end + site.site_files_dataset.where(path: %r{^#{Regexp.quote path}/}, is_directory: false).all.each do |site_file| + site_file.destroy + rescue Sequel::NoExistingObject end begin @@ -58,36 +56,26 @@ class SiteFile < Sequel::Model end def rename(new_path) - current_path = self.path + current_path = path new_path = site.scrubbed_path new_path - if new_path.length > FILE_PATH_CHARACTER_LIMIT - return false, 'new path too long' - end + return false, 'new path too long' if new_path.length > FILE_PATH_CHARACTER_LIMIT - if File.basename(new_path).length > FILE_NAME_CHARACTER_LIMIT - return false, 'new filename too long' - end + return false, 'new filename too long' if File.basename(new_path).length > FILE_NAME_CHARACTER_LIMIT - if new_path == '' - return false, 'cannot rename to empty path' - end + return false, 'cannot rename to empty path' if new_path == '' - if current_path == 'index.html' - return false, 'cannot rename or move root index.html' - end + return false, 'cannot rename or move root index.html' if current_path == 'index.html' - if site.site_files.select {|sf| sf.path == new_path}.length > 0 + if site.site_files.select { |sf| sf.path == new_path }.length > 0 return false, "#{is_directory ? 'directory' : 'file'} already exists" end if is_directory - if new_path.match(/\.html?$/) - return false, 'directory name cannot end with .htm or .html' - end + return false, 'directory name cannot end with .htm or .html' if new_path.match(/\.html?$/) else # a file - mime_type = Magic.guess_file_mime_type site.files_path(self.path) + mime_type = Magic.guess_file_mime_type site.files_path(path) extname = File.extname new_path unless site.supporter? || site.class.valid_file_mime_type_and_ext?(mime_type, extname) @@ -101,6 +89,7 @@ class SiteFile < Sequel::Model site.generate_thumbnail_or_screenshot new_path rescue Errno::ENOENT => e return false, 'destination directory does not exist' if e.message =~ /No such file or directory/i + raise e rescue ArgumentError => e raise e unless e.message =~ /same file/ @@ -108,13 +97,13 @@ class SiteFile < Sequel::Model DB.transaction do self.path = new_path - self.save_changes + save_changes if is_directory - site_files_in_dir = site.site_files.select {|sf| sf.path =~ /^#{current_path}\//} + site_files_in_dir = site.site_files.select { |sf| sf.path =~ %r{^#{current_path}/} } site_files_in_dir.each do |site_file| original_site_file_path = site_file.path - site_file.path = site_file.path.gsub(/^#{current_path}\//, "#{new_path}\/") + site_file.path = site_file.path.gsub(%r{^#{current_path}/}, "#{new_path}\/") site_file.save_changes site.delete_thumbnail_or_screenshot original_site_file_path site.generate_thumbnail_or_screenshot site_file.path @@ -127,14 +116,12 @@ class SiteFile < Sequel::Model end end - return true, nil + [true, nil] end def after_destroy super - unless is_directory - DB['update sites set space_used=space_used-? where id=?', size, site_id].first - end + DB['update sites set space_used=space_used-? where id=?', size, site_id].first unless is_directory site.purge_cache site.files_path(path) SiteChangeFile.filter(site_id: site_id, filename: path).delete From 26b6f6f6c8487d7752ab45cd337608522553a1d0 Mon Sep 17 00:00:00 2001 From: parkedhampster Date: Tue, 26 Mar 2024 11:41:06 -0400 Subject: [PATCH 2/2] undo auto-lint changes from my editor --- app/api.rb | 99 ++++++++++++++++++++++++--------------------- models/site_file.rb | 57 ++++++++++++++++---------- 2 files changed, 87 insertions(+), 69 deletions(-) diff --git a/app/api.rb b/app/api.rb index a20e1ad7..cfa8fb23 100644 --- a/app/api.rb +++ b/app/api.rb @@ -2,14 +2,14 @@ require 'base64' get '/api' do @title = 'Developers API' - erb :api + erb :'api' end post '/api/upload_hash' do require_api_credentials res = {} files = [] - params.each do |k, v| + params.each do |k,v| res[k] = current_site.sha1_hash_match? k, v end api_success files: res @@ -20,11 +20,11 @@ get '/api/list' do files = [] - file_list = if params[:path].nil? || params[:path].empty? - current_site.site_files - else - current_site.file_list params[:path] - end + if params[:path].nil? || params[:path].empty? + file_list = current_site.site_files + else + file_list = current_site.file_list params[:path] + end file_list.each do |file| new_file = {} @@ -36,7 +36,7 @@ get '/api/list' do files << new_file end - files.each { |f| f[:path].sub!(%r{^/}, '') } + files.each {|f| f[:path].sub!(/^\//, '')} api_success files: files end @@ -46,18 +46,20 @@ def extract_files(params, files = []) if params.is_a?(Array) params.each do |item| # Call extract_files on each item if it's an Array or Hash to handle nested structures - extract_files(item, files) if item.is_a?(Array) || item.is_a?(Hash) + if item.is_a?(Array) || item.is_a?(Hash) + extract_files(item, files) + end end elsif params.is_a?(Hash) - params.each do |_key, value| + params.each do |key, value| # If the value is a Hash and contains a :tempfile key, it's considered an uploaded file. if value.is_a?(Hash) && value.has_key?(:tempfile) && !value[:tempfile].nil? - files << { filename: value[:name], tempfile: value[:tempfile] } + files << {filename: value[:name], tempfile: value[:tempfile]} elsif value.is_a?(Array) value.each do |val| if val.is_a?(Hash) && val.has_key?(:tempfile) && !val[:tempfile].nil? # Directly add the file info if it's an uploaded file within an array - files << { filename: val[:name], tempfile: val[:tempfile] } + files << {filename: val[:name], tempfile: val[:tempfile]} elsif val.is_a?(Hash) || val.is_a?(Array) # Recursively search for more files if the element is a Hash or Array extract_files(val, files) @@ -76,38 +78,40 @@ post '/api/upload' do require_api_credentials files = extract_files params - unless params[:username].blank? + if !params[:username].blank? site = Site[username: params[:username]] - api_error 400, 'site_not_found', 'could not find site' if site.nil? || site.is_deleted + if site.nil? || site.is_deleted + api_error 400, 'site_not_found', "could not find site" + end if site.owned_by?(current_site) @_site = site else - api_error 400, 'site_not_allowed', 'not allowed to change this site with your current logged in site' + api_error 400, 'site_not_allowed', "not allowed to change this site with your current logged in site" end end api_error 400, 'missing_files', 'you must provide files to upload' if files.empty? - uploaded_size = files.collect { |f| f[:tempfile].size }.inject { |sum, x| sum + x } + uploaded_size = files.collect {|f| f[:tempfile].size}.inject{|sum,x| sum + x } if current_site.file_size_too_large? uploaded_size api_error 400, 'too_large', 'files are too large to fit in your space, try uploading smaller (or less) files' end if current_site.too_many_files?(files.length) - api_error 400, 'too_many_files', - "cannot exceed the maximum site files limit (#{current_site.plan_feature(:maximum_site_files)})" + api_error 400, 'too_many_files', "cannot exceed the maximum site files limit (#{current_site.plan_feature(:maximum_site_files)})" end files.each do |file| - unless current_site.okay_to_upload?(file) - api_error 400, 'invalid_file_type', - "#{file[:filename]} is not an allowed file type for free sites, supporter required" + if !current_site.okay_to_upload?(file) + api_error 400, 'invalid_file_type', "#{file[:filename]} is not an allowed file type for free sites, supporter required" end - api_error 400, 'directory_exists', "#{file[:filename]} being used by a directory" if File.directory? file[:filename] + if File.directory? file[:filename] + api_error 400, 'directory_exists', "#{file[:filename]} being used by a directory" + end if current_site.file_size_too_large? file[:tempfile].size api_error 400, 'file_too_large' "#{file[:filename]} is too large" @@ -118,7 +122,7 @@ post '/api/upload' do end if SiteFile.name_too_long? file[:filename] - api_error 400, 'file_name_too_long', "#{file[:filename]} filename is too long. (Longer than 255 characters)" + api_error 400, 'file_name_too_long', "#{file[:filename]} filename is too long (exceeds 255 characters)" end end @@ -129,21 +133,24 @@ end post '/api/rename' do require_api_credentials - if params[:path].blank? || params[:new_path].blank? - api_error 400, 'missing_arguments', - 'you must provide path and new_path' - end + api_error 400, 'missing_arguments', 'you must provide path and new_path' if params[:path].blank? || params[:new_path].blank? path = current_site.scrubbed_path params[:path] new_path = current_site.scrubbed_path params[:new_path] - api_error 400, 'bad_path', "#{path} is not a valid path, cancelled renaming" unless path.is_a?(String) + unless path.is_a?(String) + api_error 400, 'bad_path', "#{path} is not a valid path, cancelled renaming" + end - api_error 400, 'bad_new_path', "#{new_path} is not a valid new_path, cancelled renaming" unless new_path.is_a?(String) + unless new_path.is_a?(String) + api_error 400, 'bad_new_path', "#{new_path} is not a valid new_path, cancelled renaming" + end - site_file = current_site.site_files.select { |sf| sf.path == path }.first + site_file = current_site.site_files.select {|sf| sf.path == path}.first - api_error 400, 'missing_file', "could not find #{path}" if site_file.nil? + if site_file.nil? + api_error 400, 'missing_file', "could not find #{path}" + end res = site_file.rename new_path @@ -157,24 +164,23 @@ end post '/api/delete' do require_api_credentials - if params[:filenames].nil? || params[:filenames].empty? - api_error 400, 'missing_filenames', - 'you must provide files to delete' - end + api_error 400, 'missing_filenames', 'you must provide files to delete' if params[:filenames].nil? || params[:filenames].empty? paths = [] params[:filenames].each do |path| - api_error 400, 'bad_filename', "#{path} is not a valid filename, canceled deleting" unless path.is_a?(String) + unless path.is_a?(String) + api_error 400, 'bad_filename', "#{path} is not a valid filename, canceled deleting" + end if current_site.files_path(path) == current_site.files_path api_error 400, 'cannot_delete_site_directory', 'cannot delete the root directory of the site' end - unless current_site.file_exists?(path) + if !current_site.file_exists?(path) api_error 400, 'missing_files', "#{path} was not found on your site, canceled deleting" end - if ['index.html', '/index.html'].include?(path) + if path == 'index.html' || path == '/index.html' api_error 400, 'cannot_delete_index', 'you cannot delete your index.html file, canceled deleting' end @@ -214,7 +220,7 @@ def api_info_for(site) created_at: site.created_at.rfc2822, last_updated: site.site_updated_at ? site.site_updated_at.rfc2822 : nil, domain: site.domain, - tags: site.tags.collect { |t| t.name } + tags: site.tags.collect {|t| t.name} } } end @@ -234,10 +240,7 @@ def require_api_credentials if !request.env['HTTP_AUTHORIZATION'].nil? init_api_credentials - if email_not_validated? - api_error(403, 'email_not_validated', - 'you need to validate your email address before using the API') - end + api_error(403, 'email_not_validated', 'you need to validate your email address before using the API') if email_not_validated? else api_error_invalid_auth end @@ -253,7 +256,7 @@ def init_api_credentials else user, pass = Base64.decode64(auth.match(/Basic (.+)/)[1]).split(':') end - rescue StandardError + rescue api_error_invalid_auth end @@ -265,7 +268,9 @@ def init_api_credentials api_error_invalid_auth end - api_error_invalid_auth if site.nil? || site.is_banned || site.is_deleted + if site.nil? || site.is_banned || site.is_deleted + api_error_invalid_auth + end DB['update sites set api_calls=api_calls+1 where id=?', site.id].first @@ -273,7 +278,7 @@ def init_api_credentials end def api_success(message_or_obj) - output = { result: 'success' } + output = {result: 'success'} if message_or_obj.is_a?(String) output[:message] = message_or_obj @@ -285,7 +290,7 @@ def api_success(message_or_obj) end def api_response(status, output) - halt status, JSON.pretty_generate(output) + "\n" + halt status, JSON.pretty_generate(output)+"\n" end def api_error(status, error_type, message) diff --git a/models/site_file.rb b/models/site_file.rb index 8696d112..7420dcd5 100644 --- a/models/site_file.rb +++ b/models/site_file.rb @@ -13,26 +13,28 @@ class SiteFile < Sequel::Model def self.path_too_long?(filename) return true if filename.length > FILE_PATH_CHARACTER_LIMIT - false end def self.name_too_long?(filename) return true if filename.length > FILE_NAME_CHARACTER_LIMIT - false end def before_destroy if is_directory - site.site_files_dataset.where(path: %r{^#{Regexp.quote path}/}, is_directory: true).all.each do |site_file| - site_file.destroy - rescue Sequel::NoExistingObject + site.site_files_dataset.where(path: /^#{Regexp.quote path}\//, is_directory: true).all.each do |site_file| + begin + site_file.destroy + rescue Sequel::NoExistingObject + end end - site.site_files_dataset.where(path: %r{^#{Regexp.quote path}/}, is_directory: false).all.each do |site_file| - site_file.destroy - rescue Sequel::NoExistingObject + site.site_files_dataset.where(path: /^#{Regexp.quote path}\//, is_directory: false).all.each do |site_file| + begin + site_file.destroy + rescue Sequel::NoExistingObject + end end begin @@ -56,26 +58,36 @@ class SiteFile < Sequel::Model end def rename(new_path) - current_path = path + current_path = self.path new_path = site.scrubbed_path new_path - return false, 'new path too long' if new_path.length > FILE_PATH_CHARACTER_LIMIT + if new_path.length > FILE_PATH_CHARACTER_LIMIT + return false, 'new path too long' + end - return false, 'new filename too long' if File.basename(new_path).length > FILE_NAME_CHARACTER_LIMIT + if File.basename(new_path).length > FILE_NAME_CHARACTER_LIMIT + return false, 'new filename too long' + end - return false, 'cannot rename to empty path' if new_path == '' + if new_path == '' + return false, 'cannot rename to empty path' + end - return false, 'cannot rename or move root index.html' if current_path == 'index.html' + if current_path == 'index.html' + return false, 'cannot rename or move root index.html' + end - if site.site_files.select { |sf| sf.path == new_path }.length > 0 + if site.site_files.select {|sf| sf.path == new_path}.length > 0 return false, "#{is_directory ? 'directory' : 'file'} already exists" end if is_directory - return false, 'directory name cannot end with .htm or .html' if new_path.match(/\.html?$/) + if new_path.match(/\.html?$/) + return false, 'directory name cannot end with .htm or .html' + end else # a file - mime_type = Magic.guess_file_mime_type site.files_path(path) + mime_type = Magic.guess_file_mime_type site.files_path(self.path) extname = File.extname new_path unless site.supporter? || site.class.valid_file_mime_type_and_ext?(mime_type, extname) @@ -89,7 +101,6 @@ class SiteFile < Sequel::Model site.generate_thumbnail_or_screenshot new_path rescue Errno::ENOENT => e return false, 'destination directory does not exist' if e.message =~ /No such file or directory/i - raise e rescue ArgumentError => e raise e unless e.message =~ /same file/ @@ -97,13 +108,13 @@ class SiteFile < Sequel::Model DB.transaction do self.path = new_path - save_changes + self.save_changes if is_directory - site_files_in_dir = site.site_files.select { |sf| sf.path =~ %r{^#{current_path}/} } + site_files_in_dir = site.site_files.select {|sf| sf.path =~ /^#{current_path}\//} site_files_in_dir.each do |site_file| original_site_file_path = site_file.path - site_file.path = site_file.path.gsub(%r{^#{current_path}/}, "#{new_path}\/") + site_file.path = site_file.path.gsub(/^#{current_path}\//, "#{new_path}\/") site_file.save_changes site.delete_thumbnail_or_screenshot original_site_file_path site.generate_thumbnail_or_screenshot site_file.path @@ -116,12 +127,14 @@ class SiteFile < Sequel::Model end end - [true, nil] + return true, nil end def after_destroy super - DB['update sites set space_used=space_used-? where id=?', size, site_id].first unless is_directory + unless is_directory + DB['update sites set space_used=space_used-? where id=?', size, site_id].first + end site.purge_cache site.files_path(path) SiteChangeFile.filter(site_id: site_id, filename: path).delete