em-ftpd-fsd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem 'yard', '~> 0.8.2.1'
5
+ gem 'redcarpet', '~> 2.2.2'
6
+ gem 'jeweler', '~> 1.8.4'
7
+ end
8
+
9
+ group :test do
10
+ gem 'rspec', '~> 2.12.0'
11
+ gem 'simplecov', '~> 0.7.1'
12
+ end
data/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # Event Machine FTPD File System Driver
2
+ em-ftpd-fsd is a simple driver to be used with EventMachine FTP gem (em-ftpd) that use
3
+ file system as a persistence layer.
4
+
5
+ It implements basic ftp operations, authentication and callbacks (before and after)
6
+
7
+ # Usage
8
+ To use the driver, first you have to create a class that includes EM::FTPD::FSD::Base class.
9
+
10
+ require 'em-ftpd-fsd'
11
+
12
+ class BasicDriver
13
+ include EM::FTPD::FSD::Base
14
+ end
15
+
16
+ Then you can create a configuration file for em-ftpd as usual using that class as driver.
17
+
18
+ # EM-FTPD
19
+ EM-FTP is not required by this driver explicitly, you have to require it or add it to your Gemfile.
20
+ By now, em-ftpd has no activity and many forks has been created so you can pick any existing repository
21
+ not only the offically supported gem.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler'
4
+ begin
5
+ Bundler.setup(:default, :development, :test)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "em-ftpd-fsd"
18
+ gem.homepage = "https://github.com/nanocity/em-ftpd-fsd"
19
+ gem.license = "GPLv3"
20
+ gem.summary = %Q{File System Driver for EM-FTPD server}
21
+ gem.description = %Q{Very simple file system driver for EM-FTPD server; including common FTP commands, authentication and before and after operation hooks.}
22
+ gem.email = "lciudad@nosolosoftware.biz"
23
+ gem.authors = ["Luis Ciudad"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'yard'
29
+ YARD::Rake::YardocTask.new( :doc ) do |t|
30
+ t.options = [ "-m", "markdown", "-r", "README.md" ]
31
+ end
32
+
33
+ require 'rspec/core/rake_task'
34
+ RSpec::Core::RakeTask.new( :spec )
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "em-ftpd-fsd"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Luis Ciudad"]
12
+ s.date = "2012-11-15"
13
+ s.description = "Very simple file system driver for EM-FTPD server; including common FTP commands, authentication and before and after operation hooks."
14
+ s.email = "lciudad@nosolosoftware.biz"
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".simplecov",
20
+ "COPYING",
21
+ "Gemfile",
22
+ "README.md",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "em-ftpd-fsd.gemspec",
26
+ "lib/em-ftpd-fsd.rb",
27
+ "lib/em-ftpd-fsd/authentication/plain.rb",
28
+ "lib/em-ftpd-fsd/base.rb",
29
+ "lib/em-ftpd-fsd/directory_item.rb",
30
+ "lib/em-ftpd-fsd/file_operations.rb",
31
+ "lib/em-ftpd-fsd/hooks.rb",
32
+ "spec/base_spec.rb",
33
+ "spec/file_operations/authentication_spec.rb",
34
+ "spec/file_operations/bytes_spec.rb",
35
+ "spec/file_operations/change_dir_spec.rb",
36
+ "spec/file_operations/delete_dir_spec.rb",
37
+ "spec/file_operations/delete_file_spec.rb",
38
+ "spec/file_operations/dir_contents_spec.rb",
39
+ "spec/file_operations/get_file_spec.rb",
40
+ "spec/file_operations/make_dir_spec.rb",
41
+ "spec/file_operations/put_file_spec.rb",
42
+ "spec/file_operations/rename_spec.rb",
43
+ "spec/hooks_spec.rb",
44
+ "spec/spec_helper.rb",
45
+ "spec/support/files_helper.rb"
46
+ ]
47
+ s.homepage = "https://github.com/nanocity/em-ftpd-fsd"
48
+ s.licenses = ["GPLv3"]
49
+ s.require_paths = ["lib"]
50
+ s.rubygems_version = "1.8.10"
51
+ s.summary = "File System Driver for EM-FTPD server"
52
+
53
+ if s.respond_to? :specification_version then
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_development_dependency(%q<yard>, ["~> 0.8.2.1"])
58
+ s.add_development_dependency(%q<redcarpet>, ["~> 2.2.2"])
59
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
60
+ else
61
+ s.add_dependency(%q<yard>, ["~> 0.8.2.1"])
62
+ s.add_dependency(%q<redcarpet>, ["~> 2.2.2"])
63
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<yard>, ["~> 0.8.2.1"])
67
+ s.add_dependency(%q<redcarpet>, ["~> 2.2.2"])
68
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
69
+ end
70
+ end
71
+
@@ -0,0 +1,23 @@
1
+ # This file is part of em-ftpd-fsd.
2
+ #
3
+ # em-ftpd-fsd is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # em-ftpd-fsd is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with em-ftpd-fsd. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'fileutils'
17
+
18
+ require 'em-ftpd-fsd/authentication/plain'
19
+
20
+ require 'em-ftpd-fsd/file_operations'
21
+ require 'em-ftpd-fsd/directory_item'
22
+ require 'em-ftpd-fsd/hooks'
23
+ require 'em-ftpd-fsd/base'
@@ -0,0 +1,46 @@
1
+ # This file is part of em-ftpd-fsd.
2
+ #
3
+ # em-ftpd-fsd is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # em-ftpd-fsd is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with em-ftpd-fsd. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module EM
17
+ module FTPD
18
+ module FSD
19
+
20
+ # Holds different authentication mechanisims
21
+ module Authentication
22
+
23
+ # Simple plain authentication
24
+ module Plain
25
+
26
+ # Configure the authentication module
27
+ # @param [Hash] opts
28
+ # @option opts user Username
29
+ # @option opts password Password
30
+ def configure_authentication( opts )
31
+ @auth_user = opts[:user]
32
+ @auth_password = opts[:password]
33
+ end
34
+
35
+ # Check if stored username and password validates given user and password
36
+ # @param [String] user Username
37
+ # @param [String] password Password
38
+ # @yield [Boolean] True if username and password are correct
39
+ def authenticate( user, password, &block )
40
+ yield ( @auth_user == user && @auth_password == password )
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,144 @@
1
+ # This file is part of em-ftpd-fsd.
2
+ #
3
+ # em-ftpd-fsd is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # em-ftpd-fsd is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with em-ftpd-fsd. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ # Event Machine
17
+ module EM
18
+
19
+ # File Transport Protocol Driver
20
+ module FTPD
21
+
22
+ # File System Driver
23
+ module FSD
24
+
25
+ # Base module to include in custom drivers
26
+ module Base
27
+
28
+ # Supported FTP commands.
29
+ COMMANDS = {
30
+ bytes: { on_error_value: nil },
31
+ dir_contents: { on_error_value: nil },
32
+ get_file: { on_error_value: nil },
33
+ change_dir: { on_error_value: false },
34
+ delete_dir: { on_error_value: false },
35
+ delete_file: { on_error_value: false },
36
+ rename: { on_error_value: false },
37
+ make_dir: { on_error_value: false },
38
+ put_file: { on_error_value: false },
39
+ }
40
+
41
+ # Extend hooks module to load 'before' and 'after' class functions
42
+ def self.included( klass )
43
+ klass.extend( EM::FTPD::FSD::Hooks )
44
+ end
45
+
46
+ # Initiliaze the driver with basic options
47
+ # @param [Hash] options Options
48
+ # @option options base_path Base directory for FTP server
49
+ # @option options authentication Authentication module to load
50
+ # @example Default options for authentication
51
+ # {
52
+ # plain: {
53
+ # user: "admin",
54
+ # password: "root"
55
+ # }
56
+ # }
57
+ def initialize( options = {} )
58
+ opts = {
59
+ base_path: Dir.pwd,
60
+ authentication: {
61
+ plain: {
62
+ user: "admin",
63
+ password: "root"
64
+ }
65
+ }
66
+ }.merge( options )
67
+
68
+ load_auth_module( opts[:authentication] )
69
+
70
+ @base_path = opts[:base_path]
71
+ end
72
+
73
+ # Absolute path to FTP base directory.
74
+ # @return [String] Full path to FTP base folder.
75
+ def base_path
76
+ File.expand_path( @base_path || "" )
77
+ end
78
+
79
+ # Metaprogrammed redirection to FTP commands defined by FileOperation class.
80
+ # @param [Symbol] method Method called
81
+ # @param [Array] args All arguments received by called method
82
+ # @param [Block] block Block to be yield by called method
83
+ # @raise [NoMethodError] if method is not supported by driver
84
+ # @raise [ArgumentError] if no block is passed to yield value
85
+ # @yield [Object] Return value from file specific operation
86
+ def method_missing( method, *args, &block )
87
+ raise NoMethodError, "#{method}" unless COMMANDS.include?( method )
88
+ raise ArgumentError, "Block needed" unless block_given?
89
+
90
+ yield ftp_methods( method, args )
91
+ end
92
+
93
+ # Return true if obj respond to given method or method correspond to an
94
+ # implemented ftp command.
95
+ # @param [Symbol] method Given method
96
+ # @return [Boolean] Return true if obj respond to given method.
97
+ def respond_to?( method, include_private = false )
98
+ super( method, include_private ) || COMMANDS.include?( method )
99
+ end
100
+
101
+ private
102
+
103
+ def ftp_methods( method, args )
104
+ # We have to map each ftp path to a local file system path to check
105
+ # permissions, but when command is 'put_file' the second argument
106
+ # is an absolute path to temporary that should not be converted
107
+ tmp_file = args.delete_at( 1 ) if method == :put_file
108
+ args = args.map{ |arg| file_system_path( arg ) }
109
+
110
+ begin
111
+ # Bypass permissions for second argument if method is put_file
112
+ args.each{ |arg| check_file_system_permissions!( arg ) }
113
+ args.insert( 1, tmp_file ) if method == :put_file
114
+
115
+ send( self.class.before_hooks[method], *args ) if self.class.before_hooks[method]
116
+ value = EM::FTPD::FSD::FileOperations.send( method, *args )
117
+ send( self.class.after_hooks[method], *(args << value) ) if self.class.after_hooks[method]
118
+
119
+ return value
120
+ rescue Exception
121
+ return COMMANDS[method][:on_error_value]
122
+ end
123
+ end
124
+
125
+ def file_system_path( ftp_path )
126
+ "#{base_path}#{ftp_path}"
127
+ end
128
+
129
+ def check_file_system_permissions!( path )
130
+ unless File.expand_path( path ).include? base_path
131
+ raise SecurityError, "File system path out of FTP folder"
132
+ end
133
+ end
134
+
135
+ def load_auth_module( opts )
136
+ auth_mod = EM::FTPD::FSD::Authentication.const_get( opts.keys.first.to_s.capitalize )
137
+
138
+ self.class.send( :include, auth_mod )
139
+ configure_authentication( opts.values.first )
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,52 @@
1
+ # This file is part of em-ftpd-fsd.
2
+ #
3
+ # em-ftpd-fsd is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # em-ftpd-fsd is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with em-ftpd-fsd. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module EM
17
+ module FTPD
18
+ module FSD
19
+
20
+ # This is a copy of original DirectoryItem class found in yob's repository.
21
+ # The goal of duplication is not to required em-ftpd.
22
+ #
23
+ # Given gem official repository is not active and there are a lot of forks
24
+ # it is better to redefine this class here and be able to use em-ftpd from any
25
+ # source that tie the entire driver to a one repository.
26
+ class DirectoryItem
27
+
28
+ # Fields for DirectoryItem
29
+ ATTRS = [:name, :owner, :group, :size, :time, :permissions, :directory]
30
+
31
+ attr_accessor(*ATTRS)
32
+
33
+ # Initializer method
34
+ # @param [Hash] options
35
+ # @option options name
36
+ # @option options name
37
+ # @option options owner
38
+ # @option options group
39
+ # @option options size
40
+ # @option options time
41
+ # @option options permissions
42
+ # @option options directory
43
+ def initialize(options)
44
+ options.each do |attr, value|
45
+ self.send("#{attr}=", value)
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,98 @@
1
+ # This file is part of em-ftpd-fsd.
2
+ #
3
+ # em-ftpd-fsd is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # em-ftpd-fsd is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with em-ftpd-fsd. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module EM
17
+ module FTPD
18
+ module FSD
19
+
20
+ # Implements file system specific operations to be used by the base driver
21
+ class FileOperations
22
+
23
+ # Gives information about the directory content
24
+ # @param [String] path Absolute path to the directory
25
+ # @yield [Array] List of DirectoryItem-ish containing information about
26
+ # directory. Nil if there were errors or the path is not correct.
27
+ def self.dir_contents( path )
28
+ Dir.entries( path ).map do |filename|
29
+ EM::FTPD::FSD::DirectoryItem.new(
30
+ name: filename,
31
+ size: File.size?( "#{path}/#{filename}" ),
32
+ directory: File.directory?( "#{path}/#{filename}" )
33
+ )
34
+ end
35
+ end
36
+
37
+ # File size for the given file
38
+ # @param [String] path Absolute path to file
39
+ # @yield [Fixnum] File size or nil if there were errors or the path is not correct
40
+ def self.bytes( path )
41
+ File.size( path )
42
+ end
43
+
44
+ # Change current directory
45
+ # @param [String] path Absolute path to the directory
46
+ # @yield [Boolean] True if the change was correct false in other case
47
+ def self.change_dir( path )
48
+ !!File.directory?( path )
49
+ end
50
+
51
+ # Removes the given directory
52
+ # @param [String] path Absolute path to the directory
53
+ # @yield [Boolean] True if the deletion was correct false in other case
54
+ def self.delete_dir( path )
55
+ !!Dir.delete( path )
56
+ end
57
+
58
+ # Removes the given file
59
+ # @param [String] path Absolute path to the file
60
+ # @yield [Boolean] True if the deletion was correct false in other case
61
+ def self.delete_file( path )
62
+ !!File.delete( path )
63
+ end
64
+
65
+ # Moves the given file to a new location
66
+ # @param [String] from_path Absolute path to existing file
67
+ # @param [String] to_path Absolute path to new file
68
+ # @yield [Boolean] True if file was moved without errors, false otherwise
69
+ def self.rename( from_path, to_path )
70
+ !!File.rename( from_path, to_path )
71
+ end
72
+
73
+ # Creates a new directory
74
+ # @param [String] path Absolute path to the new directory
75
+ # @yield [Boolean] True if the new directory was created false in other case
76
+ def self.make_dir( path )
77
+ !!Dir.mkdir( path )
78
+ end
79
+
80
+ # Send a file to the client
81
+ # @param [String] path Absolute path to the file
82
+ # @yield [String] File content or nil if the file does not exist or an error ocurred
83
+ def self.get_file( path )
84
+ File.read( path )
85
+ end
86
+
87
+ # Upload a new file to FTP server
88
+ # @param [String] path Absolute path to final location of the file
89
+ # @param [String] tmp_path Absolute path to temporary file created by server
90
+ # @yield [Fixnum] New uploaded file size or false if there were an error
91
+ def self.put_file( path, tmp_path )
92
+ FileUtils.copy( tmp_path, path )
93
+ File.size( tmp_path )
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end