Skip to content

sam/harbor-ftp

Repository files navigation

Harbor FTP Server

Harbor FTP Server is a wrapper for the Apache FtpServer project. It allows you to run a scriptable FTP server in a way not dissimilar to writing a Harbor (or Rails, or Sinatra) Web Application.

Since it’s JRuby only, the intended deployment scenario would be starting it up in a background thread embedded in your web-application. There’s nothing preventing you from running it stand-alone however (and this is actually a requirement if you’re using a version of Harbor pre 1.0 since the Harbor namespace is a class now, not a module).

Purpose

This project was developed as a replacement for ProFTPd to support three primary goals:

1. Allow you to integrate FTP Logins with your Web Application’s User database (even with passwords encrypted using BCrypt), which ProFTPd’s mod_sql extension does not support
2. Allow Routing of particular request paths in a fashion that should be familiar to users of Ruby MVC Frameworks
3. Allow additional hooking of any FTP Command (such as file-uploads using the STOR command) for additional pre or post processing

To accomplish this we strongly encourage you use Sequel as your O/RM (though Sequel will happily live side-by-side with the other major O/RMs) and PostgreSQL as your database server.

Requirements

  • JRuby 1.7.0.preview1 or above:
    • The Apache FtpServer is a Java library, so JRuby is a hard requirement
    • Ruby 1.9 Syntax is used, including methods introduced in 1.9.3 so this project is not compatible with Ruby 1.8 syntax, or older 1.9 Rubies (MRI < 1.9.3 or JRuby < 1.7) that did not include Module#private_constant. That includes JRuby 1.6.7’s --1.9 mode, which is based on a pre Ruby 1.9.3 compatibility level.

Tested With

  • JRuby 1.7.0.preview1 (installed with rvm install jruby-1.7.0.preview1)
  • H2 and PostgreSQL 9.1.1 was used to develop the Sequel UserManager

Setup

All necessary Java JAR dependencies are included. Just bundle your Ruby dependencies from the Gemfile.

Then just start it up (I’m using RVM to run JRuby-HEAD (1.7-dev) under OpenJDK 7u4 on OSX):

bin/harbor-ftp

Usage

You can run the server stand-alone, configuring any necessary options by embedding it in your own script if need be. ie:

require "harbor/ftp
server = Harbor::FTP::Server.new
server.port = 2121
server.start

The real fun starts when you embed it however. Say you’re writing a Photo sharing app. You want users to be able to log into their account over FTP and upload bucketfuls of photos and you don’t want to have to manage a separate process. If the site is running, the FTP server is running.

In order to start the FTP server, and then your usual web-server in your config.ru you’d write something like this:

require "harbor/ftp"
require "harbor/ftp/user_managers/hash_user_manager"
require "thread"

user_manager = Harbor::FTP::UserManagers::HashUserManager.new
user_manager.add_user "me", "secret"

server = Harbor::FTP::Server.new
server.port = 21
server.user_manager = user_manager

Thread.new { server.start }

run MyAwesomeWebApp

server.stop

And that’s it. You can refer to the examples folder for other examples, and there’s always the test folder for the specs if you have more questions!

Testing

Please keep in mind while reviewing the examples or the Design section below that the tests can all be executed on an individual basis. ie: jruby test/user_adapter_spec.rb.

There is also a continuous-testing tool built in. Just run test/watch.rb and whenever a file (spec or lib) is changed, the appropriate spec will be run. You can run all specs by sending an INT (CTRL+C), or exit by sending it twice.

While we can’t run a Coverage tool under JRuby, we do aim to test most of the library, and provide solid documentation. If you have any questions or find a particular area lacking in documentation please file an issue.

Design

Here we’ll briefly cover the implementation details of the project in case you want to walk through it or contribute.

The project’s main entry point is lib/harbor/ftp.rb. This file requires all the basics for you. Additional files are under the lib/harbor/ftp folder. The Java dependencies are under jars.

In the root directory you’ll see the standard stuff such as Rakefile, Gemfile, harbor-ftp.gemspec, a bin/harbor-ftp to start the server stand-alone, and the test folder.

For testing we’re using Ruby 1.9’s MiniTest::Spec. For database integration tests we’re using Sequel.

The main library is a wrapper around Apache’s FtpServer library. It doesn’t have the prettiest interface, but this project aims to solve that through the use of the Adapter Pattern.

As a simple example, the Apache project defines an org.apache.ftpserver.ftplet.User Interface. If you’re not familiar with Interfaces that’s OK. They just define a “contract” of methods that an object must implement in order to be compatible with some other code. This particular Interface is a bit more complex than we need though. We really only need to implement two methods (#ftp_username and #home_directory).

This is where the Harbor::FTP::UserAdapter comes in. You give it an object that responds to those two methods, and it’ll wrap your object, implementing the rest of the org.apache.ftpserver.ftplet.User Interface for you to maintain compatibility with the Apache FtpServer project.

So if there’s an overall design theme to this project, this is it. Adapters implementing the actual Apache project Interfaces, allowing you to implement a much simpler duck-typed Ruby version.

We define a Harbor::FTP::UserManager module for instance, that Harbor::FTP::UserManagers::HashUserManager implements. Both of these are rather straight-forward and simplistic. The heavy lifting is done for you in Harbor::FTP::ReadonlyUserManagerAdapter. It takes that simplified Harbor::FTP::UserManager Interface, wraps it, and implements all the rest of the technical details for you.

Harbor::FTP::Server then takes the Harbor::FTP::UserManager implementation you assign (under lib/harbor/ftp/user_managers), wraps it in a Harbor::FTP::ReadonlyUserManagerAdapter, and whenever the server is returned a User object by the adapter, it wraps it with a Harbor::FTP::UserAdapter before passing it onto the internals of the Apache FtpServer.

TODO

  • Provide base Ruby implementations as reference for common tasks like integrating with your own user authentication system, or overriding the LIST command, etc
  • Allow hooking COMMAND events for pre/post processing
  • Allow routing COMMANDs to your own “actions”

Contribution Suggestions

These are items I don’t need right now, but you might.

About

An embedded FTP Server for JRuby wrapping Apache's FTPServer libraries

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages