📄 cmds.c
字号:
(htons(addr.sin_port) & 0xff00) >> 8, (htons(addr.sin_port) & 0x00ff)); return 1;}/* * cmd_pwd(): Handles PWD command (print working directory). * * Note that if somebody contacts you with the message `the server * says curr_dir() is outside root_dir()', you should fix your * /betaftpd.users file, if you use nonroot. If not, it's a bug. * Try to get it _reproducible_, and mail it to me. */int cmd_pwd(struct conn * const c){ char temp[512], *cdir = NULL; cdir = do_pwd(c, temp, c->curr_dir); if (cdir != NULL) { numeric(c, 257, "\"%s\" is current working directory.", cdir); } return 1;}/* * do_pwd(): Translates an absolute path to a path suitable for viewing * to the user (ie. removes the root_dir, and removes a trailing * slash if it exists). Note that the retbuf is only used as a * storage place -- the pointer to the right place within retbuf * is _returned_. */char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir) { char *cdir = NULL; strcpy(retbuf, dir); if (strncmp(retbuf, c->root_dir, strlen(c->root_dir)) != 0) { numeric(c, 550, "curr_dir is outside root_dir, please contact site administrator."); return NULL; } cdir = retbuf + strlen(c->root_dir) - 1; if (cdir[strlen(cdir) - 1] == '/' && strlen(cdir) > 1) { cdir[strlen(cdir) - 1] = 0; } else if (strlen(cdir) == 0) { strcpy(cdir, "/"); } return cdir;}/* * cmd_cwd(): Handles CWD command (change working directory). Uses * cmd_cwd_internal() (see below). */int cmd_cwd(struct conn * const c){ cmd_cwd_internal(c, c->recv_buf); return 1;}/* * cmd_cdup(): Handles a CDUP command (identical to `CWD ..'). Note that * RFC959 gives two different response codes (250 and 200) -- * 250 is the same as CWD gives, which sounds logical to me. * wu-ftpd uses it as well. * * Note that using a CDUP to try to get outside root_dir returns * an error, instead of just staying in the root directory (as * the OS and thus wu-ftpd does). */int cmd_cdup(struct conn * const c){ cmd_cwd_internal(c, ".."); return 1;}/* * cmd_cwd_internal(): * Does the work for CWD and CDUP (modularized to save some * space and have clearer code). Mostly, it just uses do_chdir(), * and sees where that takes us. It adds a trailing slash if needed. */void cmd_cwd_internal(struct conn * const c, const char * const newd){ if (do_chdir(c, newd) != -1) { int i; getcwd(c->curr_dir, 254); i = strlen(c->curr_dir); if (c->curr_dir[i - 1] != '/') { c->curr_dir[i++] = '/'; c->curr_dir[i] = '\0'; }#if WANT_MESSAGE dump_file(c, 250, ".message"); list_readmes(c);#endif numeric(c, 250, "CWD successful."); }}/* * cmd_rest(): Handles the REST command. All it does is tell the file * sending functions to start at the correct number. We should * perhaps add some better error checking to this? */int cmd_rest(struct conn * const c){ c->rest_pos = abs(atoi(c->recv_buf)); numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos); return 1;}/* * cmd_retr(): Handles the RETR command. This command doesn't send the * file, but it opens it and tells the socket handling code * to check for activity on the data socket. When the * connection occurs (or succeeds, if we're using PORT mode), * the actual file transfer begins. */int cmd_retr(struct conn * const c){ struct ftran *f = c->transfer; if ((f == NULL) || ((f->state != 1) && (f->state != 3))) { numeric(c, 425, "No data connection set up; please use PASV or PORT."); return 1; }#if WANT_ASCII if ((c->rest_pos > 0) && (c->ascii_mode == 1)) { numeric(c, 500, "Cannot resume while in ASCII mode."); return 1; }#endif f->local_file = do_openfile(c, c->recv_buf, f->filename, O_RDONLY#if WANT_NONROOT , 4#endif ); f->dir_listing = 0; if (f->local_file == -1) { numeric(f->owner, 550, strerror(errno)); destroy_ftran(f); } else if (f->local_file == -2) { f->local_file = -1; destroy_ftran(f); } else {#if WANT_UPLOAD f->upload = 0;#endif prepare_for_transfer(f); } return 1;}#if WANT_UPLOAD/* * cmd_stor(): Handles the STOR command (upload file). Pushes the * work down to do_store(), below. */int cmd_stor(struct conn * const c){ do_store(c, 0); return 1;}/* * cmd_appe(): Handles the APPE command (append to file). Pushes * the work down to do_store(), below. */int cmd_appe(struct conn * const c){ do_store(c, 1); return 1;}/* * do_store(): Initiate an upload. Most of the comments to do_retr() * (above) apply to this one as well. */void do_store(struct conn * const c, const int append){ struct ftran *f = c->transfer; if ((f == NULL) || ((f->state != 1) && (f->state != 3))) { numeric(c, 425, "No data connection set up; please use PASV or PORT."); return; }#if WANT_ASCII if ((c->rest_pos > 0) && (c->ascii_mode == 1)) { numeric(c, 500, "Cannot resume while in ASCII mode."); return; }#endif f->local_file = do_openfile(c, c->recv_buf, f->filename, O_WRONLY | O_CREAT | ((append || c->rest_pos > 0) ? 0 : O_TRUNC)#if WANT_NONROOT , 2#endif ); f->dir_listing = 0; if (f->local_file == -1) { numeric(f->owner, 550, strerror(errno)); } else if (f->local_file == -2) { f->local_file = -1; } else { f->upload = 1; f->append = append;#if WANT_ASCII f->ascii_mode = c->ascii_mode;#endif prepare_for_transfer(f); }}#endif /* WANT_UPLOAD *//* * cmd_size(): Handle the SIZE command -- returns the size of a * file. Note that this command is not part of RFC959, * and thus there is no clear specification (except * for some ftpext documents, which we try to follow * as closely as we can). BetaFTPD deviates from wu-ftpd * in that it lets you check the `size' of directories * as well (instead of giving 550). This is _not_ the * size of all the files in the directory, rather how * much space the directory inode uses. */int cmd_size(struct conn * const c){#if WANT_ASCII if (c->ascii_mode) { numeric(c, 550, "SIZE not available in ASCII mode."); return 1; }#endif { const char * const fname = translate_path(c, c->recv_buf); struct stat buf; TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1); numeric(c, 213, "%lu", (unsigned long)(buf.st_size)); return 1; }}/* * cmd_mdtm(): Handle the MDTM command -- returns the modification * date/time of a file. See the comments on cmd_size(), * above. */int cmd_mdtm(struct conn * const c){ const char * const fname = translate_path(c, c->recv_buf); struct stat buf; struct tm *m; TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1); m = gmtime(&(buf.st_mtime)); /* at least wu-ftpd does it in GMT */ numeric(c, 213, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900, m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec); return 1;}/* * cmd_abor(): Handle the ABOR command (abort a file transfer). This should * be clean enough, but isn't tested extensively. */int cmd_abor(struct conn * const c){ if (c->transfer != NULL) { numeric(c, 426, "File transfer aborted."); destroy_ftran(c->transfer); } numeric(c, 226, "ABOR command processed OK."); return 1;}/* * cmd_dele(): Handle the DELE command (delete a file). */int cmd_dele(struct conn * const c){ const char * const fname = translate_path(c, c->recv_buf); TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return 1); numeric(c, 250, "File deleted OK."); return 1;}/* * cmd_rnfr(): Handle the RNFR command (take a filename to rename from). */int cmd_rnfr(struct conn * const c){ const char * const fname = translate_path(c, c->recv_buf); char cwd[256]; struct stat buf; c->rename_from[0] = '\0'; if (fname == NULL) return 1; getcwd(cwd, 256); snprintf(c->rename_from, 256, "%s/%s", cwd, fname); /* Just check that the file exists. */ TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return 1); numeric(c, 350, "File exists, send RNTO."); return 1;}/* * cmd_rnto(): Handle the RNTO command (do the actual renaming). */int cmd_rnto(struct conn * const c){ const char * const fname = translate_path(c, c->recv_buf); if (fname == NULL) return 1; if (c->rename_from[0] == '\0') { numeric(c, 503, "Please send RNFR first."); return 1; } TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return 1); c->rename_from[0] = '\0'; numeric(c, 250, "File renamed successfully."); return 1;}/* * cmd_mkd(): Handle the MKD/XMKD command (create a new directory). * RFC959 is not clear on the error codes for this command -- * one place, 521 is cited as the correct error, but is * mentioned nowhere else. Different FTP servers differ here * as well. Thus, I've followed what appears to be the intention * (having `analogous' errors with STOR), and use 550 instead. * * Making directories is probably the topic covered most * extensively by RFC959 (and in the most confusing way as * well). I try to follow the conventions, but it isn't always * easy :-) (This code isn't quite easy to understand, because * temp2 is used twice, in two different roles.) */int cmd_mkd(struct conn * const c){ const char * const fname = translate_path(c, c->recv_buf); char temp[512], temp2[1024], *cdir; int i, j; TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1); chdir(fname); getcwd(temp2, 512); cdir = do_pwd(c, temp, temp2); /* double the quotes in the output */ for (i = 0, j = 0; i <= strlen(cdir); i++, j++) { temp2[j] = cdir[i]; if (cdir[i] == '"') { temp2[++j] = '"'; } } numeric(c, 257, "\"%s\" created.", temp2); return 1;}/* * cmd_rmd(): Handle the RMD/XRMD command. Works just like DELE, only for * directories. */int cmd_rmd(struct conn * const c){ const char * const fname = translate_path(c, c->recv_buf); TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1); numeric(c, 250, "Directory deleted."); return 1;}/* * cmd_allo(): Handle the ALLO command. The command does not do anything, except * sit around and play compliant. Some Windows FTP servers (Serv-U, * for instance), verifies that there is enough space on the disk, * but since we have no idea on what the filesystem will be stored on, * we just ignore the command. * * We could theoretically use this information to give more information * to the full-screen mode, but close to no FTP clients send this * command, and it would touch too much code. */int cmd_allo(struct conn * const c){ numeric(c, 202, "No storage allocation necessary."); return 1;}/* * cmd_stat(): Handle the STAT command. Please see README for more details. * Note that this command is run with euid=root, since it has * to be able to run before USER. * * Note that we need to bypass numeric(), to get a multi-line * reply. */#if WANT_STATchar conn_state[5][27] = { "Not logged in", "Waiting for e-mail address", "Waiting for password", "Logged in", "Waiting for password", /* actually non-existant user */};char ftran_state[6][42] = { "Not initialized", "Decided PASV address/port", "Waiting on PASV socket", "Got PORT address/port", "Connecting on PORT address/port", "Transferring file (or connecting on PORT)"};#endifint cmd_stat(struct conn * const c){ #if WANT_STAT char buf[1024]; int i, err; struct ftran *f = c->transfer; snprintf(buf, 1024, "211- FTP server status:\r\n" " BetaFTPD version " VERSION " (http://members.xoom.com/sneeze/betaftpd.html)\r\n" " Connected to %s\r\n" " Control connection state: %s\r\n"#if WANT_ASCII " TYPE: %s; STRUcture: File; transfer MODE: Stream\r\n"#else " TYPE: Image; STRUcture: File; transfer MODE: Stream\r\n"#endif " Data connection state: %s\r\n" "211 End of status\r\n", inet_ntoa(((struct sockaddr_in *)(&(c->addr)))->sin_addr), conn_state[c->auth],#if WANT_ASCII (c->ascii_mode == 1) ? "ASCII, FORM: Nonprint" : "Image",#endif (f) ? ftran_state[f->state] : ftran_state[0]); i = strlen(buf); err = send(c->sock, buf, i, 0); if (err == -1 && errno == EPIPE) { destroy_conn(c); return 0; }#else numeric(c, 502, "STAT command disabled for security reasons.");#endif return 1;}#if HAVE_MMAP/* * _mwrite(): This define is for mmap-listing. It works as a write() * (not in parameter, but in function), and is used in * cmd_list() and cmd_nlst() only. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -