From 605fdce9be157bdabeb30ac43521d00b9b097daa Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Fri, 15 Aug 2014 18:38:14 -0700 Subject: [PATCH 01/12] remove console.log in dashboard --- views/dashboard.erb | 1 - 1 file changed, 1 deletion(-) diff --git a/views/dashboard.erb b/views/dashboard.erb index 50df31bc..28843acd 100644 --- a/views/dashboard.erb +++ b/views/dashboard.erb @@ -224,7 +224,6 @@ }) this.on("totaluploadprogress", function(progress, totalBytes, totalBytesSent) { - console.log('OH HI') showUploadProgress() $('#progressBar').css('display', 'block') $('#uploadingProgress').css('width', progress+'%') From 2f73732daafef5c8ec40bfe4b46608efb7420695 Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Tue, 19 Aug 2014 13:44:15 -0700 Subject: [PATCH 02/12] Folders. --- Gemfile | 6 +- Gemfile.lock | 32 ++- Rakefile | 2 +- app.rb | 82 ++++--- environment.rb | 1 - models/site.rb | 218 +++++++++++------- models/site_file.rb | 8 - tests/acceptance/dashboard_tests.rb | 26 +++ tests/acceptance/environment.rb | 7 + tests/acceptance/index_tests.rb | 11 + tests/acceptance/settings_tests.rb | 44 ++++ tests/acceptance/signin_tests.rb | 53 +++++ .../signup_tests.rb} | 113 +-------- tests/api_tests.rb | 58 ++++- tests/environment.rb | 11 +- tests/site_file_tests.rb | 33 +++ views/dashboard.erb | 91 +++++--- 17 files changed, 508 insertions(+), 288 deletions(-) delete mode 100644 models/site_file.rb create mode 100644 tests/acceptance/dashboard_tests.rb create mode 100644 tests/acceptance/environment.rb create mode 100644 tests/acceptance/index_tests.rb create mode 100644 tests/acceptance/settings_tests.rb create mode 100644 tests/acceptance/signin_tests.rb rename tests/{acceptance_tests.rb => acceptance/signup_tests.rb} (63%) create mode 100644 tests/site_file_tests.rb diff --git a/Gemfile b/Gemfile index 36634fb0..b5af3671 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,6 @@ gem 'bcrypt' gem 'sinatra-flash', require: 'sinatra/flash' gem 'sinatra-xsendfile', require: 'sinatra/xsendfile' gem 'puma', require: nil -gem 'rubyzip', require: 'zip' gem 'rack-recaptcha', require: 'rack/recaptcha' gem 'rmagick', require: nil gem 'sidekiq' @@ -19,6 +18,8 @@ gem 'erubis' gem 'stripe', :git => 'https://github.com/stripe/stripe-ruby' gem 'screencap' gem 'cocaine' +gem 'zipruby' +gem 'always_verify_ssl_certificates' platform :mri do gem 'magic' # sudo apt-get install file, For OSX: brew install libmagic @@ -54,13 +55,12 @@ group :test do gem 'minitest' gem 'minitest-reporters', require: 'minitest/reporters' gem 'rack-test', require: 'rack/test' - gem 'webmock' gem 'mocha', require: nil gem 'rake', require: nil gem 'poltergeist' gem 'phantomjs', require: 'phantomjs/poltergeist' - gem 'capybara' gem 'capybara_minitest_spec' + gem 'rack_session_access', require: nil platform :mri do gem 'simplecov', require: nil diff --git a/Gemfile.lock b/Gemfile.lock index e730f3ae..72faf470 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,6 +18,7 @@ GEM tzinfo (~> 1.1) addressable (2.3.6) ago (0.1.5) + always_verify_ssl_certificates (0.3.0) ansi (1.4.3) autoparse (0.3.3) addressable (>= 2.3.1) @@ -25,7 +26,7 @@ GEM multi_json (>= 1.0.0) bcrypt (3.1.7) builder (3.2.2) - capybara (2.2.1) + capybara (2.4.1) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -44,8 +45,6 @@ GEM coderay (1.1.0) columnize (0.3.6) connection_pool (2.0.0) - crack (0.4.2) - safe_yaml (~> 1.0.0) debugger (1.6.6) columnize (>= 0.3.1) debugger-linecache (~> 1.2.0) @@ -89,7 +88,7 @@ GEM metaclass (0.0.4) method_source (0.8.2) mime-types (1.25.1) - mini_portile (0.5.3) + mini_portile (0.6.0) minitest (5.3.1) minitest-reporters (1.0.2) ansi @@ -98,13 +97,13 @@ GEM powerbar mocha (1.0.0) metaclass (~> 0.0.1) - multi_json (1.9.2) + multi_json (1.10.1) multipart-post (2.0.0) - nokogiri (1.6.1) - mini_portile (~> 0.5.0) + nokogiri (1.6.3.1) + mini_portile (= 0.6.0) pg (0.17.1) phantomjs (1.9.7.0) - poltergeist (1.5.0) + poltergeist (1.5.1) capybara (~> 2.1) cliver (~> 0.3.1) multi_json (~> 1.0) @@ -129,6 +128,9 @@ GEM json rack-test (0.6.2) rack (>= 1.0) + rack_session_access (0.1.1) + builder (>= 2.0.0) + rack (>= 1.0.0) rainbows (4.6.1) kgio (~> 2.5) rack (~> 1.1) @@ -142,8 +144,6 @@ GEM mime-types (>= 1.16) retriable (1.4.1) rmagick (2.13.2) - rubyzip (1.1.2) - safe_yaml (1.0.1) sass (3.3.8) screencap (0.1.1) phantomjs @@ -191,20 +191,18 @@ GEM rack raindrops (~> 0.7) uuidtools (2.1.4) - webmock (1.17.4) - addressable (>= 2.2.7) - crack (>= 0.3.2) - websocket-driver (0.3.2) + websocket-driver (0.3.4) xpath (2.0.0) nokogiri (~> 1.3) + zipruby (0.3.6) PLATFORMS ruby DEPENDENCIES ago + always_verify_ssl_certificates bcrypt - capybara capybara_minitest_spec cocaine erubis @@ -228,12 +226,12 @@ DEPENDENCIES puma rack-recaptcha rack-test + rack_session_access rainbows rake redis rmagick ruby-debug - rubyzip sass screencap sequel (= 4.8.0) @@ -246,4 +244,4 @@ DEPENDENCIES sinatra-xsendfile stripe! tilt - webmock + zipruby diff --git a/Rakefile b/Rakefile index 9e80f27c..f2900d07 100644 --- a/Rakefile +++ b/Rakefile @@ -7,7 +7,7 @@ end desc "Run all tests" Rake::TestTask.new do |t| t.libs << "spec" - t.test_files = FileList['tests/*_tests.rb'] + t.test_files = FileList['tests/**/*_tests.rb'] t.verbose = true end diff --git a/app.rb b/app.rb index 1774496b..6e1feb2f 100644 --- a/app.rb +++ b/app.rb @@ -452,6 +452,13 @@ end get '/dashboard' do require_login + + if params[:dir] && params[:dir][0] != '/' + params[:dir] = '/'+params[:dir] + end + + @dir = params[:dir] + @file_list = current_site.file_list @dir erb :'dashboard' end @@ -562,7 +569,7 @@ post '/change_name' do end old_host = current_site.host - old_file_paths = current_site.file_list.collect {|f| f.filename} + old_file_paths = current_site.file_list.collect {|f| f[:path]} current_site.username = params[:name] @@ -629,7 +636,8 @@ def file_upload_response(error=nil) @error = error halt 200, erb(:'dashboard') else - redirect '/dashboard' + query_string = params[:dir] ? "?"+Rack::Utils.build_query(dir: params[:dir]) : '' + redirect "/dashboard#{query_string}" end else halt http_error_code, error if error @@ -637,6 +645,20 @@ def file_upload_response(error=nil) end end +post '/site/create_directory' do + require_login + + path = "#{params[:dir] || ''}/#{params[:name]}" + + result = current_site.create_directory path + + if result != true + flash[:error] = e.message + end + + redirect "/dashboard?dir=#{Rack::Utils.escape params[:dir]}" +end + post '/site_files/upload' do require_login @errors = [] @@ -647,12 +669,12 @@ post '/site_files/upload' do end params[:files].each do |file| + file[:filename] = "#{params[:dir]}/#{file[:filename]}" if params[:dir] if current_site.file_size_too_large? file[:tempfile].size - file_upload_response "#{file[:filename]} is too large, upload cancelled." + file_upload_response "#{params[:dir]}/#{file[:filename]} is too large, upload cancelled." end - if !Site.valid_file_type? file - file_upload_response "#{file[:filename]}: file type (or content in file) is not allowed on Neocities, upload cancelled." + file_upload_response "#{params[:dir]}/#{file[:filename]}: file type (or content in file) is not allowed on Neocities, upload cancelled." end end @@ -664,7 +686,7 @@ post '/site_files/upload' do results = [] params[:files].each do |file| - results << current_site.store_file(Site.sanitize_filename(file[:filename]), file[:tempfile]) + results << current_site.store_file(file[:filename], file[:tempfile]) end current_site.increment_changed_count if results.include?(true) @@ -674,20 +696,18 @@ end post '/site_files/delete' do require_login - sanitized_filename = Site.sanitize_filename params[:filename] + current_site.delete_file params[:filename] - current_site.delete_file(sanitized_filename) - - flash[:success] = "Deleted file #{params[:filename]}." + flash[:success] = "Deleted #{params[:filename]}." redirect '/dashboard' end get '/site_files/:username.zip' do |username| require_login - zipfile = current_site.files_zip + zipfile_path = current_site.files_zip content_type 'application/octet-stream' - attachment "#{current_site.username}.zip" - zipfile + attachment "neocities-#{current_site.username}.zip" + send_file zipfile_path end get '/site_files/download/:filename' do |filename| @@ -722,9 +742,7 @@ post '/site_files/save/:filename' do |filename| halt 'File is too large to fit in your space, it has NOT been saved. Please make a local copy and then try to reduce the size.' end - sanitized_filename = Site.sanitize_filename filename - - current_site.store_file sanitized_filename, tempfile + current_site.store_file filename, tempfile 'ok' end @@ -937,14 +955,11 @@ end post '/api/upload' do require_api_credentials - files = [] - params.each do |k,v| next unless v.is_a?(Hash) && v[:tempfile] - filename = k.to_s - api_error(400, 'bad_filename', "#{filename} is not a valid filename, files not uploaded") unless Site.valid_filename? filename - files << {filename: filename, tempfile: v[:tempfile]} + path = k.to_s + files << {filename: k || v[:filename], tempfile: v[:tempfile]} end api_error 400, 'missing_files', 'you must provide files to upload' if files.empty? @@ -959,6 +974,10 @@ post '/api/upload' do if !Site.valid_file_type?(file) api_error 400, 'invalid_file_type', "#{file[:filename]} is not a valid file type (or contains not allowed content), files have not been uploaded" end + + if File.directory? file[:filename] + api_error 400, 'directory_exists', 'this name is being used by a directory, cannot continue' + end end results = [] @@ -976,26 +995,25 @@ post '/api/delete' do api_error 400, 'missing_filenames', 'you must provide files to delete' if params[:filenames].nil? || params[:filenames].empty? - filenames = [] - - params[:filenames].each do |filename| - unless filename.is_a?(String) && Site.valid_filename?(filename) - api_error 400, 'bad_filename', "#{filename} is not a valid filename, canceled deleting" + paths = [] + params[:filenames].each do |path| + unless path.is_a?(String) && Site.valid_path?(path) + api_error 400, 'bad_filename', "#{path} is not a valid filename, canceled deleting" end - if !current_site.file_exists?(filename) - api_error 400, 'missing_files', "#{filename} was not found on your site, canceled deleting" + if !current_site.file_exists?(path) + api_error 400, 'missing_files', "#{path} was not found on your site, canceled deleting" end - if filename == 'index.html' + if path == 'index.html' api_error 400, 'cannot_delete_index', 'you cannot delete your index.html file, canceled deleting' end - filenames << filename + paths << path end - filenames.each do |filename| - current_site.delete_file(filename) + paths.each do |path| + current_site.delete_file(path) end api_success 'file(s) have been deleted' diff --git a/environment.rb b/environment.rb index 10385a8f..e7f5c8ef 100644 --- a/environment.rb +++ b/environment.rb @@ -7,7 +7,6 @@ Encoding.default_external = 'UTF-8' require 'yaml' require 'json' require 'logger' -require 'zip' Bundler.require Bundler.require :development if ENV['RACK_ENV'] == 'development' diff --git a/models/site.rb b/models/site.rb index 11e3e0f2..8dc98596 100644 --- a/models/site.rb +++ b/models/site.rb @@ -1,6 +1,7 @@ require 'tilt' require 'rss' require 'nokogiri' +require 'pathname' class Site < Sequel::Model include Sequel::ParanoidDelete @@ -23,6 +24,7 @@ class Site < Sequel::Model image/x-icon application/pdf application/pgp-keys + application/pgp text/xml application/xml audio/midi @@ -70,6 +72,8 @@ class Site < Sequel::Model EMAIL_SANITY_REGEX = /.+@.+\..+/i + EDITABLE_FILE_EXT = /html|htm|txt|js|css|md/i + BANNED_TIME = 2592000 # 30 days in seconds TITLE_MAX = 100 @@ -197,16 +201,16 @@ class Site < Sequel::Model FileUtils.mkdir_p files_path %w{index not_found}.each do |name| - File.write file_path("#{name}.html"), render_template("#{name}.erb") + File.write files_path("#{name}.html"), render_template("#{name}.erb") purge_cache "#{name}.html" ScreenshotWorker.perform_async values[:username], "#{name}.html" end - FileUtils.cp template_file_path('cat.png'), file_path('cat.png') + FileUtils.cp template_file_path('cat.png'), files_path('cat.png') end - def get_file(filename) - File.read file_path(filename) + def get_file(path) + File.read files_path(path) end def before_destroy @@ -252,8 +256,8 @@ class Site < Sequel::Model FileUtils.mv files_path, File.join(PUBLIC_ROOT, 'banned_sites', username) } - site_files.file_list.collect {|f| f.filename}.each do |f| - purge_cache f + file_list.each do |path| + purge_cache path end end @@ -290,15 +294,11 @@ class Site < Sequel::Model !@blockings.select {|b| b.site_id == site.id}.empty? end - def self.valid_filename?(filename) - return false if sanitize_filename(filename) != filename + def self.valid_path?(path) + puts 'ditto restrictions scrub' true end - def self.sanitize_filename(filename) - filename.gsub(/[^a-zA-Z0-9_\-.]/, '') - end - def self.valid_username?(username) !username.empty? && username.match(/^[a-zA-Z0-9_\-]+$/i) end @@ -331,19 +331,21 @@ class Site < Sequel::Model true end - def purge_cache(filename) - payload = {site: username, path: filename} + def purge_cache(path) + payload = {site: username, path: path} payload[:domain] = domain if !domain.empty? PurgeCacheWorker.perform_async payload end - def store_file(filename, uploaded) - if File.exist?(file_path(filename)) && - Digest::SHA2.file(file_path(filename)).digest == Digest::SHA2.file(uploaded.path).digest + def store_file(path, uploaded) + path = files_path(path) + if File.exist?(path) && + Digest::SHA2.file(path).digest == Digest::SHA2.file(uploaded.path).digest return false end - if filename == 'index.html' + pathname = Pathname(path) + if pathname.basename.to_s == 'index.html' new_title = Nokogiri::HTML(File.read(uploaded.path)).css('title').first.text if new_title.length < TITLE_MAX @@ -352,20 +354,26 @@ class Site < Sequel::Model end end - FileUtils.mv uploaded.path, file_path(filename) - File.chmod(0640, file_path(filename)) + dirname = pathname.dirname.to_s - purge_cache filename - - ext = File.extname(filename).gsub(/^./, '') - - if ext.match HTML_REGEX - ScreenshotWorker.perform_async values[:username], filename - elsif ext.match IMAGE_REGEX - ThumbnailWorker.perform_async values[:username], filename + if !File.exists? dirname + FileUtils.mkdir_p dirname end - SiteChange.record self, filename + FileUtils.mv uploaded.path, path + File.chmod 0640, path + + purge_cache path + + ext = File.extname(path).gsub(/^./, '') + + if ext.match HTML_REGEX + ScreenshotWorker.perform_async values[:username], path + elsif ext.match IMAGE_REGEX + ThumbnailWorker.perform_async values[:username], path + end + + SiteChange.record self, path if self.site_changed != true self.site_changed = true @@ -375,6 +383,20 @@ class Site < Sequel::Model true end + def is_directory?(path) + File.directory? files_path(path) + end + + def create_directory(path) + relative_path = files_path path + if Dir.exists?(relative_path) || File.exist?(relative_path) + return 'Directory (or file) already exists.' + end + + FileUtils.mkdir_p relative_path + true + end + def increment_changed_count self.changed_count += 1 self.updated_at = Time.now @@ -382,49 +404,59 @@ class Site < Sequel::Model end def files_zip - file_path = "/tmp/neocities-site-#{username}.zip" + zip_name = "neocities-#{username}" - Zip::File.open(file_path, Zip::File::CREATE) do |zipfile| - file_list.collect {|f| f.filename}.each do |filename| - zipfile.add filename, file_path(filename) + tmpfile = Tempfile.new 'neocities-site-zip' + tmpfile.close + + Zip::Archive.open(tmpfile.path, Zip::CREATE) do |ar| + ar.add_dir(zip_name) + + Dir.glob("#{base_files_path}/**/*").each do |path| + relative_path = path.gsub(base_files_path+'/', '') + + if File.directory?(path) + ar.add_dir(zip_name+'/'+relative_path) + else + ar.add_file(zip_name+'/'+relative_path, path) # add_file(, ) + end end end - # TODO Don't dump the zipfile into memory - zipfile = File.read file_path - File.delete file_path - zipfile + tmpfile.path end - def delete_file(filename) + def delete_file(path) begin - FileUtils.rm file_path(filename) + FileUtils.rm files_path(path) + rescue Errno::EISDIR + FileUtils.remove_dir files_path(path), true rescue Errno::ENOENT end - purge_cache filename + purge_cache path - ext = File.extname(filename).gsub(/^./, '') + ext = File.extname(path).gsub(/^./, '') - screenshots_delete(filename) if ext.match HTML_REGEX - thumbnails_delete(filename) if ext.match IMAGE_REGEX + screenshots_delete(path) if ext.match HTML_REGEX + thumbnails_delete(path) if ext.match IMAGE_REGEX - SiteChangeFile.filter(site_id: self.id, filename: filename).delete + SiteChangeFile.filter(site_id: self.id, filename: path).delete true end def move_files_from(oldusername) - FileUtils.mv files_path(oldusername), files_path + FileUtils.mv base_files_path(oldusername), base_files_path end - def install_new_html_file(name) - File.write file_path(name), render_template('index.erb') - purge_cache name + def install_new_html_file(path) + File.write files_path(path), render_template('index.erb') + purge_cache path end - def file_exists?(filename) - File.exist? file_path(filename) + def file_exists?(path) + File.exist? files_path(path) end def after_save @@ -453,7 +485,7 @@ class Site < Sequel::Model end # def after_destroy -# FileUtils.rm_rf file_path +# FileUtils.rm_rf files_path # super # end @@ -568,16 +600,48 @@ class Site < Sequel::Model File.join TEMPLATE_ROOT, name end - def files_path(name=nil) - File.join SITE_FILES_ROOT, (name || username) + def base_files_path(name=username) + raise 'username missing' if name.nil? || name.empty? + File.join SITE_FILES_ROOT, name end - def file_path(filename) - File.join files_path, filename + # https://practicingruby.com/articles/implementing-an-http-file-server?u=dc2ab0f9bb + def scrubbed_path(path='') + path ||= '' + clean = [] + + parts = path.split '/' + + parts.each do |part| + next if part.empty? || part == '.' + clean << part if part != '..' + end + + clean end - def file_list - Dir.glob(File.join(files_path, '*')).collect {|p| File.basename(p)}.sort.collect {|sitename| SiteFile.new sitename} + def files_path(path='') + File.join base_files_path, scrubbed_path(path) + end + + def file_list(path='') + list = Dir.glob(File.join(files_path(path), '*')).collect do |file_path| + file = { + path: file_path.gsub(base_files_path, ''), + name: File.basename(file_path), + ext: File.extname(file_path).gsub('.', ''), + is_directory: File.directory?(file_path), + is_root_index: file_path == "#{base_files_path}/index.html" + } + + file[:is_html] = !(file[:ext].match HTML_REGEX).nil? + file[:is_image] = !(file[:ext].match IMAGE_REGEX).nil? + file[:is_editable] = !(file[:ext].match EDITABLE_FILE_EXT).nil? + file + end + + list.select {|f| f[:is_directory]}.sort_by {|f| f[:name]} + + list.select {|f| f[:is_directory] == false}.sort_by{|f| f[:name]} end def file_size_too_large?(size_in_bytes) @@ -658,19 +722,19 @@ class Site < Sequel::Model values[:hits].to_s.reverse.gsub(/...(?=.)/,'\&,').reverse end - def screenshots_delete(filename) + def screenshots_delete(path) SCREENSHOT_RESOLUTIONS.each do |res| begin - FileUtils.rm screenshot_path(filename, res) + FileUtils.rm screenshot_path(path, res) rescue Errno::ENOENT end end end - def thumbnails_delete(filename) + def thumbnails_delete(path) THUMBNAIL_RESOLUTIONS.each do |res| begin - FileUtils.rm thumbnail_path(filename, res) + FileUtils.rm thumbnail_path(path, res) rescue Errno::ENOENT end end @@ -680,34 +744,34 @@ class Site < Sequel::Model Site.where(tags: tags).limit(limit, offset).order(:updated_at.desc).all end - def screenshot_path(filename, resolution) - File.join(SCREENSHOTS_ROOT, values[:username], "#{filename}.#{resolution}.jpg") + def screenshot_path(path, resolution) + File.join(SCREENSHOTS_ROOT, values[:username], "#{path}.#{resolution}.jpg") end - def screenshot_exists?(filename, resolution) - File.exist? File.join(SCREENSHOTS_ROOT, values[:username], "#{filename}.#{resolution}.jpg") + def screenshot_exists?(path, resolution) + File.exist? File.join(SCREENSHOTS_ROOT, values[:username], "#{path}.#{resolution}.jpg") end - def screenshot_url(filename, resolution) - "#{SCREENSHOTS_URL_ROOT}/#{values[:username]}/#{filename}.#{resolution}.jpg" + def screenshot_url(path, resolution) + "#{SCREENSHOTS_URL_ROOT}/#{values[:username]}/#{path}.#{resolution}.jpg" end - def thumbnail_path(filename, resolution) - ext = File.extname(filename).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' - File.join THUMBNAILS_ROOT, values[:username], "#{filename}.#{resolution}.#{ext}" + def thumbnail_path(path, resolution) + ext = File.extname(path).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' + File.join THUMBNAILS_ROOT, values[:username], "#{path}.#{resolution}.#{ext}" end - def thumbnail_exists?(filename, resolution) - File.exist? thumbnail_path(filename, resolution) + def thumbnail_exists?(path, resolution) + File.exist? thumbnail_path(path, resolution) end - def thumbnail_delete(filename, resolution) - File.rm thumbnail_path(filename, resolution) + def thumbnail_delete(path, resolution) + File.rm thumbnail_path(path, resolution) end - def thumbnail_url(filename, resolution) - ext = File.extname(filename).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' - "#{THUMBNAILS_URL_ROOT}/#{values[:username]}/#{filename}.#{resolution}.#{ext}" + def thumbnail_url(path, resolution) + ext = File.extname(path).gsub('.', '').match(LOSSY_IMAGE_REGEX) ? 'jpg' : 'png' + "#{THUMBNAILS_URL_ROOT}/#{values[:username]}/#{path}.#{resolution}.#{ext}" end def to_rss diff --git a/models/site_file.rb b/models/site_file.rb deleted file mode 100644 index ac280f17..00000000 --- a/models/site_file.rb +++ /dev/null @@ -1,8 +0,0 @@ -class SiteFile - attr_reader :filename, :ext - - def initialize(filename) - @filename = filename - @ext = File.extname(@filename).sub(/^./, '') - end -end \ No newline at end of file diff --git a/tests/acceptance/dashboard_tests.rb b/tests/acceptance/dashboard_tests.rb new file mode 100644 index 00000000..cf4b60a6 --- /dev/null +++ b/tests/acceptance/dashboard_tests.rb @@ -0,0 +1,26 @@ +require_relative './environment.rb' + +describe 'dashboard' do + describe 'create directory' do + + describe 'logged in' do + + include Capybara::DSL + + before do + Capybara.reset_sessions! + @site = Fabricate :site + page.set_rack_session id: @site.id + end + + it 'creates a base directory' do + visit '/dashboard' + click_link 'New Folder' + fill_in 'name', with: 'testimages' + click_button 'Create' + page.must_have_content /testimages/ + File.directory?(@site.files_path('testimages')).must_equal true + end + end + end +end \ No newline at end of file diff --git a/tests/acceptance/environment.rb b/tests/acceptance/environment.rb new file mode 100644 index 00000000..b0f3379b --- /dev/null +++ b/tests/acceptance/environment.rb @@ -0,0 +1,7 @@ +require_relative '../environment' + +Capybara.app = Sinatra::Application + +def teardown + Capybara.reset_sessions! +end \ No newline at end of file diff --git a/tests/acceptance/index_tests.rb b/tests/acceptance/index_tests.rb new file mode 100644 index 00000000..70b8a7ac --- /dev/null +++ b/tests/acceptance/index_tests.rb @@ -0,0 +1,11 @@ +require_relative './environment.rb' + +describe 'index' do + include Capybara::DSL + it 'goes to signup' do + Capybara.reset_sessions! + visit '/' + click_button 'Create My Website' + page.must_have_content('Create a New Website') + end +end \ No newline at end of file diff --git a/tests/acceptance/settings_tests.rb b/tests/acceptance/settings_tests.rb new file mode 100644 index 00000000..a425ee74 --- /dev/null +++ b/tests/acceptance/settings_tests.rb @@ -0,0 +1,44 @@ +require_relative './environment.rb' + +describe 'site/settings' do + describe 'change username' do + include Capybara::DSL + + def visit_signup + visit '/' + click_button 'Create My Website' + end + + def fill_in_valid + @site = Fabricate.attributes_for(:site) + fill_in 'username', with: @site[:username] + fill_in 'password', with: @site[:password] + fill_in 'email', with: @site[:email] + end + + before do + Capybara.reset_sessions! + visit_signup + end + + it 'does not allow bad usernames' do + visit '/' + click_button 'Create My Website' + fill_in_valid + click_button 'Create Home Page' + visit '/settings' + fill_in 'name', with: '' + click_button 'Change Name' + fill_in 'name', with: '../hack' + click_button 'Change Name' + fill_in 'name', with: 'derp../hack' + click_button 'Change Name' + ## TODO fix this without screwing up legacy sites + #fill_in 'name', with: '-' + #click_button 'Change Name' + page.must_have_content /valid.+name.+required/i + Site[username: @site[:username]].wont_equal nil + Site[username: ''].must_equal nil + end + end +end \ No newline at end of file diff --git a/tests/acceptance/signin_tests.rb b/tests/acceptance/signin_tests.rb new file mode 100644 index 00000000..78e13e46 --- /dev/null +++ b/tests/acceptance/signin_tests.rb @@ -0,0 +1,53 @@ +require_relative './environment.rb' + +describe 'signin' do + include Capybara::DSL + + def fill_in_valid + @site = Fabricate.attributes_for :site + fill_in 'username', with: @site[:username] + fill_in 'password', with: @site[:password] + end + + def fill_in_valid_signup + fill_in_valid + fill_in 'email', with: @site[:email] + end + + before do + Capybara.reset_sessions! + end + + it 'fails for invalid login' do + visit '/' + click_link 'Sign In' + page.must_have_content 'Welcome Back' + fill_in_valid + click_button 'Sign In' + page.must_have_content 'Invalid login' + end + + it 'fails for missing login' do + visit '/' + click_link 'Sign In' + auth = {username: SecureRandom.hex, password: Faker::Internet.password} + fill_in 'username', with: auth[:username] + fill_in 'password', with: auth[:password] + click_button 'Sign In' + page.must_have_content 'Invalid login' + end + + it 'logs in with proper credentials' do + visit '/' + click_button 'Create My Website' + fill_in_valid_signup + click_button 'Create Home Page' + Capybara.reset_sessions! + visit '/' + click_link 'Sign In' + fill_in 'username', with: @site[:username] + fill_in 'password', with: @site[:password] + click_button 'Sign In' + page.must_have_content 'Your Feed' + end +end \ No newline at end of file diff --git a/tests/acceptance_tests.rb b/tests/acceptance/signup_tests.rb similarity index 63% rename from tests/acceptance_tests.rb rename to tests/acceptance/signup_tests.rb index 2f58fe01..61ab5db5 100644 --- a/tests/acceptance_tests.rb +++ b/tests/acceptance/signup_tests.rb @@ -1,61 +1,4 @@ -require_relative './environment' - -Capybara.app = Sinatra::Application - -def teardown - Capybara.reset_sessions! - Capybara.use_default_driver -end - -describe 'index' do - include Capybara::DSL - it 'goes to signup' do - visit '/' - click_button 'Create My Website' - page.must_have_content('Create a New Website') - end -end - -describe 'change username' do - include Capybara::DSL - - def visit_signup - visit '/' - click_button 'Create My Website' - end - - def fill_in_valid - @site = Fabricate.attributes_for(:site) - fill_in 'username', with: @site[:username] - fill_in 'password', with: @site[:password] - fill_in 'email', with: @site[:email] - end - - before do - Capybara.reset_sessions! - visit_signup - end - - it 'does not allow bad usernames' do - visit '/' - click_button 'Create My Website' - fill_in_valid - click_button 'Create Home Page' - visit '/settings' - fill_in 'name', with: '' - click_button 'Change Name' - fill_in 'name', with: '../hack' - click_button 'Change Name' - fill_in 'name', with: 'derp../hack' - click_button 'Change Name' - ## TODO fix this without screwing up legacy sites - #fill_in 'name', with: '-' - #click_button 'Change Name' - page.must_have_content /valid.+name.+required/i - Site[username: @site[:username]].wont_equal nil - Site[username: ''].must_equal nil - end -end +require_relative './environment.rb' describe 'signup' do include Capybara::DSL @@ -215,56 +158,4 @@ describe 'signup' do page.must_have_content 'Your Feed' Site.last.tags.collect {|t| t.name}.must_equal ['derpie', 'shoujo'] end -end - -describe 'signin' do - include Capybara::DSL - - def fill_in_valid - @site = Fabricate.attributes_for :site - fill_in 'username', with: @site[:username] - fill_in 'password', with: @site[:password] - end - - def fill_in_valid_signup - fill_in_valid - fill_in 'email', with: @site[:email] - end - - before do - Capybara.reset_sessions! - end - - it 'fails for invalid login' do - visit '/' - click_link 'Sign In' - page.must_have_content 'Welcome Back' - fill_in_valid - click_button 'Sign In' - page.must_have_content 'Invalid login' - end - - it 'fails for missing login' do - visit '/' - click_link 'Sign In' - auth = {username: SecureRandom.hex, password: Faker::Internet.password} - fill_in 'username', with: auth[:username] - fill_in 'password', with: auth[:password] - click_button 'Sign In' - page.must_have_content 'Invalid login' - end - - it 'logs in with proper credentials' do - visit '/' - click_button 'Create My Website' - fill_in_valid_signup - click_button 'Create Home Page' - Capybara.reset_sessions! - visit '/' - click_link 'Sign In' - fill_in 'username', with: @site[:username] - fill_in 'password', with: @site[:password] - click_button 'Sign In' - page.must_have_content 'Your Feed' - end -end +end \ No newline at end of file diff --git a/tests/api_tests.rb b/tests/api_tests.rb index a6bb491a..07966838 100644 --- a/tests/api_tests.rb +++ b/tests/api_tests.rb @@ -84,17 +84,17 @@ describe 'api delete' do res[:error_type].must_equal 'cannot_delete_index' end - it 'fails with bad filename' do + it 'succeeds with weird filenames' do create_site basic_authorize @user, @pass @site.store_file 't$st.jpg', Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') post '/api/delete', filenames: ['t$st.jpg'] - res[:error_type].must_equal 'bad_filename' + res[:result].must_equal 'success' create_site basic_authorize @user, @pass post '/api/delete', filenames: ['./config.yml'] - res[:error_type].must_equal 'bad_filename' + res[:error_type].must_equal 'missing_files' end it 'fails with missing files' do @@ -137,13 +137,59 @@ describe 'api upload' do res[:error_type].must_equal 'missing_files' end - it 'fails for invalid filenames' do + it 'resists directory traversal attack' do create_site basic_authorize @user, @pass post '/api/upload', { '../lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') } - res[:error_type].must_equal 'bad_filename' + res[:result].must_equal 'success' + File.exist?(File.join(Site::SITE_FILES_ROOT, @site.username, 'lol.jpg')).must_equal true + end + + it 'scrubs root path slash' do + create_site + basic_authorize @user, @pass + post '/api/upload', { + '/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:result].must_equal 'success' + File.exist?(File.join(Site::SITE_FILES_ROOT, @site.username, 'lol.jpg')).must_equal true + end + + it 'fails for missing file name' do + create_site + basic_authorize @user, @pass + post '/api/upload', { + '/' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:error_type].must_equal 'invalid_file_type' + + create_site + basic_authorize @user, @pass + post '/api/upload', { + '' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:error_type].must_equal 'missing_files' + end + + it 'fails for file with no extension' do + create_site + basic_authorize @user, @pass + post '/api/upload', { + 'derpie' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:error_type].must_equal 'invalid_file_type' + end + + it 'creates path for file uploads' do + create_site + basic_authorize @user, @pass + post '/api/upload', { + 'derpie/derpingtons/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') + } + res[:result].must_equal 'success' + File.exist?(@site.files_path('derpie/derpingtons/lol.jpg')).must_equal true end it 'fails for invalid files' do @@ -180,7 +226,7 @@ describe 'api upload' do end def site_file_exists?(file) - File.exist?(@site.file_path('test.jpg')) + File.exist?(@site.files_path('test.jpg')) end def res diff --git a/tests/environment.rb b/tests/environment.rb index 0ad4794a..ad3cb866 100644 --- a/tests/environment.rb +++ b/tests/environment.rb @@ -9,18 +9,23 @@ end SimpleCov.command_name 'minitest' +require 'rack_session_access' require './environment' -require 'webmock' -include WebMock::API require './app' Bundler.require :test #require 'minitest/pride' require 'minitest/autorun' - require 'sidekiq/testing' +Sinatra::Application.configure do |app| + app.use RackSessionAccess::Middleware +end + +require 'capybara/poltergeist' +require 'rack_session_access/capybara' + Site.bcrypt_cost = BCrypt::Engine::MIN_COST MiniTest::Reporters.use! MiniTest::Reporters::SpecReporter.new diff --git a/tests/site_file_tests.rb b/tests/site_file_tests.rb new file mode 100644 index 00000000..0904df23 --- /dev/null +++ b/tests/site_file_tests.rb @@ -0,0 +1,33 @@ +require_relative './environment.rb' +require 'rack/test' + +include Rack::Test::Methods + +def app + Sinatra::Application +end + +describe 'site_files' do + describe 'upload' do + it 'succeeds with valid file' do + site = Fabricate :site + post '/site_files/upload', { + 'files[]' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'), + 'csrf_token' => 'abcd' + }, {'rack.session' => { 'id' => site.id, '_csrf_token' => 'abcd' }} + last_response.body.must_match /successfully uploaded/i + File.exists?(site.files_path('test.jpg')).must_equal true + end + + it 'works with directory path' do + site = Fabricate :site + post '/site_files/upload', { + 'dir' => 'derpie/derptest', + 'files[]' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'), + 'csrf_token' => 'abcd' + }, {'rack.session' => { 'id' => site.id, '_csrf_token' => 'abcd' }} + last_response.body.must_match /successfully uploaded/i + File.exists?(site.files_path('derpie/derptest/test.jpg')).must_equal true + end + end +end \ No newline at end of file diff --git a/views/dashboard.erb b/views/dashboard.erb index 28843acd..bb3cdeae 100644 --- a/views/dashboard.erb +++ b/views/dashboard.erb @@ -15,14 +15,6 @@ display: none; } -
@@ -88,10 +80,27 @@
- +
@@ -99,47 +108,49 @@
-
- <% current_site.file_list.each do |file| %> +
+ <% @file_list.each do |file| %>
- <% if file.ext.match(Site::HTML_REGEX) && current_site.screenshot_exists?(file.filename, '105x63') %> + <% if file[:is_html] && current_site.screenshot_exists?(file[:path], '105x63') %>
- +
- <% elsif file.ext.match(Site::IMAGE_REGEX) && current_site.thumbnail_exists?(file.filename, '105x63') %> + <% elsif file[:is_image] && current_site.thumbnail_exists?(file[:path], '105x63') %>
- +
- <% else %>
-
<%= file.ext %>
+
<%= file[:ext] %>
<% end %> - <% if file.filename.length > 14 %> - <%= file.filename.slice(0..14) %>… + <% if file[:name].length > 15 %> + <%= file[:name].slice(0..14) %>… <% else %> - <%= file.filename %> + <%= file[:name] %> <% end %>
- <% if file.ext.match(/html|htm|txt|js|css|md/) %> - Edit + <% if file[:is_editable] %> + Edit <% end %> - <% if file.filename != 'index.html' %> - Delete + <% if file[:is_directory] %> + Manage <% end %> - + <% if !file[:is_root_index] %> + Delete + <% end %> +
<% end %> @@ -158,7 +169,7 @@

Confirm deletion

- + + @@ -231,3 +243,24 @@ } } + + \ No newline at end of file From 2743e02cc1399c30a692fb8c808da805cd428b8c Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Tue, 19 Aug 2014 13:48:02 -0700 Subject: [PATCH 03/12] Dashboard is Edit Site, redirect if folder missing --- app.rb | 4 ++++ views/_header.erb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app.rb b/app.rb index 6e1feb2f..cec1275c 100644 --- a/app.rb +++ b/app.rb @@ -457,6 +457,10 @@ get '/dashboard' do params[:dir] = '/'+params[:dir] end + if !File.directory?(current_site.files_path(params[:dir])) + redirect '/dashboard' + end + @dir = params[:dir] @file_list = current_site.file_list @dir erb :'dashboard' diff --git a/views/_header.erb b/views/_header.erb index 35e74557..060288e8 100644 --- a/views/_header.erb +++ b/views/_header.erb @@ -34,7 +34,7 @@ <% else %>
  • - +
  • From e200eb47c619fe716d74b99c3ea24231f53f29e5 Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Tue, 19 Aug 2014 15:09:20 -0700 Subject: [PATCH 04/12] fix text editor --- app.rb | 10 ++++++---- models/site.rb | 5 ----- views/site_files/text_editor.erb | 7 +++++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app.rb b/app.rb index cec1275c..0d24f4e5 100644 --- a/app.rb +++ b/app.rb @@ -721,10 +721,11 @@ get '/site_files/download/:filename' do |filename| current_site.get_file filename end -get '/site_files/text_editor/:filename' do |filename| +get %r{\/site_files\/text_editor\/(.+)} do require_login + @filename = params[:captures].first begin - @file_data = current_site.get_file filename + @file_data = current_site.get_file @filename rescue Errno::ENOENT flash[:error] = 'We could not find the requested file.' redirect '/dashboard' @@ -732,8 +733,9 @@ get '/site_files/text_editor/:filename' do |filename| erb :'site_files/text_editor' end -post '/site_files/save/:filename' do |filename| +post %r{\/site_files\/save\/(.+)} do require_login_ajax + filename = params[:captures].first tempfile = Tempfile.new 'neocities_saving_file' @@ -1001,7 +1003,7 @@ post '/api/delete' do paths = [] params[:filenames].each do |path| - unless path.is_a?(String) && Site.valid_path?(path) + unless path.is_a?(String) api_error 400, 'bad_filename', "#{path} is not a valid filename, canceled deleting" end diff --git a/models/site.rb b/models/site.rb index 8dc98596..8b8412bf 100644 --- a/models/site.rb +++ b/models/site.rb @@ -294,11 +294,6 @@ class Site < Sequel::Model !@blockings.select {|b| b.site_id == site.id}.empty? end - def self.valid_path?(path) - puts 'ditto restrictions scrub' - true - end - def self.valid_username?(username) !username.empty? && username.match(/^[a-zA-Z0-9_\-]+$/i) end diff --git a/views/site_files/text_editor.erb b/views/site_files/text_editor.erb index ef28662e..53b100a8 100644 --- a/views/site_files/text_editor.erb +++ b/views/site_files/text_editor.erb @@ -15,7 +15,7 @@
    -

    Editing <%= params[:filename] %>

    +

    Editing <%= @filename %>

    @@ -120,11 +120,14 @@ function saveTextFile(quit) { $.ajax({ - url: '/site_files/save/<%= params[:filename] %>?csrf_token=<%= csrf_token %>', + url: '/site_files/save/<%= @filename %>?csrf_token=<%= csrf_token %>', data: editor.getValue(), processData: false, contentType: false, type: 'POST', + error: function(jqXHR, textStatus, errorThrown) { + alert('There has been an error saving your file, please try again. If it continues to fail, make a copy of the file locally so you don\'t lose your changes!') + }, success: function(response){ if(response == 'ok') { if(quit === true) { From 445ec92226bd16f5ef3b520499646dab016a9f3e Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Tue, 19 Aug 2014 15:13:13 -0700 Subject: [PATCH 05/12] fix for home link --- views/dashboard.erb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/views/dashboard.erb b/views/dashboard.erb index bb3cdeae..93dfb48a 100644 --- a/views/dashboard.erb +++ b/views/dashboard.erb @@ -81,10 +81,11 @@
  • diff --git a/views/layout.erb b/views/layout.erb index 42ce089f..488ec6ba 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -20,7 +20,7 @@ - + From 0f9b9bad563f642374b4d66ff6bc97ab83428f69 Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Wed, 20 Aug 2014 18:02:05 -0700 Subject: [PATCH 10/12] slightly smaller font for file titles --- public/assets/css/_project-sass/_project-Main.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/assets/css/_project-sass/_project-Main.scss b/public/assets/css/_project-sass/_project-Main.scss index 23d8017c..a892cba8 100644 --- a/public/assets/css/_project-sass/_project-Main.scss +++ b/public/assets/css/_project-sass/_project-Main.scss @@ -253,7 +253,7 @@ } .file .title { font-weight: bold; - font-size: 12px; + font-size: 8px; color: #666; margin-top: 4px; text-decoration: none; From b2906808d106f82ef7da7c4554bdb8c971c61b2a Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Wed, 20 Aug 2014 18:23:20 -0700 Subject: [PATCH 11/12] move neo.css back to assets folder for relative url links --- .gitignore | 2 +- environment.rb | 2 +- views/browse.erb | 5 ++++- views/layout.erb | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0a7a9c09..05878c95 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ doc/ tests/coverage config.yml .DS_Store -public/css/neo.css +public/assets/css/neo.css public/site_thumbnails public/sites public/site_screenshots diff --git a/environment.rb b/environment.rb index 7c49673b..bebfe8d1 100644 --- a/environment.rb +++ b/environment.rb @@ -105,7 +105,7 @@ require 'sass/plugin/rack' Sinatra::Application.use Sass::Plugin::Rack Sass::Plugin.options[:template_location] = './public/assets/css' -Sass::Plugin.options[:css_location] = './public/css' +Sass::Plugin.options[:css_location] = './public/assets/css' Sass::Plugin.options[:style] = :nested if ENV['RACK_ENV'] == 'production' diff --git a/views/browse.erb b/views/browse.erb index 45c68c81..66e8b446 100644 --- a/views/browse.erb +++ b/views/browse.erb @@ -73,7 +73,10 @@
    diff --git a/views/layout.erb b/views/layout.erb index 488ec6ba..42ce089f 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -20,7 +20,7 @@ - + From 7520bc70b8bb6f629fe328f2e43ac3c5213869c3 Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Sat, 23 Aug 2014 10:56:01 -0500 Subject: [PATCH 12/12] implement WebDAV mount support --- Gemfile | 1 + Gemfile.lock | 9 ++++++-- app.rb | 1 - config.ru | 58 +++++++++++++++++++++++++++++++++++++++++++++++++- models/site.rb | 2 ++ 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index bff9dbc3..022c817b 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,7 @@ gem 'cocaine' gem 'zipruby' gem 'always_verify_ssl_certificates' gem 'sass', require: nil +gem 'dav4rack' platform :mri do gem 'magic' # sudo apt-get install file, For OSX: brew install libmagic diff --git a/Gemfile.lock b/Gemfile.lock index 7c754a5c..042aaf46 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -43,16 +43,20 @@ GEM cocaine (0.5.4) climate_control (>= 0.0.3, < 1.0) coderay (1.1.0) - columnize (0.3.6) + columnize (0.8.9) connection_pool (2.0.0) crack (0.4.2) safe_yaml (~> 1.0.0) + dav4rack (0.3.0) + nokogiri (>= 1.4.2) + rack (>= 1.1.0) + uuidtools (~> 2.1.1) debugger (1.6.6) columnize (>= 0.3.1) debugger-linecache (~> 1.2.0) debugger-ruby_core_source (~> 1.3.2) debugger-linecache (1.2.0) - debugger-ruby_core_source (1.3.2) + debugger-ruby_core_source (1.3.5) docile (1.1.3) erubis (2.7.0) extlib (0.9.16) @@ -211,6 +215,7 @@ DEPENDENCIES bcrypt capybara_minitest_spec cocaine + dav4rack erubis fabrication faker diff --git a/app.rb b/app.rb index 0d24f4e5..77e9151e 100644 --- a/app.rb +++ b/app.rb @@ -692,7 +692,6 @@ post '/site_files/upload' do params[:files].each do |file| results << current_site.store_file(file[:filename], file[:tempfile]) end - current_site.increment_changed_count if results.include?(true) file_upload_response diff --git a/config.ru b/config.ru index c831afe5..128f127c 100644 --- a/config.ru +++ b/config.ru @@ -1,8 +1,64 @@ +require 'rubygems' require './app.rb' require 'sidekiq/web' map('/') { run Sinatra::Application } +map '/webdav' do + + use Rack::Auth::Basic do |username, password| + Site.valid_login? username, password + end + + run lambda {|env| + site = Site[username: env['REMOTE_USER']] + + if env['REQUEST_METHOD'] == 'PUT' + path = env['PATH_INFO'] + tmpfile = Tempfile.new 'davfile', encoding: 'binary' + tmpfile.write env['rack.input'].read + tmpfile.close + + if site.file_size_too_large? tmpfile.size + return [507, {}, ['']] + end + + if Site.valid_file_type?(filename: path, tempfile: tmpfile) + site.store_file path, tmpfile + return [201, {}, ['']] + else + return [415, {}, ['']] + end + end + + if env['REQUEST_METHOD'] == 'MOVE' + tmpfile = Tempfile.new 'moved_file' + tmpfile.close + + destination = env['HTTP_DESTINATION'].match(/^.+\/webdav(.+)$/i).captures.first + + FileUtils.cp site.files_path(env['PATH_INFO']), tmpfile.path + + DB.transaction do + site.store_file destination, tmpfile + site.delete_file env['PATH_INFO'] + end + + return [201, {}, ['']] + end + + if env['REQUEST_METHOD'] == 'DELETE' + site.delete_file env['PATH_INFO'] + return [201, {}, ['']] + end + + res = DAV4Rack::Handler.new( + root: Site.select(:username).where(username: env['REMOTE_USER']).first.files_path, + root_uri_path: '/webdav' + ).call(env) + } +end + map '/sidekiq' do use Rack::Auth::Basic, "Protected Area" do |username, password| raise 'missing sidekiq auth' unless $config['sidekiq_user'] && $config['sidekiq_pass'] @@ -10,4 +66,4 @@ map '/sidekiq' do end run Sidekiq::Web -end +end \ No newline at end of file diff --git a/models/site.rb b/models/site.rb index 5c751e10..c93d258b 100644 --- a/models/site.rb +++ b/models/site.rb @@ -439,6 +439,8 @@ class Site < Sequel::Model screenshots_delete(path) if ext.match HTML_REGEX thumbnails_delete(path) if ext.match IMAGE_REGEX + path = path[1..path.length] if path[0] == '/' + SiteChangeFile.filter(site_id: self.id, filename: path).delete true