WatersOfOblivion-dyn-ftp-serv 0.0.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/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.0 2009-06-18
2
+
3
+ * 1 major enhancement:
4
+ * Lifted code from http://dyn-ftp-serv.rubyforge.org/
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ lib/dyn-ftp-serv.rb
6
+ examples/ftpserv.rb
7
+ script/console
8
+ script/destroy
9
+ script/generate
10
+ spec/dyn-ftp-serv_spec.rb
11
+ spec/spec.opts
12
+ spec/spec_helper.rb
13
+ tasks/rspec.rake
data/README.rdoc ADDED
@@ -0,0 +1,48 @@
1
+ = dyn-ftp-serv
2
+
3
+ * http://github.com/WatersOfOblivion/dyn-ftp-serv
4
+
5
+ == DESCRIPTION:
6
+
7
+ DynFtpServ is a Dynamic FTP Server. I stole the original code from [here](http://dyn-ftp-serv.rubyforce.org/)
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * Nothing, yet
12
+
13
+ == SYNOPSIS:
14
+
15
+ TODO
16
+
17
+ == REQUIREMENTS:
18
+
19
+ * None, yet
20
+
21
+ == INSTALL:
22
+
23
+ * sudo gem install WatersOfOblivion-dyn-ftp-serv -s http://gems.github.com
24
+
25
+ == LICENSE:
26
+
27
+ (The MIT License)
28
+
29
+ Copyright (c) 2009 Jonathan Bryant
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
2
+ %w[rake rake/clean fileutils newgem rubigen].each { |f| require f }
3
+ require File.dirname(__FILE__) + '/lib/dyn-ftp-serv'
4
+
5
+ # Generate all the Rake tasks
6
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
7
+ $hoe = Hoe.new('dyn-ftp-serv', DynFtpServ::VERSION) do |p|
8
+ p.developer('Jonathan Bryant', 'jonathan@watersofoblivion.com')
9
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
10
+ p.rubyforge_name = p.name # TODO this is default value
11
+ # p.extra_deps = [
12
+ # ['activesupport','>= 2.0.2'],
13
+ # ]
14
+ p.extra_dev_deps = [
15
+ ['newgem', ">= #{::Newgem::VERSION}"]
16
+ ]
17
+
18
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
19
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
20
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
21
+ p.rsync_args = '-av --delete --ignore-errors'
22
+ end
23
+
24
+ require 'newgem/tasks' # load /tasks/*.rake
25
+ Dir['tasks/**/*.rake'].each { |t| load t }
26
+
27
+ # TODO - want other tests/tasks run by default? Add them to the list
28
+ task :default => :spec
@@ -0,0 +1,79 @@
1
+ require '../dynftp_server'
2
+ require 'logger'
3
+
4
+ Thread.abort_on_exception = true
5
+
6
+ class FSProvider
7
+ attr_reader :ftp_name, :ftp_size, :ftp_dir, :ftp_date
8
+
9
+ def ftp_parent
10
+ path = @path.split('/')
11
+ return nil unless path.pop
12
+ return nil if path.size <= 1
13
+ return FSProvider.new(path.join('/'))
14
+ end
15
+
16
+ def ftp_list
17
+ output = Array.new
18
+ Dir.entries(@path).sort.each do |file|
19
+ output << FSProvider.new(@path + (@path == '/'? '': '/') + file)
20
+ end
21
+ return output
22
+ end
23
+
24
+ def ftp_create(name, dir = false)
25
+ if dir
26
+ begin
27
+ Dir.mkdir(@path + '/' + name)
28
+ return FSProvider.new(@path + '/' + name)
29
+ rescue
30
+ return false
31
+ end
32
+ else
33
+ FSProvider.new(@path + '/' + name)
34
+ end
35
+
36
+ end
37
+
38
+ def ftp_retrieve(output)
39
+ output << File.new(@path, 'r').read
40
+ end
41
+
42
+ def ftp_store(input)
43
+ return false unless File.open(@path, 'w') do |f|
44
+ f.write input.read
45
+ end
46
+ @ftp_size = File.size?(@path)
47
+ @ftp_date = File.mtime(@path) if File.exists?(@path)
48
+ end
49
+
50
+ def ftp_delete()
51
+ return false
52
+ end
53
+
54
+ def initialize(path)
55
+ @path = path
56
+ @ftp_name = path.split('/').last
57
+ @ftp_name = '/' unless @ftp_name
58
+ @ftp_dir = File.directory?(path)
59
+ @ftp_size = File.size?(path)
60
+ @ftp_size = 0 unless @ftp_size
61
+ @ftp_date = Time.now
62
+ @ftp_date = File.mtime(path) if File.exists?(path)
63
+ end
64
+
65
+ end
66
+
67
+
68
+ log = Logger.new(STDOUT)
69
+ log.datetime_format = "%H:%M:%S"
70
+ log.progname = "ftpserv.rb"
71
+
72
+ root = FSProvider.new('/')
73
+ auth =
74
+ lambda do |user,pass|
75
+ return false unless user.casecmp('anonymous') == 0
76
+ return true
77
+ end
78
+ s = DynFTPServer.new(:port => 21, :root => root, :authentication => auth, :logger => log)
79
+ s.mainloop
@@ -0,0 +1,450 @@
1
+ # :title:Dynamic FTP server in pure Ruby (dyn-ftp-serv)
2
+ # Version:: 0.1.2
3
+ # Author:: Rubtsov Vitaly (vlrubtsov *at* gmail.com)
4
+ # License:: MIT license
5
+ # Website:: http://rubyforge.org/projects/dyn-ftp-serv/
6
+ #
7
+ # This ftp server implementation features an ability to host any content you want.
8
+ # You are not limited to hosting files and directories via FTP interface.
9
+ # With dyn-ftp-serv you are able to represent any hierarchy under the guise of
10
+ # standart files and directories. You will be able to download and upload files
11
+ # and browse dynamic directories.
12
+ # To work with dyn-ftp-serv you must have an object responding to special ftp messages
13
+ # that will represent the ftp content. You can create a new object or extend the
14
+ # existing one with special messages.
15
+ # There are two sets of messages to be handled: directory messages and file messages.
16
+ # Directory messages are:
17
+ # [+ftp_dir+] must return true.
18
+ # [+ftp_name+] must return the name of a directory
19
+ # [+ftp_size+] must return size for directory
20
+ # [+ftp_date+] must return the date for a directory
21
+ # [+ftp_parent+] must return parent object or nil if root
22
+ # [+ftp_list+] must return an array of ftp objects
23
+ # [<tt>ftp_create(name, dir = false)</tt>]
24
+ # must return a new object created with the 'name' given.
25
+ # It can be file (dir=false) or a directory (dir=true). It can return nil if creating is
26
+ # forbidden.
27
+ # [+ftp_delete+] directory deletion request. must return true on success, and false on failure.
28
+ # File messages are:
29
+ # [+ftp_dir+] must return false
30
+ # [+ftp_name+] must return the name of a file
31
+ # [+ftp_size+] must return filesize
32
+ # [+ftp_date+] must return filedate
33
+ # [<tt>ftp_retrieve(output)</tt>] streams file contents via output socket.
34
+ # [<tt>ftp_store(input)</tt>] writes file contents reading from a socket
35
+ # [+ftp_delete+] file deletion request. must return true on success, and false on failure.
36
+ #
37
+ # Please, see an example in 'examples' folder showing an implementation of standard file system
38
+ # ftp server.
39
+
40
+ require 'socket'
41
+
42
+ module DynFtpServ
43
+
44
+ VERSION = '0.0.0'
45
+
46
+ class DynFTPServer
47
+
48
+ # Class to instantiate if logger is not given.
49
+ class DummyLogger
50
+ def method_missing(method_name, *args, &block); end
51
+ end
52
+
53
+ # Pass a hash containing options.
54
+ # [<tt>:host</tt>] Local bind address. Default is <em>'0.0.0.0'</em>.
55
+ # [<tt>:port</tt>] Port to listen. Default is <em>21</em>.
56
+ # [<tt>:masquerade_ip</tt>] IP masquerade for passive connections. Use this settings if you are behind a firewall and set it to the external ip address.
57
+ # [<tt>:pasv_min_port</tt>] Minimum port num for passive connections.
58
+ # [<tt>:pasv_max_port</tt>] Maximum port num for passive connections.
59
+ # [<tt>:root</tt>] Root ftp object.
60
+ # [<tt>:authentication</tt>] Function used to check users login information.
61
+ # [<tt>:logger</tt>] Logger object.
62
+ def initialize(conf)
63
+ @config = {
64
+ :host => '',
65
+ :port => 21,
66
+ :masquerade_ip => nil,
67
+ :pasv_min_port => 1024,
68
+ :pasv_max_port => 65535,
69
+ :root => nil,
70
+ :authentication => lambda {|user,pass| return true; },
71
+ :logger => nil}.merge(conf)
72
+ raise(ArgumentError, "Root object must not be null.") unless @config[:root]
73
+ @server = TCPServer.new(@config[:host], @config[:port])
74
+ end
75
+
76
+ # Starts processing incoming connections
77
+ def mainloop
78
+ threads = []
79
+ log.debug "Waiting for connection"
80
+ while (session = @server.accept)
81
+ log.debug "Accepted connection from #{session.addr.join(', ')}"
82
+ threads << Thread.new(session) do |s|
83
+ thread[:socket] = s
84
+ client_loop
85
+ end
86
+ end
87
+ threads.each {|t| t.join }
88
+ end
89
+
90
+ private
91
+
92
+ # Returns logger
93
+ def log
94
+ return @config[:logger] if @config[:logger]
95
+ return DummyLogger.new
96
+ end
97
+
98
+ def not_implemented
99
+ status(500);
100
+ end
101
+
102
+ def not_authorized
103
+ status 530
104
+ end
105
+
106
+ def status(code, descr = nil)
107
+ unless (descr.nil?)
108
+ log.debug "Response: " + code.to_s + ' ' + descr
109
+ thread[:socket].puts code.to_s + ' ' + descr + "\r\n"
110
+ return
111
+ end
112
+ case (code.to_i)
113
+ when 125
114
+ status(code, 'Data connection already open; transfer starting.')
115
+ when 150
116
+ status(code, 'File status okay; about to open data connection.')
117
+ when 200
118
+ status(code, 'Command okey.')
119
+ when 226
120
+ status(code, 'Closing data connection.')
121
+ when 230
122
+ status(code, 'User logged in, proceed.')
123
+ when 250
124
+ status(code, 'Requested file action okay, completed.')
125
+ when 331
126
+ status(code, 'User name okay, need password.')
127
+ when 425
128
+ status(code, "Can't open data connection.")
129
+ when 500
130
+ status(code, 'Syntax error, command unrecognized.')
131
+ when 502
132
+ status(code, 'Command not implemented.')
133
+ when 530
134
+ status(code, 'Not logged in.')
135
+ when 550
136
+ status(code, 'Requested action not taken.')
137
+ else
138
+ status(code, '')
139
+ end
140
+ end
141
+
142
+ def data_connection(&block)
143
+ client_socket = nil
144
+ if (thread[:passive])
145
+ unless (IO.select([thread[:data_socket]], nil, nil, 60000))
146
+ status 425
147
+ return false
148
+ end
149
+ client_socket = thread[:data_socket].accept
150
+ status 150
151
+ else
152
+ client_socket = thread[:data_socket]
153
+ status 125
154
+ end
155
+ yield(client_socket)
156
+ return true
157
+ ensure
158
+ client_socket.close if client_socket && thread[:passive]
159
+ client_socket = nil
160
+ end
161
+
162
+ def passive_server
163
+ server = nil
164
+ port = @config[:pasv_min_port]
165
+ while (server.nil?) and (port <= @config[:pasv_max_port])
166
+ begin
167
+ server = TCPServer.new(@config[:host], port)
168
+ rescue Errno::EADDRINUSE
169
+ log.error "#{port} is already in use. Trying next port."
170
+ end
171
+ port += 1
172
+ end
173
+ server
174
+ end
175
+
176
+ def open_object(path)
177
+ if (path[0,1] == '/') || (path.is_a?(Array) && (path[0] == ''))
178
+ dir = @config[:root]
179
+ else
180
+ dir = thread[:cwd]
181
+ end
182
+ path = path.split('/') unless path.is_a?(Array)
183
+ return dir if path.empty?
184
+ last_element = path.pop
185
+ path.each do |p|
186
+ unless p == ''
187
+ dir = dir.ftp_list.detect {|d| (d.ftp_name.casecmp(p) == 0) && (d.ftp_dir) }
188
+ return nil unless dir
189
+ end
190
+ end
191
+ dir = dir.ftp_list.detect {|d| (d.ftp_name.casecmp(last_element) == 0) } unless last_element == ''
192
+ return dir
193
+ end
194
+
195
+ def open_path(path)
196
+ result = open_object(path)
197
+ result = nil if result && !result.ftp_dir
198
+ return result
199
+ end
200
+
201
+ def open_file(path)
202
+ result = open_object(path)
203
+ result = nil if result && result.ftp_dir
204
+ return result
205
+ end
206
+
207
+ def get_path(object)
208
+ return '/' unless object
209
+ return '/' if object == @config[:root]
210
+ result = ''
211
+ while object do
212
+ result = '/' + object.ftp_name + result
213
+ object = object.ftp_parent
214
+ end
215
+ return result
216
+ end
217
+
218
+ def get_quoted_path(object)
219
+ get_path(object).gsub('"', '""')
220
+ end
221
+
222
+ def thread
223
+ Thread.current
224
+ end
225
+
226
+ # Commands
227
+
228
+ def cmd_cdup(params)
229
+ thread[:cwd] = thread[:cwd].ftp_parent
230
+ thread[:cwd] = @config[:root] unless thread[:cwd]
231
+ status(250, 'Directory successfully changed.')
232
+ end
233
+
234
+ def cmd_cwd(path)
235
+ if (newpath = open_path(path))
236
+ thread[:cwd] = newpath
237
+ status(250, 'Directory successfully changed.')
238
+ else
239
+ status(550, 'Failed to change directory.')
240
+ end
241
+ end
242
+
243
+ def cmd_dele(path)
244
+ if (file = open_file(path)) && file.ftp_delete
245
+ status 250
246
+ else
247
+ status(550, 'Delete operation failed.')
248
+ end
249
+ end
250
+
251
+ # def cmd_feat(params)
252
+ # thread[:socket].puts "211-Features\r\n"
253
+ # thread[:socket].puts " UTF8\r\n"
254
+ # thread[:socket].puts "211 end\r\n"
255
+ # end
256
+
257
+ def cmd_list(params)
258
+ data_connection do |data_socket|
259
+ list = thread[:cwd].ftp_list
260
+ list.each {|file| data_socket.puts((file.ftp_dir ? 'd': '-') + 'rw-rw-rw- 1 ftp ftp ' + file.ftp_size.to_s + ' ' + file.ftp_date.strftime('%b %d %H:%M') + ' ' + file.ftp_name + "\r\n") }
261
+ end
262
+ thread[:data_socket].close if thread[:data_socket]
263
+ thread[:data_socket] = nil
264
+
265
+ status 226, "Transfer complete"
266
+ end
267
+
268
+ def cmd_mdtm(path)
269
+ if (file = open_file(path))
270
+ status 213, file.ftp_date.strftime('%Y%m%d%H%M%S')
271
+ else
272
+ status(550, 'Could not get modification time.')
273
+ end
274
+ end
275
+
276
+ def cmd_mkd(path)
277
+ dir = open_object(path)
278
+ if (dir)
279
+ status 521, "Directory already exists"
280
+ return
281
+ end
282
+ splitted_path = path.split('/')
283
+ mkdir = splitted_path.pop
284
+ dir = open_path(splitted_path)
285
+ if dir && (newone = dir.ftp_create(mkdir, true))
286
+ status 257, '"'+get_quoted_path(newone)+'" directory created.'
287
+ else
288
+ status 550
289
+ end
290
+ end
291
+
292
+ def cmd_pass(pass)
293
+ thread[:pass] = pass
294
+ if @config[:authentication].call(thread[:user], thread[:pass])
295
+ thread[:authenticated] = true
296
+ status 230
297
+ else
298
+ thread[:authenticated] = false
299
+ not_authorized
300
+ end
301
+ end
302
+
303
+ def cmd_pasv(params)
304
+ if thread[:data_socket]
305
+ thread[:data_socket].close
306
+ thread[:data_socket] = nil
307
+ end
308
+ thread[:data_socket] = passive_server
309
+ return status(425) if thread[:data_socket].nil?
310
+ thread[:passive] = true
311
+ port = thread[:data_socket].addr[1]
312
+ port_lo = port & "0x00FF".hex
313
+ port_hi = port >> 8
314
+ ip = thread[:data_socket].addr[3]
315
+ ip = @config[:masquerade_ip] if @config[:masquerade_ip]
316
+ ip = ip.split('.')
317
+ status 227, "Entering Passive Mode (#{ip[0]},#{ip[1]},#{ip[2]},#{ip[3]},#{port_hi},#{port_lo})"
318
+ end
319
+
320
+ def cmd_port(ip_port)
321
+ s = ip_port.split(',')
322
+ port = s[4].to_i * 256 + s[5].to_i
323
+ host = s[0..3].join('.')
324
+ if thread[:data_socket]
325
+ thread[:data_socket].close
326
+ thread[:data_socket] = nil
327
+ end
328
+ thread[:data_socket] = TCPSocket.new(host, port)
329
+ thread[:passive] = false
330
+ status 200, "Passive connection established (#{port})"
331
+ end
332
+
333
+ def cmd_pwd(params)
334
+ status 257, "\"#{get_quoted_path(thread[:cwd])}\" is the current directory"
335
+ end
336
+
337
+ def cmd_rmd(path)
338
+ if (dir = open_path(path)) && dir.ftp_delete
339
+ status 250
340
+ else
341
+ status(550, 'Remove directory operation failed.')
342
+ end
343
+ end
344
+
345
+ def cmd_quit(params)
346
+ status(200)
347
+ thread[:socket].close
348
+ thread[:socket] = nil
349
+ end
350
+
351
+ def cmd_retr(path)
352
+ if (file = open_file(path))
353
+ data_connection do |data_socket|
354
+ if file.ftp_retrieve(data_socket)
355
+ status 226, 'Transfer complete'
356
+ else
357
+ status(550, 'Failed to open file.')
358
+ end
359
+ end
360
+ else
361
+ status(550, 'Failed to open file.')
362
+ end
363
+
364
+ thread[:data_socket].close if thread[:data_socket]
365
+ thread[:data_socket] = nil
366
+ end
367
+
368
+ def cmd_size(path)
369
+ if (file = open_file(path))
370
+ status 213, file.ftp_size.to_s
371
+ else
372
+ status(550, 'Could not get file size.')
373
+ end
374
+ end
375
+
376
+ def cmd_stor(path)
377
+ file = open_file(path)
378
+ if file
379
+ status 553, 'Could not create file.'
380
+ return
381
+ end
382
+ unless file
383
+ splitted_path = path.split('/')
384
+ filename = splitted_path.pop
385
+ dir = open_path(splitted_path)
386
+ file = dir.ftp_create(filename) if dir
387
+ end
388
+ if file
389
+ data_connection do |data_socket|
390
+ file.ftp_store(data_socket)
391
+ end
392
+ status 226, 'Transfer complete'
393
+ else
394
+ status 550, 'Failed to open file.'
395
+ end
396
+
397
+ thread[:data_socket].close if thread[:data_socket]
398
+ thread[:data_socket] = nil
399
+ end
400
+
401
+ def cmd_syst(params)
402
+ status(215, 'UNIX')
403
+ end
404
+
405
+ def cmd_type(type)
406
+ status 200, "Type set."
407
+ end
408
+
409
+ def cmd_user(user)
410
+ thread[:user] = user
411
+ status(331)
412
+ end
413
+
414
+ def welcome
415
+ thread[:authenticated] = false
416
+ thread[:cwd] = @config[:root]
417
+ status(220, "Microsoft FTP Server ready")
418
+ end
419
+
420
+ def client_loop
421
+ welcome
422
+ while (thread[:socket] && (s = thread[:socket].gets))
423
+ s.chomp!
424
+ log.debug "Request: #{s}"
425
+ params = s.split(' ', 2)
426
+ command = params.first
427
+ command.downcase! if command
428
+ m = 'cmd_'+command.to_s
429
+ if self.respond_to?(m, true)
430
+ if (['cmd_user', 'cmd_pass'].include?(m)) or (thread[:authenticated])
431
+ self.send(m, params[1])
432
+ else
433
+ not_authorized
434
+ end
435
+ else
436
+ not_implemented
437
+ end
438
+ end
439
+ #rescue
440
+ # log.error $!
441
+ ensure
442
+ thread[:socket].close if thread[:socket] and not thread[:socket].closed?
443
+ thread[:socket] = nil
444
+ thread[:data_socket].close if thread[:data_socket] and not thread[:data_socket].closed?
445
+ thread[:data_socket] = nil
446
+ end
447
+
448
+ end
449
+
450
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/dyn-ftp-serv.rb'}"
9
+ puts "Loading dyn-ftp-serv gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ # Time to add your specs!
4
+ # http://rspec.info/
5
+ describe "Place your specs here" do
6
+
7
+ it "find this spec in spec directory" do
8
+ # violated "Be sure to write your specs"
9
+ end
10
+
11
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'dyn-ftp-serv'
data/tasks/rspec.rake ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ require 'spec'
6
+ end
7
+ begin
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ desc "Run the specs under spec/models"
18
+ Spec::Rake::SpecTask.new do |t|
19
+ t.spec_opts = ['--options', "spec/spec.opts"]
20
+ t.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: WatersOfOblivion-dyn-ftp-serv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Bryant
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-18 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: newgem
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.4.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.0
34
+ version:
35
+ description: DynFtpServ is a Dynamic FTP Server. I stole the original code from [here](http://dyn-ftp-serv.rubyforce.org/)
36
+ email:
37
+ - jonathan@watersofoblivion.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - History.txt
44
+ - Manifest.txt
45
+ - README.rdoc
46
+ files:
47
+ - History.txt
48
+ - Manifest.txt
49
+ - README.rdoc
50
+ - Rakefile
51
+ - lib/dyn-ftp-serv.rb
52
+ - examples/ftpserv.rb
53
+ - script/console
54
+ - script/destroy
55
+ - script/generate
56
+ - spec/dyn-ftp-serv_spec.rb
57
+ - spec/spec.opts
58
+ - spec/spec_helper.rb
59
+ - tasks/rspec.rake
60
+ has_rdoc: true
61
+ homepage: http://github.com/WatersOfOblivion/dyn-ftp-serv
62
+ post_install_message:
63
+ rdoc_options:
64
+ - --main
65
+ - README.rdoc
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ requirements: []
81
+
82
+ rubyforge_project: dyn-ftp-serv
83
+ rubygems_version: 1.2.0
84
+ signing_key:
85
+ specification_version: 2
86
+ summary: DynFtpServ is a Dynamic FTP Server
87
+ test_files: []
88
+