/*****************************************************************************\ * Copyright (c) 2002 Pelle Johansson. * * All rights reserved. * * * * This file is part of the moftpd package. Use and distribution of * * this software is governed by the terms in the file LICENCE, which * * should have come with this package. * \*****************************************************************************/ /* $moftpd: commands.c 1251 2005-03-06 22:24:29Z morth $ */ #include "system.h" #include "commands.h" #include "connection.h" #include "main.h" #include "user.h" #include "utf8fs/file.h" #include "defaults.h" #include "utf8fs/memory.h" const command_t commands[] = { {"ABOR", command_abor, 1, 1, ": Abort current action."}, {"ACCT", command_acct, 1, 0, " : Login to specified account."}, {"ADAT", command_adat, 1, 0, " : Authentication data."}, {"ALLO", command_allo, 0, 0, " : Allocate file size (not needed here)."}, {"APPE", command_appe, 0, 0, " : Append to file."}, {"AUTH", command_auth, 1, 0, " : Secure connection."}, {"CCC" , command_ccc , 1, 0, ": Clear command channel."}, {"CDUP", command_cdup, 0, 0, ": Change to parent directory."}, {"CONF", command_conf, 1, 0, " : Send confidential data."}, {"CWD" , command_cwd , 0, 0, " : Change directory."}, {"DELE", command_dele, 0, 0, " : Delete a file."}, {"ENC" , command_enc , 1, 0, " : Send secured confidential data."}, {"EPRT", command_eprt, 0, 0, " : Set port (extended version)."}, {"EPSV", command_epsv, 0, 0, " [ALL]: Set passive mode (extended version)."}, {"FEAT", command_feat, 1, 0, ": List server features."}, {"HELP", command_help, 1, 0, " []: Show help."}, {"HOST", command_host, 1, 0, " []: List or switch host."}, {"LANG", command_lang, 1, 0, " : Switch Language."}, {"LIST", command_list, 0, 0, " [-a] []: List files and directories."}, {"LPRT", command_lprt, 0, 0, " : Set port (long version)."}, {"LPSV", command_lpsv, 0, 0, ": Set passive mode (long version)."}, {"MIC" , command_mic , 1, 0, " : Send secured data."}, {"MKD" , command_mkd , 0, 0, " : Create a directory."}, {"MDTM", command_mdtm, 0, 0, " : Display modification time."}, {"MFMT", command_mfmt, 0, 0, " : Set modification time."}, {"MLST", command_mlst, 0, 0, " : List data about ."}, {"MLSD", command_mlsd, 0, 0, " : List data about contents of ."}, {"MODE", command_mode, 0, 0, " S|B|C: Set mode (only stream is supported)."}, {"NLST", command_nlst, 0, 0, " [-a] []: List file names in ."}, {"NOOP", command_noop, 1, 0, ": No operation."}, {"OPTS", command_opts, 1, 0, " []: Set options."}, {"PASS", command_pass, 1, 0, " : Send password."}, {"QUIT", command_quit, 1, 1, ": Close connection."}, {"PASV", command_pasv, 0, 0, ": Enter passive mode."}, {"PBSZ", command_pbsz, 0, 0, " : Set protection block size."}, {"PORT", command_port, 0, 0, " : Set port."}, {"PROT", command_prot, 0, 0, " C|S|E|P: Set data protection level."}, {"PWD" , command_pwd , 0, 0, ": Display current directory."}, {"REIN", command_rein, 1, 0, ": Reinitialise connection."}, {"REST", command_rest, 0, 0, " : Set restart marker."}, {"RETR", command_retr, 0, 0, " : Retrieve file."}, {"RMD" , command_rmd , 0, 0, " : Delete directory."}, {"RNFR", command_rnfr, 0, 0, " : Rename to something else."}, {"RNTO", command_rnto, 0, 0, " : Rename something to ."}, {"SITE", command_site, 1, 0}, {"SIZE", command_size, 0, 0, " : Display size for current transfer type."}, {"SMNT", command_smnt, 0, 0, " : Mount a different structure."}, {"STAT", command_stat, 0, 1, " []: Display statistics."}, {"STOR", command_stor, 0, 0, " : Store a file."}, {"STOU", command_stou, 0, 0, ": Store a file with an unique name."}, {"STRU", command_stru, 0, 0, " F|R|P: Set structure (only File supported)."}, {"SYST", command_syst, 0, 0, ": Display system type."}, {"TYPE", command_type, 0, 0, " A [N|T|C]|E|I|L []: Set transfer type."}, {"USER", command_user, 1, 0, " : Send user name."}, {"XEND", command_end , 0, 0, " : Set transfer end marker."}, {NULL} }; const int numCommands = sizeof(commands) / sizeof(command_t) - 1; const command_t siteCommands[] = { {"ADMIN", sitecommand_admin, 0, 0, " ...: Administrate server."}, {"CHMOD", sitecommand_chmod, 0, 0, " : Set file permission bits."}, {"HELP" , sitecommand_help , 1, 0, " []: Show help."}, {"FTPD" , sitecommand_ftpd , 1, 0, ": Display info about " PACKAGE_NAME "."}, {NULL} }; const int numSiteCommands = sizeof (siteCommands) / sizeof (command_t) - 1; int handle_command(connection_t *conn, char *line) { const char *arg; const command_t *cmd; int expected; const char *esep, *enext; if(!strlen(line)) return 0; /* Set up uid, gid, faked chroot, etc. */ if(conn->user) user_setup_environ (conn->user, conn->authed, conn->extRFd, conn->extWFd); else drop_privs(); set_locale (conn->currLang); if(conn->spontQuit && !conn->working) { if(conn->spontReply) { send_telnet(conn, conn->spontReply, 1); pfree(conn->spontReply, conn); conn->spontReply = NULL; } disconnected(conn); return 1; } /* * Try to cd back into the directory the connection is currently in. If it * fails, try the root directory before giving up. set_cwd() will make sure * we're not outside a faked chroot. */ if (conn->authed && (!conn->cwd || !set_cwd (conn->cwd, NULL))) { pfree (conn->cwd, conn); conn->cwd = pstring (set_cwd ("/", NULL), conn); if (!conn->cwd) { reply (conn, "421 Root directory inaccessable."); disconnected (conn); return 1; } } arg = strchr(line, ' '); if(arg) *(char*)arg++ = 0; else arg = ""; if(strlen(line) < 3 || strlen(line) > 4) { conn->expect = NULL; reply(conn, "500 Syntax error."); return 0; } // Check for expected commands. expected = 0; for(esep = conn->expect; esep && !expected; esep = enext) { enext = strchr(esep, '|'); if(enext) expected = !strncasecmp(line, esep, enext++ - esep); else expected = !strcasecmp(line, esep); } conn->expect = NULL; for(cmd = commands; cmd->name; cmd++) { if(!strcasecmp(cmd->name, line)) { if(conn->working && !cmd->whileWorking) { /* * rfc0959 is unclear of what to do when receiving commands during a * 1xx command. In one place it says to queue them, but in others it * refers to commands received during transfer. It does however state * that it is illegal for the client to send commands while waiting * for a reply (again, this seems to have some exceptions), so we * mark the commands that are exceptions and deny the rest. * This should really be a 4xx command, but a fitting such does not * exist. */ reply(conn, "500 Please wait until the server is ready."); } else if(!conn->authed && !cmd->unauthed) reply(conn, "503 Please login."); else if(cmd->handler(conn, arg, expected)) return 1; break; } } if(!cmd->name) reply(conn, "500 No such command %s.", line); if(conn->spontQuit && !conn->working) { disconnected(conn); return 1; } return 0; } time_t get_timeval (char *timestr) { char *dot = strchr (timestr, '.'); struct tm tm; int i; if (dot) *dot++ = 0; if (strlen (timestr) != 14) return -1; for (tm.tm_year = i = 0; i < 4; i++) { if (*timestr < '0' || *timestr > '9') return -1; tm.tm_year = tm.tm_year * 10 + *timestr++ - '0'; } tm.tm_year -= 1900; for (tm.tm_mon = i = 0; i < 2; i++) { if (*timestr < '0' || *timestr > '9') return -1; tm.tm_mon = tm.tm_mon * 10 + *timestr++ - '0'; } tm.tm_mon--; for (tm.tm_mday = i = 0; i < 2; i++) { if (*timestr < '0' || *timestr > '9') return -1; tm.tm_mday = tm.tm_mday * 10 + *timestr++ - '0'; } for (tm.tm_hour = i = 0; i < 2; i++) { if (*timestr < '0' || *timestr > '9') return -1; tm.tm_hour = tm.tm_hour * 10 + *timestr++ - '0'; } for (tm.tm_min = i = 0; i < 2; i++) { if (*timestr < '0' || *timestr > '9') return -1; tm.tm_min = tm.tm_min * 10 + *timestr++ - '0'; } for (tm.tm_sec = i = 0; i < 2; i++) { if (*timestr < '0' || *timestr > '9') return -1; tm.tm_sec = tm.tm_sec * 10 + *timestr++ - '0'; } tm.tm_isdst = 0; #ifdef HAVE_STRUCT_TM_TM_ZONE tm.tm_zone = NULL; #endif #ifdef HAVE_STRUCT_TM_TM_GMTOFF tm.tm_gmtoff = 0; #endif #ifdef HAVE_TIMEGM return timegm (&tm); #else return mktime (&tm); #endif }