theirix/omnifiles

View on GitHub
lib/omnifiles/protectedapp.rb

Summary

Maintainability
A
0 mins
Test Coverage
# encoding: utf-8

require 'sinatra/base'
require 'sinatra/flash'
require 'filemagic'
require 'uri'
require 'tempfile'
require 'settingslogic'
require 'haml'
require 'tilt/haml'
require 'rack'
require 'sprockets'
require 'sprockets-helpers'

module OmniFiles

  # Protected app for POST request
  class ProtectedApp < BaseApp

    use Rack::Auth::Digest::MD5, "OmniFiles Realm", Settings.auth_opaque do |_|
      Settings.auth_password
    end

    set :sprockets, Sprockets::Environment.new(root)
    set :assets_prefix, '/omnifiles-assets'

    configure do
      Sprockets::Helpers.configure do |config|
        config.environment = sprockets
        config.prefix      = assets_prefix
        config.digest      = true
      end
      sprockets.append_path 'assets/stylesheets'
      sprockets.css_compressor = :scss
    end

    helpers Sprockets::Helpers

    enable :sessions
    set :session_secret, Settings.session_secret

    register Sinatra::Flash

    # POST a file
    post '/store/' do
      store_file
    end

    post '/store' do
      store_file
    end

    def make_visible_filename filename
        filename ? URI.unescape(filename) : "<i>Not provided</i>"
    end

    def format_time_str time
      if time
        time.localtime.to_s
      else
        '<i>Not yet</i>'
      end
    end

    def make_haml_data_from_doc doc
      {
        shortened: doc['shortened'],
        url: url('/f/' + doc['shortened']),
        original_filename: make_visible_filename(doc['original_filename']),
        mime: doc['mime'],
        access_time: format_time_str(doc['accessed']['time']),
        created_time: format_time_str(doc['created']['time']),
        access_count: doc['accessed']['count']
      }
    end

    # GET stat of file
    get '/stat/:name' do |name|
      logger.info "Route GET stat #{name}"

      halt 500, "Wrong URL" if name != BaseApp.sanitize(name)

      data = @storage.get_file name
      halt 404, "File not found" unless data

      @hdata = make_haml_data_from_doc data
      logger.info @hdata.inspect

      haml :stat
    end

    # POST to delete file
    # cannot remap methods
    post '/stat/:name/delete' do |name|
      logger.info "Route POST to delete file #{name}"

      target_path = File.join(Settings.storage_dir, name)

      if @storage.delete_file(name)
        if File.file?(target_path)
          FileUtils.rm target_path
          flash[:notice] = "Successfully deleted file #{name}"
        else
          flash[:error] = "Cannot delete file #{name} from disk"
        end
      else
        flash[:error] = "Cannot delete file #{name} from mongo"
      end

      redirect to('/stat')
    end

    # GET index
    get '/stat' do
        logger.info "Route GET index"

        @hdata = []
        @storage.enumerate_docs do |doc|
            @hdata << make_haml_data_from_doc(doc)
        end

        haml :index
    end

    # POST handler with form/body handling
    def store_file
      logger.info "Route POST store"
      begin
        req = Rack::Request.new(env)
        if !req.POST || req.POST == {}
          logger.info "Saving POST body to temp file"

          original_filename = nil

          # Make a temp file with body content
          temp_file = Tempfile.new("omnifiles-post-")
          File.open(temp_file.path, 'wb') do |ftemp|
            IO.copy_stream(req.body, ftemp)
          end
        else
          logger.info "Using POST form"

          # Use a Rack provided file with content
          post_file = req.POST['file']
          original_filename = URI.escape(File.basename(post_file[:filename]))

          temp_file = post_file[:tempfile]
        end

        store_with_file temp_file.path, original_filename

      ensure
        if temp_file
          temp_file.close
          temp_file.unlink
        end
      end
    end

    # Save temporary file to storage
    def store_with_file path, original_filename
      # Take a sample of file
      sample = Digest::MD5.hexdigest(IO.binread(path, 0x100))

      # Determine file mime and desired url
      mime = FileMagic.mime.file path

      # Short URL is composed from escaped filename from form, mime type and leading file bytes
      shortened = @storage.shorten_file sample, original_filename, mime

      # Save file to storage
      target_path = File.join(Settings.storage_dir, shortened)
      raise "Not so unique id #{shortened}" if File.exists? target_path
      FileUtils.cp path, target_path

      # Put record to storage
      @storage.put_file shortened, original_filename, mime
      short_url = url('/f/'+shortened)

      logger.info "Stored file #{target_path} to shortened #{shortened}, magic '#{mime}'"

      short_url
    end

  end

end