cmds.c revision 92282
192282Sobrien/* $NetBSD: cmds.c,v 1.16 2002/02/13 15:15:23 lukem Exp $ */ 279968Sobrien 379968Sobrien/* 479968Sobrien * Copyright (c) 1999-2001 The NetBSD Foundation, Inc. 579968Sobrien * All rights reserved. 679968Sobrien * 779968Sobrien * This code is derived from software contributed to The NetBSD Foundation 879968Sobrien * by Luke Mewburn. 979968Sobrien * 1079968Sobrien * Redistribution and use in source and binary forms, with or without 1179968Sobrien * modification, are permitted provided that the following conditions 1279968Sobrien * are met: 1379968Sobrien * 1. Redistributions of source code must retain the above copyright 1479968Sobrien * notice, this list of conditions and the following disclaimer. 1579968Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1679968Sobrien * notice, this list of conditions and the following disclaimer in the 1779968Sobrien * documentation and/or other materials provided with the distribution. 1879968Sobrien * 3. All advertising materials mentioning features or use of this software 1979968Sobrien * must display the following acknowledgement: 2079968Sobrien * This product includes software developed by the NetBSD 2179968Sobrien * Foundation, Inc. and its contributors. 2279968Sobrien * 4. Neither the name of The NetBSD Foundation nor the names of its 2379968Sobrien * contributors may be used to endorse or promote products derived 2479968Sobrien * from this software without specific prior written permission. 2579968Sobrien * 2679968Sobrien * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2779968Sobrien * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2879968Sobrien * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2979968Sobrien * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 3079968Sobrien * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3179968Sobrien * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3279968Sobrien * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3379968Sobrien * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3479968Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3579968Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3679968Sobrien * POSSIBILITY OF SUCH DAMAGE. 3779968Sobrien */ 3879968Sobrien 3979968Sobrien/* 4079968Sobrien * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 4179968Sobrien * The Regents of the University of California. All rights reserved. 4279968Sobrien * 4379968Sobrien * Redistribution and use in source and binary forms, with or without 4479968Sobrien * modification, are permitted provided that the following conditions 4579968Sobrien * are met: 4679968Sobrien * 1. Redistributions of source code must retain the above copyright 4779968Sobrien * notice, this list of conditions and the following disclaimer. 4879968Sobrien * 2. Redistributions in binary form must reproduce the above copyright 4979968Sobrien * notice, this list of conditions and the following disclaimer in the 5079968Sobrien * documentation and/or other materials provided with the distribution. 5179968Sobrien * 3. All advertising materials mentioning features or use of this software 5279968Sobrien * must display the following acknowledgement: 5379968Sobrien * This product includes software developed by the University of 5479968Sobrien * California, Berkeley and its contributors. 5579968Sobrien * 4. Neither the name of the University nor the names of its contributors 5679968Sobrien * may be used to endorse or promote products derived from this software 5779968Sobrien * without specific prior written permission. 5879968Sobrien * 5979968Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 6079968Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 6179968Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 6279968Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 6379968Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6479968Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 6579968Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6679968Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6779968Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6879968Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6979968Sobrien * SUCH DAMAGE. 7079968Sobrien */ 7179968Sobrien 7279968Sobrien/* 7379968Sobrien * Copyright (C) 1997 and 1998 WIDE Project. 7479968Sobrien * All rights reserved. 7579968Sobrien * 7679968Sobrien * Redistribution and use in source and binary forms, with or without 7779968Sobrien * modification, are permitted provided that the following conditions 7879968Sobrien * are met: 7979968Sobrien * 1. Redistributions of source code must retain the above copyright 8079968Sobrien * notice, this list of conditions and the following disclaimer. 8179968Sobrien * 2. Redistributions in binary form must reproduce the above copyright 8279968Sobrien * notice, this list of conditions and the following disclaimer in the 8379968Sobrien * documentation and/or other materials provided with the distribution. 8479968Sobrien * 3. Neither the name of the project nor the names of its contributors 8579968Sobrien * may be used to endorse or promote products derived from this software 8679968Sobrien * without specific prior written permission. 8779968Sobrien * 8879968Sobrien * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 8979968Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 9079968Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 9179968Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 9279968Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 9379968Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 9479968Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 9579968Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 9679968Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 9779968Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 9879968Sobrien * SUCH DAMAGE. 9979968Sobrien */ 10079968Sobrien 10179968Sobrien#include "lukemftpd.h" 10279968Sobrien 10379968Sobrien#include "extern.h" 10479968Sobrien 10592282Sobrientypedef enum { 10692282Sobrien FE_MLSD = 1<<0, /* if op is MLSD (MLST otherwise ) */ 10792282Sobrien FE_ISCURDIR = 1<<1, /* if name is the current directory */ 10892282Sobrien} factflag_t; 10992282Sobrien 11079968Sobrientypedef struct { 11179968Sobrien const char *path; /* full pathname */ 11279968Sobrien const char *display; /* name to display */ 11379968Sobrien struct stat *stat; /* stat of path */ 11479968Sobrien struct stat *pdirstat; /* stat of path's parent dir */ 11592282Sobrien factflag_t flags; /* flags */ 11679968Sobrien} factelem; 11779968Sobrien 11879968Sobrienstatic void ack(const char *); 11979968Sobrienstatic void base64_encode(const char *, size_t, char *, int); 12079968Sobrienstatic void fact_type(const char *, FILE *, factelem *); 12179968Sobrienstatic void fact_size(const char *, FILE *, factelem *); 12279968Sobrienstatic void fact_modify(const char *, FILE *, factelem *); 12379968Sobrienstatic void fact_perm(const char *, FILE *, factelem *); 12479968Sobrienstatic void fact_unique(const char *, FILE *, factelem *); 12579968Sobrienstatic int matchgroup(gid_t); 12679968Sobrienstatic void mlsname(FILE *, factelem *); 12779968Sobrienstatic void replydirname(const char *, const char *); 12879968Sobrien 12979968Sobrienstruct ftpfact { 13079968Sobrien const char *name; /* name of fact */ 13179968Sobrien int enabled; /* if fact is enabled */ 13279968Sobrien void (*display)(const char *, FILE *, factelem *); 13379968Sobrien /* function to display fact */ 13479968Sobrien}; 13579968Sobrien 13679968Sobrienstruct ftpfact facttab[] = { 13779968Sobrien { "Type", 1, fact_type }, 13879968Sobrien#define FACT_TYPE 0 13979968Sobrien { "Size", 1, fact_size }, 14079968Sobrien { "Modify", 1, fact_modify }, 14179968Sobrien { "Perm", 1, fact_perm }, 14279968Sobrien { "Unique", 1, fact_unique }, 14379968Sobrien /* "Create" */ 14479968Sobrien /* "Lang" */ 14579968Sobrien /* "Media-Type" */ 14679968Sobrien /* "CharSet" */ 14779968Sobrien}; 14879968Sobrien 14979968Sobrien#define FACTTABSIZE (sizeof(facttab) / sizeof(struct ftpfact)) 15079968Sobrien 15179968Sobrien 15279968Sobrienvoid 15379968Sobriencwd(const char *path) 15479968Sobrien{ 15579968Sobrien 15679968Sobrien if (chdir(path) < 0) 15779968Sobrien perror_reply(550, path); 15879968Sobrien else { 15979968Sobrien show_chdir_messages(250); 16079968Sobrien ack("CWD"); 16179968Sobrien } 16279968Sobrien} 16379968Sobrien 16479968Sobrienvoid 16579968Sobriendelete(const char *name) 16679968Sobrien{ 16779968Sobrien char *p = NULL; 16879968Sobrien 16979968Sobrien if (remove(name) < 0) { 17079968Sobrien p = strerror(errno); 17179968Sobrien perror_reply(550, name); 17279968Sobrien } else 17379968Sobrien ack("DELE"); 17479968Sobrien logxfer("delete", -1, name, NULL, NULL, p); 17579968Sobrien} 17679968Sobrien 17779968Sobrienvoid 17879968Sobrienfeat(void) 17979968Sobrien{ 18079968Sobrien int i; 18179968Sobrien 18279968Sobrien reply(-211, "Features supported"); 18379968Sobrien cprintf(stdout, " MDTM\r\n"); 18479968Sobrien cprintf(stdout, " MLST "); 18579968Sobrien for (i = 0; i < FACTTABSIZE; i++) 18679968Sobrien cprintf(stdout, "%s%s;", facttab[i].name, 18779968Sobrien facttab[i].enabled ? "*" : ""); 18879968Sobrien cprintf(stdout, "\r\n"); 18979968Sobrien cprintf(stdout, " REST STREAM\r\n"); 19079968Sobrien cprintf(stdout, " SIZE\r\n"); 19179968Sobrien cprintf(stdout, " TVFS\r\n"); 19279968Sobrien reply(211, "End"); 19379968Sobrien} 19479968Sobrien 19579968Sobrienvoid 19679968Sobrienmakedir(const char *name) 19779968Sobrien{ 19879968Sobrien char *p = NULL; 19979968Sobrien 20079968Sobrien if (mkdir(name, 0777) < 0) { 20179968Sobrien p = strerror(errno); 20279968Sobrien perror_reply(550, name); 20379968Sobrien } else 20479968Sobrien replydirname(name, "directory created."); 20579968Sobrien logxfer("mkdir", -1, name, NULL, NULL, p); 20679968Sobrien} 20779968Sobrien 20879968Sobrienvoid 20979968Sobrienmlsd(const char *path) 21079968Sobrien{ 21179968Sobrien struct dirent *dp; 21279968Sobrien struct stat sb, pdirstat; 21379968Sobrien factelem f; 21479968Sobrien FILE *dout; 21579968Sobrien DIR *dirp; 21679968Sobrien char name[MAXPATHLEN]; 21779968Sobrien int hastypefact; 21879968Sobrien 21979968Sobrien hastypefact = facttab[FACT_TYPE].enabled; 22079968Sobrien if (path == NULL) 22179968Sobrien path = "."; 22279968Sobrien if (stat(path, &pdirstat) == -1) { 22379968Sobrien mlsdperror: 22479968Sobrien perror_reply(550, path); 22579968Sobrien return; 22679968Sobrien } 22779968Sobrien if (! S_ISDIR(pdirstat.st_mode)) { 22879968Sobrien errno = ENOTDIR; 22979968Sobrien perror_reply(501, path); 23079968Sobrien return; 23179968Sobrien } 23292282Sobrien if ((dirp = opendir(path)) == NULL) 23392282Sobrien goto mlsdperror; 23492282Sobrien 23579968Sobrien dout = dataconn("MLSD", (off_t)-1, "w"); 23679968Sobrien if (dout == NULL) 23779968Sobrien return; 23879968Sobrien 23992282Sobrien memset(&f, 0, sizeof(f)); 24079968Sobrien f.stat = &sb; 24192282Sobrien f.flags |= FE_MLSD; 24279968Sobrien while ((dp = readdir(dirp)) != NULL) { 24379968Sobrien snprintf(name, sizeof(name), "%s/%s", path, dp->d_name); 24479968Sobrien if (ISDOTDIR(dp->d_name)) { /* special case curdir: */ 24579968Sobrien if (! hastypefact) 24679968Sobrien continue; 24779968Sobrien f.pdirstat = NULL; /* require stat of parent */ 24879968Sobrien f.display = path; /* set name to real name */ 24992282Sobrien f.flags |= FE_ISCURDIR; /* flag name is curdir */ 25079968Sobrien } else { 25179968Sobrien if (ISDOTDOTDIR(dp->d_name)) { 25279968Sobrien if (! hastypefact) 25379968Sobrien continue; 25479968Sobrien f.pdirstat = NULL; 25579968Sobrien } else 25679968Sobrien f.pdirstat = &pdirstat; /* cache parent stat */ 25779968Sobrien f.display = dp->d_name; 25892282Sobrien f.flags &= ~FE_ISCURDIR; 25979968Sobrien } 26079968Sobrien if (stat(name, &sb) == -1) 26179968Sobrien continue; 26279968Sobrien f.path = name; 26379968Sobrien mlsname(dout, &f); 26479968Sobrien } 26579968Sobrien (void)closedir(dirp); 26679968Sobrien 26779968Sobrien if (ferror(dout) != 0) 26879968Sobrien perror_reply(550, "Data connection"); 26979968Sobrien else 27079968Sobrien reply(226, "MLSD complete."); 27179968Sobrien closedataconn(dout); 27279968Sobrien total_xfers_out++; 27379968Sobrien total_xfers++; 27479968Sobrien} 27579968Sobrien 27679968Sobrienvoid 27779968Sobrienmlst(const char *path) 27879968Sobrien{ 27979968Sobrien struct stat sb; 28079968Sobrien factelem f; 28179968Sobrien 28279968Sobrien if (path == NULL) 28379968Sobrien path = "."; 28479968Sobrien if (stat(path, &sb) == -1) { 28579968Sobrien perror_reply(550, path); 28679968Sobrien return; 28779968Sobrien } 28879968Sobrien reply(-250, "MLST %s", path); 28992282Sobrien memset(&f, 0, sizeof(f)); 29079968Sobrien f.path = path; 29179968Sobrien f.display = path; 29279968Sobrien f.stat = &sb; 29379968Sobrien f.pdirstat = NULL; 29479968Sobrien CPUTC(' ', stdout); 29579968Sobrien mlsname(stdout, &f); 29679968Sobrien reply(250, "End"); 29779968Sobrien} 29879968Sobrien 29979968Sobrien 30079968Sobrienvoid 30179968Sobrienopts(const char *command) 30279968Sobrien{ 30379968Sobrien struct tab *c; 30479968Sobrien char *ep; 30579968Sobrien 30679968Sobrien if ((ep = strchr(command, ' ')) != NULL) 30779968Sobrien *ep++ = '\0'; 30879968Sobrien c = lookup(cmdtab, command); 30979968Sobrien if (c == NULL) { 31079968Sobrien reply(502, "Unknown command %s.", command); 31179968Sobrien return; 31279968Sobrien } 31379968Sobrien if (! CMD_IMPLEMENTED(c)) { 31479968Sobrien reply(501, "%s command not implemented.", c->name); 31579968Sobrien return; 31679968Sobrien } 31779968Sobrien if (! CMD_HAS_OPTIONS(c)) { 31879968Sobrien reply(501, "%s command does not support persistent options.", 31979968Sobrien c->name); 32079968Sobrien return; 32179968Sobrien } 32279968Sobrien 32379968Sobrien /* special case: MLST */ 32479968Sobrien if (strcasecmp(command, "MLST") == 0) { 32579968Sobrien int enabled[FACTTABSIZE]; 32679968Sobrien int i, onedone; 32779968Sobrien size_t len; 32879968Sobrien char *p; 32979968Sobrien 33079968Sobrien for (i = 0; i < sizeof(enabled) / sizeof(int); i++) 33179968Sobrien enabled[i] = 0; 33279968Sobrien if (ep == NULL || *ep == '\0') 33379968Sobrien goto displaymlstopts; 33479968Sobrien 33579968Sobrien /* don't like spaces, and need trailing ; */ 33679968Sobrien len = strlen(ep); 33779968Sobrien if (strchr(ep, ' ') != NULL || ep[len - 1] != ';') { 33879968Sobrien badmlstopt: 33979968Sobrien reply(501, "Invalid MLST options"); 34079968Sobrien return; 34179968Sobrien } 34279968Sobrien ep[len - 1] = '\0'; 34379968Sobrien while ((p = strsep(&ep, ";")) != NULL) { 34479968Sobrien if (*p == '\0') 34579968Sobrien goto badmlstopt; 34679968Sobrien for (i = 0; i < FACTTABSIZE; i++) 34779968Sobrien if (strcasecmp(p, facttab[i].name) == 0) { 34879968Sobrien enabled[i] = 1; 34979968Sobrien break; 35079968Sobrien } 35179968Sobrien } 35279968Sobrien 35379968Sobrien displaymlstopts: 35479968Sobrien for (i = 0; i < FACTTABSIZE; i++) 35579968Sobrien facttab[i].enabled = enabled[i]; 35679968Sobrien cprintf(stdout, "200 MLST OPTS"); 35779968Sobrien for (i = onedone = 0; i < FACTTABSIZE; i++) { 35879968Sobrien if (facttab[i].enabled) { 35979968Sobrien cprintf(stdout, "%s%s;", onedone ? "" : " ", 36079968Sobrien facttab[i].name); 36179968Sobrien onedone++; 36279968Sobrien } 36379968Sobrien } 36479968Sobrien cprintf(stdout, "\r\n"); 36579968Sobrien fflush(stdout); 36679968Sobrien return; 36779968Sobrien } 36879968Sobrien 36979968Sobrien /* default cases */ 37079968Sobrien if (ep != NULL && *ep != '\0') 37179968Sobrien REASSIGN(c->options, xstrdup(ep)); 37279968Sobrien if (c->options != NULL) 37379968Sobrien reply(200, "Options for %s are '%s'.", c->name, 37479968Sobrien c->options); 37579968Sobrien else 37679968Sobrien reply(200, "No options defined for %s.", c->name); 37779968Sobrien} 37879968Sobrien 37979968Sobrienvoid 38079968Sobrienpwd(void) 38179968Sobrien{ 38279968Sobrien char path[MAXPATHLEN]; 38379968Sobrien 38479968Sobrien if (getcwd(path, sizeof(path) - 1) == NULL) 38579968Sobrien reply(550, "Can't get the current directory: %s.", 38679968Sobrien strerror(errno)); 38779968Sobrien else 38879968Sobrien replydirname(path, "is the current directory."); 38979968Sobrien} 39079968Sobrien 39179968Sobrienvoid 39279968Sobrienremovedir(const char *name) 39379968Sobrien{ 39479968Sobrien char *p = NULL; 39579968Sobrien 39679968Sobrien if (rmdir(name) < 0) { 39779968Sobrien p = strerror(errno); 39879968Sobrien perror_reply(550, name); 39979968Sobrien } else 40079968Sobrien ack("RMD"); 40179968Sobrien logxfer("rmdir", -1, name, NULL, NULL, p); 40279968Sobrien} 40379968Sobrien 40479968Sobrienchar * 40579968Sobrienrenamefrom(const char *name) 40679968Sobrien{ 40779968Sobrien struct stat st; 40879968Sobrien 40979968Sobrien if (stat(name, &st) < 0) { 41079968Sobrien perror_reply(550, name); 41179968Sobrien return (NULL); 41279968Sobrien } 41379968Sobrien reply(350, "File exists, ready for destination name"); 41479968Sobrien return (xstrdup(name)); 41579968Sobrien} 41679968Sobrien 41779968Sobrienvoid 41879968Sobrienrenamecmd(const char *from, const char *to) 41979968Sobrien{ 42079968Sobrien char *p = NULL; 42179968Sobrien 42279968Sobrien if (rename(from, to) < 0) { 42379968Sobrien p = strerror(errno); 42479968Sobrien perror_reply(550, "rename"); 42579968Sobrien } else 42679968Sobrien ack("RNTO"); 42779968Sobrien logxfer("rename", -1, from, to, NULL, p); 42879968Sobrien} 42979968Sobrien 43079968Sobrienvoid 43179968Sobriensizecmd(const char *filename) 43279968Sobrien{ 43379968Sobrien switch (type) { 43479968Sobrien case TYPE_L: 43579968Sobrien case TYPE_I: 43679968Sobrien { 43779968Sobrien struct stat stbuf; 43879968Sobrien if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 43979968Sobrien reply(550, "%s: not a plain file.", filename); 44079968Sobrien else 44179968Sobrien reply(213, ULLF, (ULLT)stbuf.st_size); 44279968Sobrien break; 44379968Sobrien } 44479968Sobrien case TYPE_A: 44579968Sobrien { 44679968Sobrien FILE *fin; 44779968Sobrien int c; 44879968Sobrien off_t count; 44979968Sobrien struct stat stbuf; 45079968Sobrien fin = fopen(filename, "r"); 45179968Sobrien if (fin == NULL) { 45279968Sobrien perror_reply(550, filename); 45379968Sobrien return; 45479968Sobrien } 45579968Sobrien if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 45679968Sobrien reply(550, "%s: not a plain file.", filename); 45779968Sobrien (void) fclose(fin); 45879968Sobrien return; 45979968Sobrien } 46092282Sobrien if (stbuf.st_size > 10240) { 46192282Sobrien reply(550, "%s: file too large for SIZE.", filename); 46292282Sobrien (void) fclose(fin); 46392282Sobrien return; 46492282Sobrien } 46579968Sobrien 46679968Sobrien count = 0; 46792282Sobrien while((c = getc(fin)) != EOF) { 46879968Sobrien if (c == '\n') /* will get expanded to \r\n */ 46979968Sobrien count++; 47079968Sobrien count++; 47179968Sobrien } 47279968Sobrien (void) fclose(fin); 47379968Sobrien 47479968Sobrien reply(213, LLF, (LLT)count); 47579968Sobrien break; 47679968Sobrien } 47779968Sobrien default: 47879968Sobrien reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 47979968Sobrien } 48079968Sobrien} 48179968Sobrien 48279968Sobrienvoid 48379968Sobrienstatfilecmd(const char *filename) 48479968Sobrien{ 48579968Sobrien FILE *fin; 48679968Sobrien int c; 48779968Sobrien char *argv[] = { INTERNAL_LS, "-lgA", "", NULL }; 48879968Sobrien 48979968Sobrien argv[2] = (char *)filename; 49079968Sobrien fin = ftpd_popen(argv, "r", STDOUT_FILENO); 49179968Sobrien reply(-211, "status of %s:", filename); 49279968Sobrien/* XXX: use fgetln() or fparseln() here? */ 49379968Sobrien while ((c = getc(fin)) != EOF) { 49479968Sobrien if (c == '\n') { 49579968Sobrien if (ferror(stdout)){ 49679968Sobrien perror_reply(421, "control connection"); 49779968Sobrien (void) ftpd_pclose(fin); 49879968Sobrien dologout(1); 49979968Sobrien /* NOTREACHED */ 50079968Sobrien } 50179968Sobrien if (ferror(fin)) { 50279968Sobrien perror_reply(551, filename); 50379968Sobrien (void) ftpd_pclose(fin); 50479968Sobrien return; 50579968Sobrien } 50679968Sobrien CPUTC('\r', stdout); 50779968Sobrien } 50879968Sobrien CPUTC(c, stdout); 50979968Sobrien } 51079968Sobrien (void) ftpd_pclose(fin); 51179968Sobrien reply(211, "End of Status"); 51279968Sobrien} 51379968Sobrien 51479968Sobrien/* -- */ 51579968Sobrien 51679968Sobrienstatic void 51779968Sobrienack(const char *s) 51879968Sobrien{ 51979968Sobrien 52079968Sobrien reply(250, "%s command successful.", s); 52179968Sobrien} 52279968Sobrien 52379968Sobrien/* 52479968Sobrien * Encode len bytes starting at clear using base64 encoding into encoded, 52579968Sobrien * which should be at least ((len + 2) * 4 / 3 + 1) in size. 52679968Sobrien * If nulterm is non-zero, terminate with \0 otherwise pad to 3 byte boundary 52779968Sobrien * with `='. 52879968Sobrien */ 52979968Sobrienstatic void 53079968Sobrienbase64_encode(const char *clear, size_t len, char *encoded, int nulterm) 53179968Sobrien{ 53279968Sobrien static const char base64[] = 53379968Sobrien "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 53479968Sobrien const char *c; 53579968Sobrien char *e, termchar; 53679968Sobrien int i; 53779968Sobrien 53879968Sobrien /* determine whether to pad with '=' or NUL terminate */ 53979968Sobrien termchar = nulterm ? '\0' : '='; 54079968Sobrien c = clear; 54179968Sobrien e = encoded; 54279968Sobrien /* convert all but last 2 bytes */ 54379968Sobrien for (i = len; i > 2; i -= 3, c += 3) { 54479968Sobrien *e++ = base64[(c[0] >> 2) & 0x3f]; 54579968Sobrien *e++ = base64[((c[0] << 4) & 0x30) | ((c[1] >> 4) & 0x0f)]; 54679968Sobrien *e++ = base64[((c[1] << 2) & 0x3c) | ((c[2] >> 6) & 0x03)]; 54779968Sobrien *e++ = base64[(c[2]) & 0x3f]; 54879968Sobrien } 54979968Sobrien /* handle slop at end */ 55079968Sobrien if (i > 0) { 55179968Sobrien *e++ = base64[(c[0] >> 2) & 0x3f]; 55279968Sobrien *e++ = base64[((c[0] << 4) & 0x30) | 55379968Sobrien (i > 1 ? ((c[1] >> 4) & 0x0f) : 0)]; 55479968Sobrien *e++ = (i > 1) ? base64[(c[1] << 2) & 0x3c] : termchar; 55579968Sobrien *e++ = termchar; 55679968Sobrien } 55779968Sobrien *e = '\0'; 55879968Sobrien} 55979968Sobrien 56079968Sobrienstatic void 56179968Sobrienfact_modify(const char *fact, FILE *fd, factelem *fe) 56279968Sobrien{ 56379968Sobrien struct tm *t; 56479968Sobrien 56579968Sobrien t = gmtime(&(fe->stat->st_mtime)); 56679968Sobrien cprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact, 56779968Sobrien TM_YEAR_BASE + t->tm_year, 56879968Sobrien t->tm_mon+1, t->tm_mday, 56979968Sobrien t->tm_hour, t->tm_min, t->tm_sec); 57079968Sobrien} 57179968Sobrien 57279968Sobrienstatic void 57379968Sobrienfact_perm(const char *fact, FILE *fd, factelem *fe) 57479968Sobrien{ 57579968Sobrien int rok, wok, xok, pdirwok; 57679968Sobrien struct stat *pdir; 57779968Sobrien 57879968Sobrien if (fe->stat->st_uid == geteuid()) { 57979968Sobrien rok = ((fe->stat->st_mode & S_IRUSR) != 0); 58079968Sobrien wok = ((fe->stat->st_mode & S_IWUSR) != 0); 58179968Sobrien xok = ((fe->stat->st_mode & S_IXUSR) != 0); 58279968Sobrien } else if (matchgroup(fe->stat->st_gid)) { 58379968Sobrien rok = ((fe->stat->st_mode & S_IRGRP) != 0); 58479968Sobrien wok = ((fe->stat->st_mode & S_IWGRP) != 0); 58579968Sobrien xok = ((fe->stat->st_mode & S_IXGRP) != 0); 58679968Sobrien } else { 58779968Sobrien rok = ((fe->stat->st_mode & S_IROTH) != 0); 58879968Sobrien wok = ((fe->stat->st_mode & S_IWOTH) != 0); 58979968Sobrien xok = ((fe->stat->st_mode & S_IXOTH) != 0); 59079968Sobrien } 59179968Sobrien 59279968Sobrien cprintf(fd, "%s=", fact); 59379968Sobrien 59479968Sobrien /* 59579968Sobrien * if parent info not provided, look it up, but 59679968Sobrien * only if the current class has modify rights, 59779968Sobrien * since we only need this info in such a case. 59879968Sobrien */ 59979968Sobrien pdir = fe->pdirstat; 60079968Sobrien if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) { 60179968Sobrien size_t len; 60279968Sobrien char realdir[MAXPATHLEN], *p; 60379968Sobrien struct stat dir; 60479968Sobrien 60579968Sobrien len = strlcpy(realdir, fe->path, sizeof(realdir)); 60679968Sobrien if (len < sizeof(realdir) - 4) { 60779968Sobrien if (S_ISDIR(fe->stat->st_mode)) 60879968Sobrien strlcat(realdir, "/..", sizeof(realdir)); 60979968Sobrien else { 61079968Sobrien /* if has a /, move back to it */ 61179968Sobrien /* otherwise use '..' */ 61279968Sobrien if ((p = strrchr(realdir, '/')) != NULL) { 61379968Sobrien if (p == realdir) 61479968Sobrien p++; 61579968Sobrien *p = '\0'; 61679968Sobrien } else 61779968Sobrien strlcpy(realdir, "..", sizeof(realdir)); 61879968Sobrien } 61979968Sobrien if (stat(realdir, &dir) == 0) 62079968Sobrien pdir = &dir; 62179968Sobrien } 62279968Sobrien } 62379968Sobrien pdirwok = 0; 62479968Sobrien if (pdir != NULL) { 62579968Sobrien if (pdir->st_uid == geteuid()) 62679968Sobrien pdirwok = ((pdir->st_mode & S_IWUSR) != 0); 62779968Sobrien else if (matchgroup(pdir->st_gid)) 62879968Sobrien pdirwok = ((pdir->st_mode & S_IWGRP) != 0); 62979968Sobrien else 63079968Sobrien pdirwok = ((pdir->st_mode & S_IWOTH) != 0); 63179968Sobrien } 63279968Sobrien 63379968Sobrien /* 'a': can APPE to file */ 63479968Sobrien if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 63579968Sobrien CPUTC('a', fd); 63679968Sobrien 63779968Sobrien /* 'c': can create or append to files in directory */ 63879968Sobrien if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 63979968Sobrien CPUTC('c', fd); 64079968Sobrien 64179968Sobrien /* 'd': can delete file or directory */ 64279968Sobrien if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) { 64379968Sobrien int candel; 64479968Sobrien 64579968Sobrien candel = 1; 64679968Sobrien if (S_ISDIR(fe->stat->st_mode)) { 64779968Sobrien DIR *dirp; 64879968Sobrien struct dirent *dp; 64979968Sobrien 65079968Sobrien if ((dirp = opendir(fe->display)) == NULL) 65179968Sobrien candel = 0; 65279968Sobrien else { 65379968Sobrien while ((dp = readdir(dirp)) != NULL) { 65479968Sobrien if (ISDOTDIR(dp->d_name) || 65579968Sobrien ISDOTDOTDIR(dp->d_name)) 65679968Sobrien continue; 65779968Sobrien candel = 0; 65879968Sobrien break; 65979968Sobrien } 66079968Sobrien closedir(dirp); 66179968Sobrien } 66279968Sobrien } 66379968Sobrien if (candel) 66479968Sobrien CPUTC('d', fd); 66579968Sobrien } 66679968Sobrien 66779968Sobrien /* 'e': can enter directory */ 66879968Sobrien if (xok && S_ISDIR(fe->stat->st_mode)) 66979968Sobrien CPUTC('e', fd); 67079968Sobrien 67179968Sobrien /* 'f': can rename file or directory */ 67279968Sobrien if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) 67379968Sobrien CPUTC('f', fd); 67479968Sobrien 67579968Sobrien /* 'l': can list directory */ 67679968Sobrien if (rok && xok && S_ISDIR(fe->stat->st_mode)) 67779968Sobrien CPUTC('l', fd); 67879968Sobrien 67979968Sobrien /* 'm': can create directory */ 68079968Sobrien if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 68179968Sobrien CPUTC('m', fd); 68279968Sobrien 68379968Sobrien /* 'p': can remove files in directory */ 68479968Sobrien if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 68579968Sobrien CPUTC('p', fd); 68679968Sobrien 68779968Sobrien /* 'r': can RETR file */ 68879968Sobrien if (rok && S_ISREG(fe->stat->st_mode)) 68979968Sobrien CPUTC('r', fd); 69079968Sobrien 69179968Sobrien /* 'w': can STOR file */ 69279968Sobrien if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 69379968Sobrien CPUTC('w', fd); 69479968Sobrien 69579968Sobrien CPUTC(';', fd); 69679968Sobrien} 69779968Sobrien 69879968Sobrienstatic void 69979968Sobrienfact_size(const char *fact, FILE *fd, factelem *fe) 70079968Sobrien{ 70179968Sobrien 70279968Sobrien if (S_ISREG(fe->stat->st_mode)) 70379968Sobrien cprintf(fd, "%s=" LLF ";", fact, (LLT)fe->stat->st_size); 70479968Sobrien} 70579968Sobrien 70679968Sobrienstatic void 70779968Sobrienfact_type(const char *fact, FILE *fd, factelem *fe) 70879968Sobrien{ 70979968Sobrien 71079968Sobrien cprintf(fd, "%s=", fact); 71179968Sobrien switch (fe->stat->st_mode & S_IFMT) { 71279968Sobrien case S_IFDIR: 71392282Sobrien if (fe->flags & FE_MLSD) { 71492282Sobrien if ((fe->flags & FE_ISCURDIR) || ISDOTDIR(fe->display)) 71592282Sobrien cprintf(fd, "cdir"); 71692282Sobrien else if (ISDOTDOTDIR(fe->display)) 71792282Sobrien cprintf(fd, "pdir"); 71892282Sobrien else 71992282Sobrien cprintf(fd, "dir"); 72092282Sobrien } else { 72179968Sobrien cprintf(fd, "dir"); 72292282Sobrien } 72379968Sobrien break; 72479968Sobrien case S_IFREG: 72579968Sobrien cprintf(fd, "file"); 72679968Sobrien break; 72779968Sobrien case S_IFIFO: 72879968Sobrien cprintf(fd, "OS.unix=fifo"); 72979968Sobrien break; 73079968Sobrien case S_IFLNK: /* XXX: probably a NO-OP with stat() */ 73179968Sobrien cprintf(fd, "OS.unix=slink"); 73279968Sobrien break; 73379968Sobrien case S_IFSOCK: 73479968Sobrien cprintf(fd, "OS.unix=socket"); 73579968Sobrien break; 73679968Sobrien case S_IFBLK: 73779968Sobrien case S_IFCHR: 73879968Sobrien cprintf(fd, "OS.unix=%s-%d/%d", 73979968Sobrien S_ISBLK(fe->stat->st_mode) ? "blk" : "chr", 74079968Sobrien major(fe->stat->st_rdev), minor(fe->stat->st_rdev)); 74179968Sobrien break; 74279968Sobrien default: 74379968Sobrien cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT); 74479968Sobrien break; 74579968Sobrien } 74679968Sobrien CPUTC(';', fd); 74779968Sobrien} 74879968Sobrien 74979968Sobrienstatic void 75079968Sobrienfact_unique(const char *fact, FILE *fd, factelem *fe) 75179968Sobrien{ 75279968Sobrien char obuf[(sizeof(dev_t) + sizeof(ino_t) + 2) * 4 / 3 + 2]; 75379968Sobrien char tbuf[sizeof(dev_t) + sizeof(ino_t)]; 75479968Sobrien 75579968Sobrien memcpy(tbuf, 75679968Sobrien (char *)&(fe->stat->st_dev), sizeof(dev_t)); 75779968Sobrien memcpy(tbuf + sizeof(dev_t), 75879968Sobrien (char *)&(fe->stat->st_ino), sizeof(ino_t)); 75979968Sobrien base64_encode(tbuf, sizeof(dev_t) + sizeof(ino_t), obuf, 1); 76079968Sobrien cprintf(fd, "%s=%s;", fact, obuf); 76179968Sobrien} 76279968Sobrien 76379968Sobrienstatic int 76479968Sobrienmatchgroup(gid_t gid) 76579968Sobrien{ 76679968Sobrien int i; 76779968Sobrien 76879968Sobrien for (i = 0; i < gidcount; i++) 76979968Sobrien if (gid == gidlist[i]) 77079968Sobrien return(1); 77179968Sobrien return (0); 77279968Sobrien} 77379968Sobrien 77479968Sobrienstatic void 77579968Sobrienmlsname(FILE *fp, factelem *fe) 77679968Sobrien{ 77792282Sobrien char realfile[MAXPATHLEN]; 77892282Sobrien int i, userf; 77979968Sobrien 78079968Sobrien for (i = 0; i < FACTTABSIZE; i++) { 78179968Sobrien if (facttab[i].enabled) 78279968Sobrien (facttab[i].display)(facttab[i].name, fp, fe); 78379968Sobrien } 78492282Sobrien if ((fe->flags & FE_MLSD) && 78592282Sobrien !(fe->flags & FE_ISCURDIR) && !ISDOTDIR(fe->display)) { 78692282Sobrien /* if MLSD and not "." entry, display as-is */ 78792282Sobrien userf = 0; 78892282Sobrien } else { 78992282Sobrien /* if MLST, or MLSD and "." entry, realpath(3) it */ 79092282Sobrien if (realpath(fe->display, realfile) != NULL) 79192282Sobrien userf = 1; 79292282Sobrien } 79392282Sobrien cprintf(fp, " %s\r\n", userf ? realfile : fe->display); 79479968Sobrien} 79579968Sobrien 79679968Sobrienstatic void 79779968Sobrienreplydirname(const char *name, const char *message) 79879968Sobrien{ 79979968Sobrien char *p, *ep; 80079968Sobrien char npath[MAXPATHLEN * 2]; 80179968Sobrien 80279968Sobrien p = npath; 80379968Sobrien ep = &npath[sizeof(npath) - 1]; 80479968Sobrien while (*name) { 80579968Sobrien if (*name == '"') { 80679968Sobrien if (ep - p < 2) 80779968Sobrien break; 80879968Sobrien *p++ = *name++; 80979968Sobrien *p++ = '"'; 81079968Sobrien } else { 81179968Sobrien if (ep - p < 1) 81279968Sobrien break; 81379968Sobrien *p++ = *name++; 81479968Sobrien } 81579968Sobrien } 81679968Sobrien *p = '\0'; 81779968Sobrien reply(257, "\"%s\" %s", npath, message); 81879968Sobrien} 819