package Project::Net::FTP::Server; use strict; use DBI; use Net::FTPServer; use Project::Net::FTP::FileHandle; use Project::Net::FTP::DirHandle; use vars qw($VERSION); $VERSION = '0.1 '; use vars qw(@ISA); @ISA = qw(Net::FTPServer); # Cached statement handles. use vars qw($sth1 $sth2 $sth3); # This is called before configuration. sub pre_configuration_hook { my $self = shift; $self->{version_string} .= " Project::Net::FTP/$VERSION"; # Custom SITE commands. $self->{site_command_table}{USAGE} = \&_SITE_USAGE_command; } # This is called just after accepting a new connection. We connect # to the database here. sub post_accept_hook { my $self = shift; # Connect to the database. my $dbh = DBI->connect ("dbi:mysql(RaiseError=>1,AutoCommit=>1):dbname=TEST", "username", "password") or die "cannot connect to database: ftp: $!"; # Store the database handle. $self->{fs_dbh} = $dbh; } # This is called after executing every command. It commits the transaction # into the database. sub post_command_hook { my $self = shift; } # Perform login against the database. sub authentication_hook { my $self = shift; my $user = shift; my $pass = shift; my $user_is_anon = shift; # Disallow anonymous access. return -1 if $user_is_anon; # Verify access against the database. my $sql = "select password from USERS where username = ?"; $sth1 ||= $self->{fs_dbh}->prepare ($sql); $sth1->execute ($user); my $row = $sth1->fetch or return -1; # No such user. # Check password. my $hashed_pass = $row->[0]; return -1 unless crypt ($pass, $hashed_pass) eq $hashed_pass; # Successful login. return 0; } # Called just after user C<$user> has successfully logged in. sub user_login_hook { # Do nothing for now, but in future it would be a good # idea to change uid or chroot to a safe place. } # Return an instance of Net::FTPServer::DBeg1::DirHandle # corresponding to the root directory. sub root_directory_hook { my $self = shift; return new Project::Net::FTP::DirHandle ($self); } # The SITE USAGE command. sub _SITE_USAGE_command { my $self = shift; my $cmd = shift; my $rest = shift; # Count the number of files and directories used. my $sql = "select count(ID) from FILES"; $sth2 ||= $self->{fs_dbh}->prepare ($sql); $sth2->execute; my $row = $sth2->fetch or die "no rows returned from count"; my $nr_files = $row->[0]; $sql = "select count(ID) from DIRECTORIES"; $sth3 ||= $self->{fs_dbh}->prepare ($sql); $sth3->execute; $row = $sth3->fetch or die "no rows returned from count"; my $nr_dirs = $row->[0]; $self->reply (200, "There are $nr_files files and $nr_dirs directories."); } 1; __END__