class ElFinderFtp::FtpAdapter
Attributes
Public Class Methods
# File lib/el_finder_ftp/ftp_adapter.rb, line 7 def initialize(server) @server = { response_cache_expiry_seconds: 30, passive: false, }.merge(server) @cached_responses = {} end
Public Instance Methods
# File lib/el_finder_ftp/ftp_adapter.rb, line 37 def children(pathname, with_directory) cached :children, pathname do ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Fetching children of #{pathname}" list("-la", pathname).map { |e| entry = Net::FTP::List.parse(e) # Skip . and .. entries next if entry.basename =~ /^\.+$/ # ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Seeing #{e}" if with_directory pathname.fullpath + ::ElFinderFtp::FtpPathname.new(self, entry) else ::ElFinderFtp::FtpPathname.new(self, entry) end }.compact end end end
# File lib/el_finder_ftp/ftp_adapter.rb, line 26 def close if connected? ElFinderFtp::Connector.logger.info " \e[1;32mFTP:\e[0m Closing connection to #{server[:host]}" @connection.close end end
# File lib/el_finder_ftp/ftp_adapter.rb, line 16 def connect unless connected? ElFinderFtp::Connector.logger.info " \e[1;32mFTP:\e[0m Connecting to #{server[:host]} as #{server[:username]}" @connection = Net::FTP.new( server[:host], server[:username], server[:password] ) @connection.passive = server[:passive] end @connection end
# File lib/el_finder_ftp/ftp_adapter.rb, line 33 def connected? self.connection && !self.connection.closed? end
# File lib/el_finder_ftp/ftp_adapter.rb, line 177 def delete(pathname) ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Deleting #{pathname}" if pathname.directory? rmdir(pathname.to_s) else delete(pathname.to_s) end end clear_cache(pathname) end
# File lib/el_finder_ftp/ftp_adapter.rb, line 75 def exist?(pathname) cached :exist?, pathname do ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Testing existence of #{pathname}" begin # Test if the file exists size(pathname.to_s) true rescue Net::FTPPermError => ex # Can't "size" directories, but the error returned is different than if the file # doesn't exist at all if ex.message.match /(?:The system cannot find the file specified|Could not get file size)/ false else true end end end end end
# File lib/el_finder_ftp/ftp_adapter.rb, line 161 def mkdir(pathname) ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Creating directory #{pathname}" mkdir(pathname.to_s) end clear_cache(pathname) end
Both rename and move perform an FTP RNFR/RNTO (rename). Move differs because it first changes to the parent of the source pathname and uses a relative path for the RNFR. This seems to allow the (Microsoft) FTP server to rename a directory into another directory (e.g. /subdir/target -> /target )
# File lib/el_finder_ftp/ftp_adapter.rb, line 152 def move(pathname, new_name) ftp_context(pathname.dirname) do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Moving #{pathname} to #{new_name}" rename(pathname.basename.to_s, new_name.to_s) end clear_cache(pathname) clear_cache(new_name) end
# File lib/el_finder_ftp/ftp_adapter.rb, line 125 def mtime(pathname) cached :mtime, pathname do ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Getting modified time of #{pathname}" begin mtime(pathname.to_s) rescue Net::FTPPermError => e # This command doesn't work on directories 0 end end end end
# File lib/el_finder_ftp/ftp_adapter.rb, line 96 def path_type(pathname) cached :path_type, pathname do ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Getting type of #{pathname}" begin chdir(pathname.to_s) :directory rescue Net::FTPPermError => e :file end end end end
# File lib/el_finder_ftp/ftp_adapter.rb, line 139 def rename(pathname, new_name) ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Renaming #{pathname} to #{new_name}" rename(pathname.to_s, new_name.to_s) end clear_cache(pathname) end
# File lib/el_finder_ftp/ftp_adapter.rb, line 189 def retrieve(pathname) ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Retrieving #{pathname}" content = StringIO.new() begin retrbinary("RETR #{pathname}", 10240) do |block| content << block end content.string ensure content.close end end end
# File lib/el_finder_ftp/ftp_adapter.rb, line 169 def rmdir(pathname) ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Removing directory #{pathname}" rmdir(pathname.to_s) end clear_cache(pathname) end
# File lib/el_finder_ftp/ftp_adapter.rb, line 110 def size(pathname) cached :size, pathname do ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Getting size of #{pathname}" begin size(pathname.to_s) rescue Net::FTPPermError => e nil rescue Net::FTPReplyError => e nil end end end end
# File lib/el_finder_ftp/ftp_adapter.rb, line 204 def store(pathname, content) ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Storing #{pathname}" # If content is a string, wrap it in a StringIO content = StringIO.new(content) if content.is_a? String begin storbinary("STOR #{pathname}", content, 10240) ensure content.close if content.respond_to?(:close) end end clear_cache(pathname) end
# File lib/el_finder_ftp/ftp_adapter.rb, line 58 def touch(pathname, options={}) unless exist?(pathname) ftp_context do ElFinderFtp::Connector.logger.debug " \e[1;32mFTP:\e[0m Touching #{pathname}" empty_file = StringIO.new("") # File does not exist, create begin storlines("STOR #{pathname}", empty_file) ensure empty_file.close end end clear_cache(pathname) end true end
Private Instance Methods
Fetch an FTP response from the cache
# File lib/el_finder_ftp/ftp_adapter.rb, line 294 def cache_get(operation, pathname) if @cached_responses.include?(pathname.to_s) && @cached_responses[pathname.to_s].include?(operation) response = @cached_responses[pathname.to_s][operation] max_staleness = Time.now - @server[:response_cache_expiry_seconds] if response[:timestamp] < max_staleness @cached_responses[pathname.to_s].delete(operation) nil else response[:response] end end end
Store an FTP response in the cache
# File lib/el_finder_ftp/ftp_adapter.rb, line 283 def cache_put(operation, pathname, response) @cached_responses[pathname.to_s] = {} unless @cached_responses.include?(pathname.to_s) @cached_responses[pathname.to_s][operation] = { timestamp: Time.now, response: response } end
Looks in the cache for an entry for the given pathname and operation, returning the cached result if one is found. If one is not found, the given block is invoked and its result is stored in the cache and returned
The FTP cache is used to prevent redundant FTP queries for information such as a file’s size, or a directory’s contents, during a single FTP session.
# File lib/el_finder_ftp/ftp_adapter.rb, line 269 def cached(operation, pathname) response = cache_get(operation, pathname) unless response.nil? return response end response = yield cache_put operation, pathname, response response end
Remove all entries for the given pathname (and its parent folder) from the FTP cache
# File lib/el_finder_ftp/ftp_adapter.rb, line 253 def clear_cache(pathname) @cached_responses.delete(pathname.to_s) if pathname.to_s != '/' && pathname.to_s != '.' # Clear parent of this entry, too @cached_responses.delete(pathname.dirname.to_s) end end
Creates an FTP connection, if necessary, and executes the given block in the context of that connection. If a pathname is provided, it is used to set the current working directory first
# File lib/el_finder_ftp/ftp_adapter.rb, line 224 def ftp_context(pathname = nil, &block) begin connect self.connection.chdir(pathname) unless pathname.nil? self.connection.instance_eval &block rescue Net::FTPPermError => ex if ex.message =~ /(?:User cannot log in|Login incorrect)/ ElFinderFtp::Connector.logger.info " \e[1;32mFTP:\e[0m Authentication required: #{ex}" raise FtpAuthenticationError.new(ex.message) else ElFinderFtp::Connector.logger.error " \e[1;32mFTP:\e[0m Operation failed with error #{ex}" raise end rescue Net::FTPReplyError => ex if ex.message =~ /(?:Password required|Login incorrect)/ ElFinderFtp::Connector.logger.info " \e[1;32mFTP:\e[0m Authentication required: #{ex}" raise FtpAuthenticationError.new(ex.message) else ElFinderFtp::Connector.logger.error " \e[1;32mFTP:\e[0m Operation failed with error #{ex}" raise end end end