#define CHUNK_SIZE 8192
int timeout_handle;
string name;
string password;
private mapping cmds;
int connected;
string cwd;
int priv;
int binary;
object connection;
string file_name;
int filesize;
string chunk;
string store_file_name;
int where;
void FTP_connection_wait( void );
void FTP_CMD_list( string str );
void open( void ) {
send_message( "220-GurbaLib FTP daemon v0.01 ready.\n" );
send_message( "220 Use your mud name as login.\n" );
cmds = ([
"user" : "FTP_CMD_user",
"pass" : "FTP_CMD_pass",
"retr" : "FTP_CMD_retr",
"stor" : "FTP_CMD_stor",
"nlst" : "FTP_CMD_nlst",
"list" : "FTP_CMD_list",
"pwd" : "FTP_CMD_pwd",
"cdup" : "FTP_CMD_cdup",
"cwd" : "FTP_CMD_cwd",
"quit" : "FTP_CMD_quit",
"type" : "FTP_CMD_type",
"mkd" : "FTP_CMD_mkd",
"port" : "FTP_CMD_port",
"noop" : "FTP_CMD_noop",
"dele" : "FTP_CMD_dele",
"syst" : "FTP_CMD_syst",
]);
connected = 0;
name = "";
timeout_handle = call_out( "login_timeout", 120 );
}
void close( void ) {
if( connection ) {
destruct_object( connection );
}
destruct_object( this_object() );
}
string query_name( void ) {
return( name );
}
void FTPLOG( string str ) {
write_file( "/logs/ftpd", ctime( time() ) + " : " + str );
}
void FTP_CMD_user( string arg ) {
arg = lowercase(arg);
if(connected) {
send_message("530 User " + arg + " access denied.\n");
return;
}
name = arg;
if(name == "ftp" || name == "anonymous" ) {
send_message("331 Guest login ok, send your complete e-mail address as password.\n");
return;
}
send_message("331 Password required for " + arg + ".\n");
return;
}
void FTP_CMD_pass( string arg ) {
object player;
if( name == "" ) {
send_message( "530 Login with USER first.\n" );
return;
}
if( name == "ftp" || name == "anonymous" ) {
send_message("230 guest login ok, access restrictions apply.\n");
connected = 1;
priv = 0;
cwd = "/pub";
FTPLOG("Anomymous login (" + arg + ")\n" );
return;
}
password = crypt( arg, "fudge" );
player = clone_object( "/std/player" );
player->set_name( name );
player->restore_me();
if( password != player->query_password() ) {
send_message( "530 Login incorrect.\n" );
destruct_object( this_object() );
return;
}
send_message( "230 User " + name + " logged in.\n" );
FTPLOG( name + " logged in.\n" );
connected = 1;
cwd = "/";
priv = SECURE_D->query_priv( name );
}
void FTP_CMD_retr( string str ) {
if( !str ) {
send_message( "550 No file selected.\n" );
destruct_object( connection );
return;
}
str = normalize_path( str, cwd );
if( str == "" ) {
send_message( "550 " + str + ": Permission denied.\n" );
destruct_object( connection );
return;
}
if( file_exists( str ) < 0 ) {
send_message( "550 " + str + ": No such file.\n" );
destruct_object( connection );
return;
}
chunk = read_file( str, 0, CHUNK_SIZE );
filesize = file_size( str );
file_name = str;
if( binary == 0 ) {
where = strlen( chunk );
chunk = implode( explode( chunk, "\n" ), "\r\n" );
send_message("150 Opening ASCII mode data connection for " + str + " (" + filesize + " bytes).\n" );
} else {
where = strlen( chunk );
send_message("150 Opening binary mode data connection for " + str + " (" + filesize + " bytes).\n" );
}
if( filesize < CHUNK_SIZE ) {
connection->set_callback( "FTP_write" );
connection->send_data( chunk );
} else {
connection->set_callback( "FTP_retr" );
connection->send_data( chunk );
}
FTPLOG( name + " GOT " + str + ".\n" );
}
void FTP_CMD_stor( string arg ) {
string path;
string *dirs;
string dir;
if( priv == 0 ) {
send_message( "550 Permission denied.\n" );
return;
}
path = normalize_path( arg, cwd );
if( path == "" ) {
send_message( "550 Permission denied.\n" );
return;
}
store_file_name = path;
dirs = explode( path, "/" );
dir = implode( dirs[..sizeof(dirs)-2], "/" );
if( strlen( dir ) > 2 )
dir = dir[..strlen(dir)-1];
if( file_exists( dir ) != -1 ) {
send_message( "553 No such directory to STOR into. (" + dir + ")\n" );
return;
}
if( file_exists( store_file_name ) )
remove_file( store_file_name );
connection->set_read_callback( "FTP_stor" );
if( binary == 0 ) {
send_message( "150 Opening ASCII mode data connection for "+arg+".\n" );
} else {
send_message( "150 Opening binary mode data connection for "+arg+".\n" );
}
where = 0;
FTPLOG( name + " PUT " + store_file_name + ".\n" );
}
void FTP_CMD_nlst( string str ) {
mixed *files, *objects;
string *names, timestr, size, dirlist;
int *sizes, *times, long, ancient, i, j, sz, max, len, rows, time;
if( str == "-l" ) {
FTP_CMD_list( "./" );
return;
}
if( !str ) {
str = ".";
} else if (sscanf(str, "-%s", str) != 0) {
long = 1;
if (str == "l") {
str = ".";
} else if (sscanf(str, "l %s", str) == 0) {
return;
}
}
str = normalize_path( str, cwd );
if( str == "" ) {
send_message( "550 " + str + ": Permission denied.\n" );
destruct_object( connection );
return;
}
files = get_dir( str );
if( !files ) {
send_message( "550 " + str + ": No such file or directory.\n" );
destruct_object( connection );
return;
}
files = get_dir(str + "/*");
if (!files) {
send_message( "550 " + str + ": Permission denied.\n");
destruct_object( connection );
return;
}
names = files[0];
sz = sizeof(names);
if (sz == 0) {
send_message( "550 No files found.\n" );
destruct_object( connection );
return;
}
sizes = files[1];
times = files[2];
dirlist = "";
for( i = 0; i < sizeof( names ); i++ ) {
dirlist += names[i] + "\r\n";
}
send_message("150 Opening ASCII mode data connection for file list.\n");
connection->set_callback( "FTP_write" );
connection->send_data( dirlist );
}
string FTP_myctime( int nTime ) {
string zDate;
string zWeekday, zMon, zTime, zYear;
string zThis_year;
string zDay;
int nDay;
zDate = ctime( nTime );
sscanf( zDate, "%s %s %d %s %s", zWeekday, zMon, nDay, zTime, zYear );
zThis_year = ctime( time() )[20..];
zDay = "";
zDay += nDay;
if( strlen( zDay ) == 1 ) {
zDay = " " + zDay;
}
if( zYear == zThis_year ) {
return ( zMon + " " + zDay + " " + zTime[0..4] );
} else {
return ( zMon + " " + zDay + " " + zYear );
}
}
void FTP_CMD_list( string str ) {
mixed *files, *objects;
string *names, timestr, size, dirlist;
int *sizes, *times, long, ancient, i, j, sz, max, len, rows, time;
if( !str ) {
str = ".";
} else if (sscanf(str, "-%s", str) != 0) {
long = 1;
if (str == "l") {
str = ".";
} else if (sscanf(str, "l %s", str) == 0) {
return;
}
}
str = normalize_path( str, cwd );
if( str == "" ) {
send_message( "550 " + str + ": Permission denied.\n" );
destruct_object( connection );
return;
}
files = get_dir( str );
if( !files ) {
send_message( "550 " + str + ": No such file or directory.\n" );
destruct_object( connection );
return;
}
files = get_dir(str + "/*");
if (!files) {
send_message( "550 " + str + ": Permission denied.\n");
destruct_object( connection );
return;
}
names = files[0];
sz = sizeof(names);
if (sz == 0) {
send_message( "550 No files found.\n" );
destruct_object( connection );
return;
}
sizes = files[1];
times = files[2];
dirlist = "";
for( i = 0; i < sizeof( names ); i++ ) {
if( sizes[i] < 0 ) {
/* We're dealing with a directory */
dirlist += "drwxr-xr-x 1 gurba gurba 1024 " + FTP_myctime( times[i]) + " " + names[i] + "\r\n";
} else {
/* We're dealing with a file */
dirlist += "-rw-r--r-- 1 gurba gurba ";
size = " " + sizes[i];
size = size[strlen(size)-11.. ];
dirlist += size + " " + FTP_myctime( times[i]) + " " + names[i] + "\r\n";
}
}
send_message("150 Opening ASCII mode data connection for /bin/ls.\n");
connection->set_callback( "FTP_write" );
connection->send_data( dirlist );
}
void FTP_connection_wait( void ) {
if( connection->is_connected() == 0 ) {
call_out( "FTP_connection_wait", 1 );
return;
}
send_message( "200 PORT command successful.\n" );
}
void FTP_CMD_pwd( string arg ) {
send_message( "257 \"" + cwd + "\" is current directory.\n" );
}
void FTP_CMD_cwd( string arg ) {
arg = normalize_path( arg, cwd );
if( arg == "" ) {
send_message( "550 " + arg + ": Permission denied.\n" );
return;
}
if( strlen(arg) > 1 && arg[strlen(arg)-1] == '/' ) {
arg = arg[..strlen(arg)-2];
}
if( file_exists( arg ) != -1 ) {
send_message( "550 " + arg + ": No such file or directory.\n" );
return;
}
cwd = arg;
send_message("250 CWD command successful.\n");
}
void FTP_CMD_cdup( string arg ) {
FTP_CMD_cwd( ".." );
}
void FTP_CMD_quit( string arg ) {
send_message( "221 Goodbye.\n" );
FTPLOG( name + " quit.\n" );
}
void FTP_CMD_type( string arg ) {
switch(arg) {
case "a":
case "A":
binary = 0;
send_message("200 Type set to A.\n");
return;
case "i":
case "I":
binary = 1;
send_message("200 Type set to I.\n");
return;
default:
send_message("550 Unknown file type.\n");
return;
}
}
void FTP_CMD_mkd( string arg ) {
string file;
file = normalize_path( arg, cwd );
if( file == "" || priv == 0) {
send_message( "550 Permission denied.\n" );
return;
}
if( file_exists( file ) == 0 ) {
if( make_dir( file ) == 0 ) {
send_message( "550 Unable to create directory.\n" );
return;
} else {
send_message( "257 MKD command successful.\n" );
return;
}
} else {
send_message( "550 File or dir already exists.\n" );
}
}
void FTP_CMD_port( string arg ) {
string *tmp;
string ip;
int port;
tmp = explode( arg, "," );
if( sizeof( tmp ) != 6 ) {
send_message( "500 PORT " + arg + " not understood.\n" );
return;
}
ip = implode( tmp[0..3], "." );
port = ( str2val( tmp[4] ) << 8 ) + ( str2val( tmp[5] ) );
if( connection != 0 ) {
destruct_object( connection );
}
connection = clone_object( "/kernel/net/ftp_conn" );
connection->start_connection( ip, port, binary );
FTP_connection_wait();
}
void FTP_CMD_noop( string arg ) {
}
void FTP_CMD_dele( string arg ) {
string file;
file = normalize_path( arg, cwd );
if( file == "" || priv == 0) {
send_message( "550 " + arg + ": Permission denied.\n" );
return;
}
if( file_exists( file ) == -1 ) {
if( remove_dir( file ) == 0 ) {
send_message( "550 " + arg + ": Not empty.\n" );
} else {
send_message( "250 DELE command successful.\n" );
}
} else if( file_exists( file ) != 0 ) {
if( remove_file( file ) == 0 ) {
send_message( "550 " + arg + ": Unable to DELE.\n" );
} else {
send_message( "250 DELE command successful.\n" );
}
} else {
send_message( "550 " + arg + ": Not found.\n" );
}
}
void FTP_CMD_syst( string arg ) {
send_message( "215 UNIX Mud name: Gurba\n" );
}
void FTP_write( void ) {
send_message( "226 Transfer complete.\n" );
destruct_object( connection );
}
void FTP_retr( void ) {
string *converted;
if( where < filesize ) {
chunk = read_file( file_name, where, CHUNK_SIZE );
}
if( binary == 0 && chunk != "" ) {
if( chunk[strlen(chunk)-1] == '\n' )
chunk += " ";
if( chunk[0] == '\n' )
chunk = " " + chunk;
converted = explode( chunk, "\n" );
chunk = implode( converted, "\r\n" );
}
if( (where + CHUNK_SIZE) > filesize ) {
connection->set_callback( "FTP_write" );
}
connection->send_data( chunk );
where += CHUNK_SIZE;
}
void FTP_stor( string str ) {
string *lines;
if( binary == 0 ) {
lines = explode( str, "\r" );
str = implode( lines, "" );
}
write_file( store_file_name, str );
}
void receive_message( string message ) {
string cmd, arg;
string func;
/* FTPLOG( "Got: \"" + message + "\"\n" ); */
if( message != "" && strlen(message) >= 2 ) {
if( message[strlen(message)-1] == '\n' ) {
message = message[..strlen(message)-2];
}
}
if( message != "" && strlen(message) >= 2 ) {
if( message[strlen(message)-1] == '\r' ) {
message = message[..strlen(message)-2];
}
}
arg = "";
if( sscanf( message, "%s %s", cmd, arg ) != 2 ) {
cmd = message;
}
cmd = lowercase( cmd );
if( connected == 0 ) {
/* Only allow these commands if not connected */
switch( cmd ) {
case "user" :
FTP_CMD_user( arg );
return;
case "pass":
FTP_CMD_pass( arg );
return;
case "quit":
FTP_CMD_quit( arg );
return;
case "noop":
FTP_CMD_noop( arg );
return;
default:
send_message("503 Log in with USER first.\n");
return;
}
}
func = cmds[cmd];
if (!func)
{
/* Log command so we know what clients are trying to use */
send_message( "502 Unknown command " + cmd + "\n" );
return;
}
if((call_other(this_object(), func, arg))) {
FTPLOG(name + " caused a FAILURE with command '" + message + "'.\n" );
send_message("550 Unknown failure. Please report what you were doing to the mud admin.\n");
}
return;
}
void login_timeout( void ) {
if( !connected ) {
send_message( "220 Timed out.\n" );
destruct_object( this_object() );
}
}