d4l3k/WebSync

View on GitHub
lib/routes/upload_download.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module WebSync
  module Routes
    # The routes for uploading, downloading and converting files.
    class UploadDownload < Base
      helpers do

        # Fail the conversion by displaying a message and redirecting to /.
        def conversion_fail
          flash[:danger] = "'#{h params[:file][:filename]}' failed to be converted."
          redirect "/"
        end

        # Upload a file
        #
        # @param file [Hash] A file hash with options :tempfile, :filename, and :type.
        # @param type [String] The file type
        # @param parent [WSFile] The parent file.
        # @return [WSFile] the new file
        def upload_file file: params['file'], type: file[:type], parent: nil
          # Fingerprint file for mime-type if we aren't provided with it.
          if type == 'application/octet-stream'
            type = get_mime_type(file[:tempfile].path)
          end
          blob = WSFile.create(name: file[:filename], content_type: type, parent: parent)
          blob.data = file[:tempfile].read
          Permission.create(user: current_user, file: blob, level: "owner")
          blob
        end

        # Convert some HTML into a new file.
        #
        # @param html [String] the HTML content to convert
        def upload_html html
          dom = Nokogiri::HTML(content)
          upload_list = []
          dom.css("img[src]").each do |img|
            path = img.attr("src").split("/").last
            # Security check, make sure it starts with RackMultipart and it exists.
            if File.exists? "/tmp/#{path}" and /^#{tempfile.path}/.match img.attr("src")
              upload_list.push path
              img["src"] = "assets/#{path}"
            end
          end
          sanitize_upload dom
          doc = WSFile.create(
            name: filename,
            body: {
              html: dom.to_html
            },
            content_type: 'text/websync'
          )
          doc.assets = AssetGroup.get(1).assets
          doc.save
          perm = Permission.create(user: current_user, file: doc, level: "owner")

          # Upload images
          upload_list.each do |file|
            path = "/tmp/#{file}"
            upload_file(parent: doc, file: {
              type: get_mime_type(path),
              tempfile: File.new(path),
              filename: file
            })
            File.delete path
          end
          if doc.id
            flash[:success] = "'#{h params[:file][:filename]}' was successfully converted."
            redirect "/#{doc.id.encode62}/edit"
          else
            conversion_fail
          end
        end
      end

      get '/upload' do
        login_required
        cache do
          erb :upload
        end
      end

      post '/upload' do
        login_required
        if params[:file]==nil
          redirect "/upload"
        end
        tempfile = params[:file][:tempfile]
        filename = params[:file][:filename]
        content = nil
        # TODO: Split upload/download into its own external server. Right now Unoconv is blocking. Also issues may arise if multiple copies of LibreOffice are running on the same server. Should probably use a single server instance of LibreOffice
        if params["convert"]
          content = convert_file tempfile
          File.delete tempfile.path
          if content!=nil
            upload_html content
          else
            conversion_fail
          end
        else
          upload_file
          flash[:success] = "'#{h file[:filename]}' was successfully uploaded."
          redirect "/"
        end
      end

      # This doesn't need to verify authentication because the token is a 16 byte string.
      get '/:doc/download/:id' do
        doc = document_auth
        key = "websync:document_export:#{params[:doc].decode62}:#{params[:id]}"
        response = $redis.get key
        if response
          ext = $redis.get "#{key}:extension"
          attachment("#{doc.name}.#{ext}")
          content_type 'application/octet_stream'
          response
        else
          halt 404
        end
      end
    end
  end
end