em-ftpd-fsd 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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