class ElFinderFtp::Connector

Represents ElFinder connector on Rails side.

Constants

DEFAULT_OPTIONS

Default options for instances. @see initialize

VALID_COMMANDS

Valid commands to run. @see run

Attributes

adapter[R]

Public Class Methods

logger() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 60
def logger
  @logger ||= Logger.new(STDOUT)
end
logger=(val) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 63
def logger=(val)
  @logger = val
end
new(options) click to toggle source

Initializes new instance. @param [Hash] options Instance options. :url and :server options are required. @option options [String] :url Entry point of ElFinder router. @option options [String] :server A hash containing the :host, :username, :password, and, optionally, :port to connect to @see DEFAULT_OPTIONS

# File lib/el_finder_ftp/connector.rb, line 46
def initialize(options)
  @options = DEFAULT_OPTIONS.merge(options)

  raise(ArgumentError, "Missing required :url option") unless @options.key?(:url) 
  raise(ArgumentError, "Missing required :server option") unless @options.key?(:server) 
  raise(ArgumentError, "Mime Handler is invalid") unless mime_handler.respond_to?(:for)
  raise(ArgumentError, "Image Handler is invalid") unless image_handler.nil? || ([:size, :resize, :thumbnail].all?{|m| image_handler.respond_to?(m)})

  @headers = {}
  @response = {}
end

Public Instance Methods

from_hash(hash) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 121
def from_hash(hash)
  # restore missing '='
  len = hash.length % 4
  hash += '==' if len == 1 or len == 2
  hash += '='  if len == 3

  decoded_hash = Base64.urlsafe_decode64(hash)
  decoded_hash = decoded_hash.respond_to?(:force_encoding) ? decoded_hash.force_encoding('utf-8') : decoded_hash
  pathname = @root + decoded_hash
rescue ArgumentError => e
  if e.message != 'invalid base64'
    raise
  end
  nil
end
options=(value = {}) click to toggle source

@!attribute [w] options Options setter. @param value [Hash] Options to be merged with instance ones. @return [Hash] Updated options.

# File lib/el_finder_ftp/connector.rb, line 141
def options=(value = {})
  value.each_pair do |k, v|
    @options[k.to_sym] = v
  end
  @options
end
run(params) click to toggle source

Runs request-response cycle. @param [Hash] params Request parameters. :cmd option is required. @option params [String] :cmd Command to be performed. @see VALID_COMMANDS

# File lib/el_finder_ftp/connector.rb, line 72
def run(params)

  @adapter = ElFinderFtp::FtpAdapter.new(@options[:server])
  @root = ElFinderFtp::Pathname.new(adapter)

  begin
    @params = params.dup
    @headers = {}
    @response = {}
    @response[:errorData] = {}

    if VALID_COMMANDS.include?(@params[:cmd])

      if @options[:thumbs]
        @thumb_directory = @root + @options[:thumbs_directory]
        @thumb_directory.mkdir unless @thumb_directory.exist? 
        raise(RuntimeError, "Unable to create thumbs directory") unless @thumb_directory.directory?
      end

      @current = @params[:current] ? from_hash(@params[:current]) : nil
      @target = (@params[:target] and !@params[:target].empty?) ? from_hash(@params[:target]) : nil
      if params[:targets]
        @targets = @params[:targets].map{|t| from_hash(t)}
      end

      begin
        send("_#{@params[:cmd]}")
      rescue Net::FTPPermError
        @response[:error] = 'Access Denied'
      end
    else
      invalid_request
    end

    @response.delete(:errorData) if @response[:errorData].empty?

    return @headers, @response
  ensure
    adapter.close
  end
end
to_hash(pathname) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 115
def to_hash(pathname)
  # note that '=' are removed
  Base64.urlsafe_encode64(pathname.path.to_s).chomp.tr("=\n", "")
end

Protected Instance Methods

_archive() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 409
def _archive
  command_not_implemented
end
_duplicate() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 367
def _duplicate
  command_not_implemented 
end
_extract() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 404
def _extract
  command_not_implemented
end
_file() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 381
def _file
  if perms_for(@target)[:read] == true
    @response[:file_data] = @target.read
    @response[:mime_type] = mime_handler.for(@target)
    @response[:disposition] = 'attachment'
    @response[:filename] = @target.basename.to_s
  else
    @response[:error] = 'Access Denied'
  end
end
_get() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 372
def _get
  if perms_for(@target)[:read] == true
    @response[:content] = @target.read
  else
    @response[:error] = 'Access Denied'
  end
end
_ls() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 196
def _ls
  if @target.directory?
    files = @target.files.reject{ |child| perms_for(child)[:hidden] }

    @response[:list] = files.map{|e| e.basename.to_s }.compact
  else
    @response[:error] = "Directory does not exist"
  end

end
_mkdir() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 217
def _mkdir
  if perms_for(@target)[:write] == false
    @response[:error] = 'Access Denied'
    return
  end
  unless valid_name?(@params[:name])
    @response[:error] = 'Unable to create folder'
    return
  end

  dir = @target + @params[:name]
  if !dir.exist? && dir.mkdir
    @response[:added] = [cdc_for(dir)]
  else
    @response[:error] = "Unable to create folder"
  end
end
_mkfile() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 236
def _mkfile
  if perms_for(@target)[:write] == false
    @response[:error] = 'Access Denied'
    return
  end
  unless valid_name?(@params[:name])
    @response[:error] = 'Unable to create file'
    return
  end

  file = @target + @params[:name]
  if !file.exist? && file.touch
    @response[:added] = [cdc_for(file)]
  else
    @response[:error] = "Unable to create file"
  end
end
_open(target = nil) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 152
def _open(target = nil)
  target ||= @target

  if target.nil?
    _open(@root)
    return
  end

  if perms_for(target)[:read] == false
    @response[:error] = 'Access Denied'
    return
  end

  if target.file?
    command_not_implemented
  elsif target.directory?
    @response[:cwd] = cdc_for(target)
    files = [target].concat( target.files.reject{ |child| perms_for(child)[:hidden] } )

    if @params[:tree]
      files = files.concat( tree_for(@root) )
    else
      files = files.concat( target.child_directories.reject{ |child| perms_for(child)[:hidden] || child.fullpath == target.fullpath } )
    end

    @response[:files] = files.map{|e| cdc_for(e)}.compact

    if @params[:init]
      @response[:api] = 2
      @response[:uplMaxSize] = @options[:upload_max_size]
      @response[:options] = {
        :disabled => @options[:disabled_commands],
        :dotFiles => @options[:allow_dot_files],
        :url => @options[:url]
      }
    end

  else
    @response[:error] = "Directory does not exist"
    _open(@root) if File.directory?(@root)
  end

end
_paste() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 317
def _paste
  if perms_for(from_hash(@params[:dst]))[:write] == false
    @response[:error] = 'Access Denied'
    return
  end

  added_list = []
  removed_list = []
  @targets.to_a.each do |src|
    if perms_for(src)[:read] == false || (@params[:cut].to_i > 0 && perms_for(src)[:locked] == true)
      @response[:error] ||= 'Some files were not moved.'
      @response[:errorData][src.basename.to_s] = "Access Denied"
      return
    else
      dst = from_hash(@params[:dst]) + src.basename
      if dst.exist?
        @response[:error] ||= 'The target file already exists'
        @response[:errorData][src.basename.to_s] = "already exists in '#{dst.dirname}'"
      else
        if @params[:cut].to_i > 0
          adapter.move(src, dst)

          added_list.push cdc_for(dst)
          removed_list.push to_hash(src)
        else
          command_not_implemented
          return
        end
      end
    end
  end

  @response[:added] = added_list unless added_list.empty?
  @response[:removed] = removed_list unless removed_list.empty?
end
_ping() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 312
def _ping
  @headers['Connection'] = 'Close'
end
_put() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 393
def _put
  perms = perms_for(@target)
  if perms[:read] == true && perms[:write] == true
    @target.write @params[:content]
    @response[:changed] = [cdc_for(@target)]
  else
    @response[:error] = 'Access Denied'
  end
end
_rename() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 255
def _rename
  unless valid_name?(@params[:name])
    @response[:error] = "Unable to rename #{@target.ftype}"
    return
  end

  to = @target.dirname + @params[:name]

  perms_for_target = perms_for(@target)
  if perms_for_target[:locked] == true
    @response[:error] = 'Access Denied'
    return
  end

  perms_for_current = perms_for(@target)
  if perms_for_current[:write] == false
    @response[:error] = 'Access Denied'
    return
  end

  if to.exist?
    @response[:error] = "Unable to rename #{@target.ftype}. '#{to.basename}' already exists"
  else        
    to = @target.rename(to)
    if to 
      @response[:added] = [cdc_for(to)]
      @response[:removed] = [to_hash(@target)] 
    else
      @response[:error] = "Unable to rename #{@target.ftype}"
    end
  end
end
_resize() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 419
def _resize
  command_not_implemented
end
_rm() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 354
def _rm
  if @targets.empty?
    @response[:error] = "No files were selected for removal"
  else
    removed_list = []        
    @targets.to_a.each do |target|
      removed_list.concat remove_target(target)
    end
    @response[:removed] = removed_list unless removed_list.empty?
  end
end
_tmb() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 414
def _tmb
  command_not_implemented
end
_tree() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 207
def _tree
  if @target.directory?
    @response[:tree] = tree_for(@target).map{|e| cdc_for(e) }.compact
  else
    @response[:error] = "Directory does not exist"
  end

end
_upload() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 289
def _upload
  if perms_for(@target)[:write] == false
    @response[:error] = 'Access Denied'
    return
  end
  added_list = []
  @params[:upload].to_a.each do |io|
    name = @options[:original_filename_method].call(io)
    unless valid_name?(name)
      @response[:error] = 'Unable to create file'
      return
    end
    dst = @target + name

    dst.write(io)
    
    added_list.push cdc_for(dst)
  end
  @response[:added] = added_list unless added_list.empty?
  _open(@current)
end

Private Instance Methods

cdc_for(pathname) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 486
def cdc_for(pathname)
  return nil if @options[:thumbs] && pathname.to_s == @thumb_directory.to_s
  response = {
    :name => pathname.is_root? ? "Home" : pathname.basename.to_s,
    :hash => to_hash(pathname),
    :date => pathname.mtime.to_s,
    :ts => pathname.mtime.to_i
  }
  response.merge! perms_for(pathname)

  response[:phash] = to_hash(pathname.dirname) unless pathname.is_root?

  if pathname.directory?
    response.merge!(
      :size => 0,
      :mime => 'directory', 
      :dirs => pathname.child_directories.size
    )

    response[:volumeid] = 'Home' if pathname.is_root?
  elsif pathname.file?
    response.merge!(
      :size => pathname.size, 
      :mime => mime_handler.for(pathname),
      :url => (@options[:url] + '/' + pathname.path.to_s)
    )

    if pathname.readable? && response[:mime] =~ /image/ && !image_handler.nil?
      response.merge!(
        :resize => true,
        :dim => image_handler.size(pathname)
      )
      if @options[:thumbs] 
        if (thumbnail = thumbnail_for(pathname)).exist?
          response.merge!( :tmb => (@options[:url] + '/' + thumbnail.path.to_s))
        else
          @response[:tmb] = true 
        end
      end
    end

  end

  if pathname.symlink?
    response.merge!(
      :link => to_hash(@root + pathname.readlink), # hash of file to which point link
      :linkTo => (@root + pathname.readlink).relative_to(pathname.dirname.path).to_s, # relative path to
      :parent => to_hash((@root + pathname.readlink).dirname) # hash of directory in which is linked file
    )
  end

  return response
end
command_not_implemented() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 613
def command_not_implemented
  @response[:error] = "Command '#{@params[:cmd]}' not implemented"
end
image_handler() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 482
def image_handler
  @options[:image_handler]
end
invalid_request() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 608
def invalid_request
  @response[:error] = "Invalid command '#{@params[:cmd]}'"
end
mime_handler() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 477
def mime_handler
  @options[:mime_handler]
end
perms_for(pathname, options = {}) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 568
def perms_for(pathname, options = {})
  skip = [options[:skip]].flatten
  response = {}

  response[:read] = pathname.readable? 
  response[:read] &&= specific_perm_for(pathname, :read)
  response[:read] &&= @options[:default_perms][:read] 

  response[:write] = pathname.writable? 
  response[:write] &&= specific_perm_for(pathname, :write) 
  response[:write] &&= @options[:default_perms][:write]

  response[:locked] = pathname.is_root?
  response[:locked] &&= specific_perm_for(pathname, :locked)
  response[:locked] &&= @options[:default_perms][:locked]

  response[:hidden] = false
  response[:hidden] ||= specific_perm_for(pathname, :hidden)
  response[:hidden] ||= @options[:default_perms][:hidden]

  response
end
remove_target(target) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 450
def remove_target(target)
  removed = []
  if target.directory?
    target.children.each do |child|
      removed.concat remove_target(child)
    end
  end
  if perms_for(target)[:locked] == true
    @response[:error] ||= 'Some files/directories were unable to be removed'
    @response[:errorData][target.basename.to_s] = "Access Denied"
  else
    begin
      removed.push to_hash(target)
      target.unlink
      if @options[:thumbs] && (thumbnail = thumbnail_for(target)).file?
        removed.push to_hash(thumbnail)
        thumbnail.unlink
      end
    rescue Exception => ex
      @response[:error] ||= 'Some files/directories were unable to be removed'
      @response[:errorData][target.basename.to_s] = "Remove failed"
    end
  end

  removed
end
specific_perm_for(pathname, perm) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 592
def specific_perm_for(pathname, perm)
  pathname = pathname.path if pathname.is_a?(ElFinderFtp::Pathname)
  matches = @options[:perms].select{ |k,v| pathname.to_s.send((k.is_a?(String) ? :== : :match), k) }
  if perm == :hidden
    matches.one?{|e| e.last[perm] }
  else
    matches.none?{|e| e.last[perm] == false}
  end
end
thumbnail_for(pathname) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 445
def thumbnail_for(pathname)
  @thumb_directory + "#{to_hash(pathname)}.png"
end
tree_for(root) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 541
def tree_for(root)

  # root.child_directories.
  # reject{ |child|
  #   ( @options[:thumbs] && child.to_s == @thumb_directory.to_s ) || perms_for(child)[:hidden]
  # }.
  # sort_by{|e| e.basename.to_s.downcase}.
  # map { |child|
  #     {:name => child.basename.to_s,
  #      :hash => to_hash(child),
  #      :dirs => tree_for(child),
  #     }.merge(perms_for(child))
  # }

  children = [root]
  flattened_tree = []
  while !children.empty? do
    child = children.pop
    unless (@options[:thumbs] && child.to_s == @thumb_directory.to_s ) || perms_for(child)[:hidden] 
      flattened_tree.push child
      children.concat child.child_directories
    end
  end
  flattened_tree
end
upload_max_size_in_bytes() click to toggle source
# File lib/el_finder_ftp/connector.rb, line 427
def upload_max_size_in_bytes
  bytes = @options[:upload_max_size]
  if bytes.is_a?(String) && bytes.strip =~ /(\d+)([KMG]?)/
    bytes = $1.to_i
    unit = $2
    case unit
      when 'K'
        bytes *= 1024
      when 'M'
        bytes *= 1024 * 1024
      when 'G'
        bytes *= 1024 * 1024 * 1024
    end
  end
  bytes.to_i
end
valid_name?(name) click to toggle source
# File lib/el_finder_ftp/connector.rb, line 603
def valid_name?(name)
  @options[:name_validator].call(name)
end