Annotation of src/libexec/ftpd/cmds.c, Revision 1.4.2.5
1.4.2.5 ! jmc 1: /* $NetBSD: cmds.c,v 1.4.2.4 2002/10/25 02:12:34 itojun Exp $ */
1.1 lukem 2:
3: /*
1.4.2.5 ! jmc 4: * Copyright (c) 1999-2004 The NetBSD Foundation, Inc.
1.1 lukem 5: * All rights reserved.
6: *
7: * This code is derived from software contributed to The NetBSD Foundation
8: * by Luke Mewburn.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the NetBSD
21: * Foundation, Inc. and its contributors.
22: * 4. Neither the name of The NetBSD Foundation nor the names of its
23: * contributors may be used to endorse or promote products derived
24: * from this software without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36: * POSSIBILITY OF SUCH DAMAGE.
37: */
38:
39: /*
40: * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
41: * The Regents of the University of California. All rights reserved.
42: *
43: * Redistribution and use in source and binary forms, with or without
44: * modification, are permitted provided that the following conditions
45: * are met:
46: * 1. Redistributions of source code must retain the above copyright
47: * notice, this list of conditions and the following disclaimer.
48: * 2. Redistributions in binary form must reproduce the above copyright
49: * notice, this list of conditions and the following disclaimer in the
50: * documentation and/or other materials provided with the distribution.
1.4.2.5 ! jmc 51: * 3. Neither the name of the University nor the names of its contributors
1.1 lukem 52: * may be used to endorse or promote products derived from this software
53: * without specific prior written permission.
54: *
55: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65: * SUCH DAMAGE.
66: */
67:
68: /*
69: * Copyright (C) 1997 and 1998 WIDE Project.
70: * All rights reserved.
71: *
72: * Redistribution and use in source and binary forms, with or without
73: * modification, are permitted provided that the following conditions
74: * are met:
75: * 1. Redistributions of source code must retain the above copyright
76: * notice, this list of conditions and the following disclaimer.
77: * 2. Redistributions in binary form must reproduce the above copyright
78: * notice, this list of conditions and the following disclaimer in the
79: * documentation and/or other materials provided with the distribution.
80: * 3. Neither the name of the project nor the names of its contributors
81: * may be used to endorse or promote products derived from this software
82: * without specific prior written permission.
83: *
84: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
85: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
86: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
87: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
88: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
89: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
90: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
91: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
92: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
93: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
94: * SUCH DAMAGE.
95: */
96:
97:
98: #include <sys/cdefs.h>
99: #ifndef lint
1.4.2.5 ! jmc 100: __RCSID("$NetBSD: cmds.c,v 1.4.2.4 2002/10/25 02:12:34 itojun Exp $");
1.1 lukem 101: #endif /* not lint */
102:
103: #include <sys/param.h>
104: #include <sys/stat.h>
105:
106: #include <arpa/ftp.h>
107:
108: #include <dirent.h>
109: #include <errno.h>
110: #include <stdio.h>
111: #include <stdlib.h>
112: #include <string.h>
113: #include <tzfile.h>
114: #include <unistd.h>
1.4.2.4 itojun 115: #include <ctype.h>
1.2 explorer 116:
117: #ifdef KERBEROS5
118: #include <krb5/krb5.h>
119: #endif
1.1 lukem 120:
121: #include "extern.h"
122:
1.4.2.5 ! jmc 123: typedef enum {
! 124: FE_MLSD = 1<<0, /* if op is MLSD (MLST otherwise ) */
! 125: FE_ISCURDIR = 1<<1, /* if name is the current directory */
! 126: } factflag_t;
! 127:
1.1 lukem 128: typedef struct {
129: const char *path; /* full pathname */
130: const char *display; /* name to display */
131: struct stat *stat; /* stat of path */
132: struct stat *pdirstat; /* stat of path's parent dir */
1.4.2.5 ! jmc 133: factflag_t flags; /* flags */
1.1 lukem 134: } factelem;
135:
136: static void ack(const char *);
1.3 lukem 137: static void base64_encode(const char *, size_t, char *, int);
1.1 lukem 138: static void fact_type(const char *, FILE *, factelem *);
139: static void fact_size(const char *, FILE *, factelem *);
140: static void fact_modify(const char *, FILE *, factelem *);
141: static void fact_perm(const char *, FILE *, factelem *);
142: static void fact_unique(const char *, FILE *, factelem *);
1.3 lukem 143: static int matchgroup(gid_t);
1.1 lukem 144: static void mlsname(FILE *, factelem *);
145: static void replydirname(const char *, const char *);
146:
147: struct ftpfact {
148: const char *name; /* name of fact */
149: int enabled; /* if fact is enabled */
150: void (*display)(const char *, FILE *, factelem *);
151: /* function to display fact */
152: };
153:
154: struct ftpfact facttab[] = {
155: { "Type", 1, fact_type },
1.3 lukem 156: #define FACT_TYPE 0
1.1 lukem 157: { "Size", 1, fact_size },
158: { "Modify", 1, fact_modify },
159: { "Perm", 1, fact_perm },
160: { "Unique", 1, fact_unique },
161: /* "Create" */
162: /* "Lang" */
163: /* "Media-Type" */
164: /* "CharSet" */
165: };
166:
1.4 lukem 167: #define FACTTABSIZE (sizeof(facttab) / sizeof(struct ftpfact))
168:
1.4.2.5 ! jmc 169: static char cached_path[MAXPATHLEN + 1] = "/";
! 170: static void discover_path(char *, const char *);
1.1 lukem 171:
172: void
173: cwd(const char *path)
174: {
175:
176: if (chdir(path) < 0)
177: perror_reply(550, path);
178: else {
179: show_chdir_messages(250);
180: ack("CWD");
1.4.2.5 ! jmc 181: if (getcwd(cached_path, MAXPATHLEN) == NULL) {
! 182: discover_path(cached_path, path);
! 183: }
1.1 lukem 184: }
185: }
186:
187: void
188: delete(const char *name)
189: {
190: char *p = NULL;
191:
192: if (remove(name) < 0) {
193: p = strerror(errno);
194: perror_reply(550, name);
195: } else
196: ack("DELE");
1.4.2.3 lukem 197: logxfer("delete", -1, name, NULL, NULL, p);
1.1 lukem 198: }
199:
200: void
201: feat(void)
202: {
203: int i;
204:
1.3 lukem 205: reply(-211, "Features supported");
206: cprintf(stdout, " MDTM\r\n");
207: cprintf(stdout, " MLST ");
1.4 lukem 208: for (i = 0; i < FACTTABSIZE; i++)
1.3 lukem 209: cprintf(stdout, "%s%s;", facttab[i].name,
1.1 lukem 210: facttab[i].enabled ? "*" : "");
1.3 lukem 211: cprintf(stdout, "\r\n");
212: cprintf(stdout, " REST STREAM\r\n");
213: cprintf(stdout, " SIZE\r\n");
214: cprintf(stdout, " TVFS\r\n");
1.1 lukem 215: reply(211, "End");
216: }
217:
218: void
219: makedir(const char *name)
220: {
221: char *p = NULL;
222:
223: if (mkdir(name, 0777) < 0) {
224: p = strerror(errno);
225: perror_reply(550, name);
226: } else
227: replydirname(name, "directory created.");
1.4.2.3 lukem 228: logxfer("mkdir", -1, name, NULL, NULL, p);
1.1 lukem 229: }
230:
231: void
232: mlsd(const char *path)
233: {
1.3 lukem 234: struct dirent *dp;
235: struct stat sb, pdirstat;
1.1 lukem 236: factelem f;
1.3 lukem 237: FILE *dout;
238: DIR *dirp;
239: char name[MAXPATHLEN];
240: int hastypefact;
1.1 lukem 241:
1.3 lukem 242: hastypefact = facttab[FACT_TYPE].enabled;
1.1 lukem 243: if (path == NULL)
244: path = ".";
245: if (stat(path, &pdirstat) == -1) {
246: mlsdperror:
247: perror_reply(550, path);
248: return;
249: }
250: if (! S_ISDIR(pdirstat.st_mode)) {
251: errno = ENOTDIR;
1.3 lukem 252: perror_reply(501, path);
253: return;
1.1 lukem 254: }
1.4.2.5 ! jmc 255: if ((dirp = opendir(path)) == NULL)
! 256: goto mlsdperror;
! 257:
1.1 lukem 258: dout = dataconn("MLSD", (off_t)-1, "w");
259: if (dout == NULL)
260: return;
261:
1.4.2.5 ! jmc 262: memset(&f, 0, sizeof(f));
1.1 lukem 263: f.stat = &sb;
1.4.2.5 ! jmc 264: f.flags |= FE_MLSD;
1.1 lukem 265: while ((dp = readdir(dirp)) != NULL) {
266: snprintf(name, sizeof(name), "%s/%s", path, dp->d_name);
267: if (ISDOTDIR(dp->d_name)) { /* special case curdir: */
1.3 lukem 268: if (! hastypefact)
269: continue;
270: f.pdirstat = NULL; /* require stat of parent */
1.1 lukem 271: f.display = path; /* set name to real name */
1.4.2.5 ! jmc 272: f.flags |= FE_ISCURDIR; /* flag name is curdir */
1.1 lukem 273: } else {
1.3 lukem 274: if (ISDOTDOTDIR(dp->d_name)) {
275: if (! hastypefact)
276: continue;
277: f.pdirstat = NULL;
278: } else
279: f.pdirstat = &pdirstat; /* cache parent stat */
1.1 lukem 280: f.display = dp->d_name;
1.4.2.5 ! jmc 281: f.flags &= ~FE_ISCURDIR;
1.1 lukem 282: }
1.3 lukem 283: if (stat(name, &sb) == -1)
284: continue;
285: f.path = name;
1.1 lukem 286: mlsname(dout, &f);
287: }
288: (void)closedir(dirp);
289:
290: if (ferror(dout) != 0)
291: perror_reply(550, "Data connection");
292: else
293: reply(226, "MLSD complete.");
1.3 lukem 294: closedataconn(dout);
1.1 lukem 295: total_xfers_out++;
296: total_xfers++;
297: }
298:
299: void
300: mlst(const char *path)
301: {
302: struct stat sb;
303: factelem f;
304:
305: if (path == NULL)
306: path = ".";
307: if (stat(path, &sb) == -1) {
308: perror_reply(550, path);
309: return;
310: }
1.3 lukem 311: reply(-250, "MLST %s", path);
1.4.2.5 ! jmc 312: memset(&f, 0, sizeof(f));
1.1 lukem 313: f.path = path;
314: f.display = path;
315: f.stat = &sb;
316: f.pdirstat = NULL;
1.3 lukem 317: CPUTC(' ', stdout);
1.1 lukem 318: mlsname(stdout, &f);
319: reply(250, "End");
320: }
321:
322:
323: void
324: opts(const char *command)
325: {
326: struct tab *c;
327: char *ep;
328:
329: if ((ep = strchr(command, ' ')) != NULL)
330: *ep++ = '\0';
331: c = lookup(cmdtab, command);
332: if (c == NULL) {
1.4.2.5 ! jmc 333: reply(502, "Unknown command '%s'.", command);
1.1 lukem 334: return;
335: }
336: if (! CMD_IMPLEMENTED(c)) {
1.4.2.5 ! jmc 337: reply(502, "%s command not implemented.", c->name);
1.1 lukem 338: return;
339: }
340: if (! CMD_HAS_OPTIONS(c)) {
341: reply(501, "%s command does not support persistent options.",
342: c->name);
343: return;
344: }
345:
346: /* special case: MLST */
347: if (strcasecmp(command, "MLST") == 0) {
1.4 lukem 348: int enabled[FACTTABSIZE];
1.1 lukem 349: int i, onedone;
1.3 lukem 350: size_t len;
1.1 lukem 351: char *p;
352:
1.3 lukem 353: for (i = 0; i < sizeof(enabled) / sizeof(int); i++)
354: enabled[i] = 0;
1.1 lukem 355: if (ep == NULL || *ep == '\0')
356: goto displaymlstopts;
357:
358: /* don't like spaces, and need trailing ; */
1.3 lukem 359: len = strlen(ep);
360: if (strchr(ep, ' ') != NULL || ep[len - 1] != ';') {
361: badmlstopt:
1.1 lukem 362: reply(501, "Invalid MLST options");
363: return;
364: }
1.3 lukem 365: ep[len - 1] = '\0';
1.1 lukem 366: while ((p = strsep(&ep, ";")) != NULL) {
1.3 lukem 367: if (*p == '\0')
368: goto badmlstopt;
1.4 lukem 369: for (i = 0; i < FACTTABSIZE; i++)
1.1 lukem 370: if (strcasecmp(p, facttab[i].name) == 0) {
1.3 lukem 371: enabled[i] = 1;
1.1 lukem 372: break;
373: }
374: }
375:
376: displaymlstopts:
1.4 lukem 377: for (i = 0; i < FACTTABSIZE; i++)
1.3 lukem 378: facttab[i].enabled = enabled[i];
379: cprintf(stdout, "200 MLST OPTS");
1.4 lukem 380: for (i = onedone = 0; i < FACTTABSIZE; i++) {
1.1 lukem 381: if (facttab[i].enabled) {
1.3 lukem 382: cprintf(stdout, "%s%s;", onedone ? "" : " ",
1.1 lukem 383: facttab[i].name);
384: onedone++;
385: }
386: }
1.3 lukem 387: cprintf(stdout, "\r\n");
388: fflush(stdout);
1.1 lukem 389: return;
390: }
391:
392: /* default cases */
393: if (ep != NULL && *ep != '\0')
394: REASSIGN(c->options, xstrdup(ep));
395: if (c->options != NULL)
396: reply(200, "Options for %s are '%s'.", c->name,
397: c->options);
398: else
399: reply(200, "No options defined for %s.", c->name);
400: }
401:
402: void
403: pwd(void)
404: {
405: char path[MAXPATHLEN];
406:
1.4.2.5 ! jmc 407: if (getcwd(path, sizeof(path) - 1) == NULL) {
! 408: if (chdir(cached_path) < 0) {
! 409: reply(550, "Can't get the current directory: %s.",
! 410: strerror(errno));
! 411: return;
! 412: }
! 413: (void)strlcpy(path, cached_path, MAXPATHLEN);
! 414: }
! 415: replydirname(path, "is the current directory.");
1.1 lukem 416: }
417:
418: void
419: removedir(const char *name)
420: {
421: char *p = NULL;
422:
423: if (rmdir(name) < 0) {
424: p = strerror(errno);
425: perror_reply(550, name);
426: } else
427: ack("RMD");
1.4.2.3 lukem 428: logxfer("rmdir", -1, name, NULL, NULL, p);
1.1 lukem 429: }
430:
431: char *
432: renamefrom(const char *name)
433: {
434: struct stat st;
435:
436: if (stat(name, &st) < 0) {
437: perror_reply(550, name);
438: return (NULL);
439: }
440: reply(350, "File exists, ready for destination name");
441: return (xstrdup(name));
442: }
443:
444: void
445: renamecmd(const char *from, const char *to)
446: {
447: char *p = NULL;
448:
449: if (rename(from, to) < 0) {
450: p = strerror(errno);
451: perror_reply(550, "rename");
452: } else
453: ack("RNTO");
1.4.2.3 lukem 454: logxfer("rename", -1, from, to, NULL, p);
1.1 lukem 455: }
456:
457: void
458: sizecmd(const char *filename)
459: {
460: switch (type) {
461: case TYPE_L:
1.4.2.3 lukem 462: case TYPE_I:
463: {
1.1 lukem 464: struct stat stbuf;
465: if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
466: reply(550, "%s: not a plain file.", filename);
467: else
1.4.2.3 lukem 468: reply(213, ULLF, (ULLT)stbuf.st_size);
469: break;
470: }
471: case TYPE_A:
472: {
1.1 lukem 473: FILE *fin;
474: int c;
475: off_t count;
476: struct stat stbuf;
477: fin = fopen(filename, "r");
478: if (fin == NULL) {
479: perror_reply(550, filename);
480: return;
481: }
482: if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
483: reply(550, "%s: not a plain file.", filename);
484: (void) fclose(fin);
485: return;
486: }
1.4.2.5 ! jmc 487: if (stbuf.st_size > 10240) {
! 488: reply(550, "%s: file too large for SIZE.", filename);
! 489: (void) fclose(fin);
! 490: return;
! 491: }
1.1 lukem 492:
493: count = 0;
1.4.2.5 ! jmc 494: while((c = getc(fin)) != EOF) {
1.1 lukem 495: if (c == '\n') /* will get expanded to \r\n */
496: count++;
497: count++;
498: }
499: (void) fclose(fin);
500:
1.4.2.3 lukem 501: reply(213, LLF, (LLT)count);
502: break;
503: }
1.1 lukem 504: default:
505: reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
506: }
507: }
508:
509: void
510: statfilecmd(const char *filename)
511: {
512: FILE *fin;
513: int c;
1.4.2.4 itojun 514: int atstart;
1.1 lukem 515: char *argv[] = { INTERNAL_LS, "-lgA", "", NULL };
516:
517: argv[2] = (char *)filename;
518: fin = ftpd_popen(argv, "r", STDOUT_FILENO);
1.3 lukem 519: reply(-211, "status of %s:", filename);
520: /* XXX: use fgetln() or fparseln() here? */
1.4.2.4 itojun 521: atstart = 1;
1.1 lukem 522: while ((c = getc(fin)) != EOF) {
523: if (c == '\n') {
524: if (ferror(stdout)){
525: perror_reply(421, "control connection");
526: (void) ftpd_pclose(fin);
527: dologout(1);
528: /* NOTREACHED */
529: }
530: if (ferror(fin)) {
531: perror_reply(551, filename);
532: (void) ftpd_pclose(fin);
533: return;
534: }
1.3 lukem 535: CPUTC('\r', stdout);
1.1 lukem 536: }
1.4.2.4 itojun 537: if (atstart && isdigit(c))
538: CPUTC(' ', stdout);
1.3 lukem 539: CPUTC(c, stdout);
1.4.2.4 itojun 540: atstart = (c == '\n');
1.1 lukem 541: }
542: (void) ftpd_pclose(fin);
543: reply(211, "End of Status");
544: }
545:
546: /* -- */
547:
548: static void
549: ack(const char *s)
550: {
551:
552: reply(250, "%s command successful.", s);
553: }
554:
1.3 lukem 555: /*
556: * Encode len bytes starting at clear using base64 encoding into encoded,
557: * which should be at least ((len + 2) * 4 / 3 + 1) in size.
1.4.2.1 thorpej 558: * If nulterm is non-zero, terminate with \0 otherwise pad to 3 byte boundary
559: * with `='.
1.3 lukem 560: */
561: static void
562: base64_encode(const char *clear, size_t len, char *encoded, int nulterm)
563: {
1.4.2.1 thorpej 564: static const char base64[] =
1.3 lukem 565: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1.4.2.1 thorpej 566: const char *c;
567: char *e, termchar;
1.3 lukem 568: int i;
569:
1.4.2.1 thorpej 570: /* determine whether to pad with '=' or NUL terminate */
571: termchar = nulterm ? '\0' : '=';
572: c = clear;
573: e = encoded;
574: /* convert all but last 2 bytes */
575: for (i = len; i > 2; i -= 3, c += 3) {
576: *e++ = base64[(c[0] >> 2) & 0x3f];
577: *e++ = base64[((c[0] << 4) & 0x30) | ((c[1] >> 4) & 0x0f)];
578: *e++ = base64[((c[1] << 2) & 0x3c) | ((c[2] >> 6) & 0x03)];
579: *e++ = base64[(c[2]) & 0x3f];
580: }
581: /* handle slop at end */
582: if (i > 0) {
583: *e++ = base64[(c[0] >> 2) & 0x3f];
584: *e++ = base64[((c[0] << 4) & 0x30) |
585: (i > 1 ? ((c[1] >> 4) & 0x0f) : 0)];
586: *e++ = (i > 1) ? base64[(c[1] << 2) & 0x3c] : termchar;
587: *e++ = termchar;
588: }
589: *e = '\0';
1.3 lukem 590: }
591:
1.1 lukem 592: static void
593: fact_modify(const char *fact, FILE *fd, factelem *fe)
594: {
595: struct tm *t;
596:
597: t = gmtime(&(fe->stat->st_mtime));
1.3 lukem 598: cprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact,
1.1 lukem 599: TM_YEAR_BASE + t->tm_year,
600: t->tm_mon+1, t->tm_mday,
601: t->tm_hour, t->tm_min, t->tm_sec);
602: }
603:
604: static void
605: fact_perm(const char *fact, FILE *fd, factelem *fe)
606: {
1.3 lukem 607: int rok, wok, xok, pdirwok;
1.1 lukem 608: struct stat *pdir;
609:
610: if (fe->stat->st_uid == geteuid()) {
611: rok = ((fe->stat->st_mode & S_IRUSR) != 0);
612: wok = ((fe->stat->st_mode & S_IWUSR) != 0);
613: xok = ((fe->stat->st_mode & S_IXUSR) != 0);
1.3 lukem 614: } else if (matchgroup(fe->stat->st_gid)) {
1.1 lukem 615: rok = ((fe->stat->st_mode & S_IRGRP) != 0);
616: wok = ((fe->stat->st_mode & S_IWGRP) != 0);
617: xok = ((fe->stat->st_mode & S_IXGRP) != 0);
618: } else {
619: rok = ((fe->stat->st_mode & S_IROTH) != 0);
620: wok = ((fe->stat->st_mode & S_IWOTH) != 0);
621: xok = ((fe->stat->st_mode & S_IXOTH) != 0);
622: }
623:
1.3 lukem 624: cprintf(fd, "%s=", fact);
1.1 lukem 625:
626: /*
627: * if parent info not provided, look it up, but
628: * only if the current class has modify rights,
629: * since we only need this info in such a case.
630: */
631: pdir = fe->pdirstat;
1.4.2.3 lukem 632: if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) {
1.1 lukem 633: size_t len;
634: char realdir[MAXPATHLEN], *p;
635: struct stat dir;
636:
637: len = strlcpy(realdir, fe->path, sizeof(realdir));
638: if (len < sizeof(realdir) - 4) {
639: if (S_ISDIR(fe->stat->st_mode))
640: strlcat(realdir, "/..", sizeof(realdir));
641: else {
642: /* if has a /, move back to it */
643: /* otherwise use '..' */
644: if ((p = strrchr(realdir, '/')) != NULL) {
645: if (p == realdir)
646: p++;
647: *p = '\0';
648: } else
649: strlcpy(realdir, "..", sizeof(realdir));
650: }
651: if (stat(realdir, &dir) == 0)
652: pdir = &dir;
653: }
654: }
655: pdirwok = 0;
656: if (pdir != NULL) {
657: if (pdir->st_uid == geteuid())
658: pdirwok = ((pdir->st_mode & S_IWUSR) != 0);
1.3 lukem 659: else if (matchgroup(pdir->st_gid))
1.1 lukem 660: pdirwok = ((pdir->st_mode & S_IWGRP) != 0);
661: else
662: pdirwok = ((pdir->st_mode & S_IWOTH) != 0);
663: }
664:
665: /* 'a': can APPE to file */
1.4.2.3 lukem 666: if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode))
1.3 lukem 667: CPUTC('a', fd);
1.1 lukem 668:
669: /* 'c': can create or append to files in directory */
1.4.2.3 lukem 670: if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
1.3 lukem 671: CPUTC('c', fd);
1.1 lukem 672:
673: /* 'd': can delete file or directory */
1.4.2.3 lukem 674: if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) {
1.1 lukem 675: int candel;
676:
677: candel = 1;
678: if (S_ISDIR(fe->stat->st_mode)) {
679: DIR *dirp;
680: struct dirent *dp;
681:
682: if ((dirp = opendir(fe->display)) == NULL)
683: candel = 0;
684: else {
685: while ((dp = readdir(dirp)) != NULL) {
686: if (ISDOTDIR(dp->d_name) ||
687: ISDOTDOTDIR(dp->d_name))
688: continue;
689: candel = 0;
690: break;
691: }
692: closedir(dirp);
693: }
694: }
695: if (candel)
1.3 lukem 696: CPUTC('d', fd);
1.1 lukem 697: }
698:
699: /* 'e': can enter directory */
700: if (xok && S_ISDIR(fe->stat->st_mode))
1.3 lukem 701: CPUTC('e', fd);
1.1 lukem 702:
703: /* 'f': can rename file or directory */
1.4.2.3 lukem 704: if (pdirwok && CURCLASS_FLAGS_ISSET(modify))
1.3 lukem 705: CPUTC('f', fd);
1.1 lukem 706:
707: /* 'l': can list directory */
708: if (rok && xok && S_ISDIR(fe->stat->st_mode))
1.3 lukem 709: CPUTC('l', fd);
1.1 lukem 710:
711: /* 'm': can create directory */
1.4.2.3 lukem 712: if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
1.3 lukem 713: CPUTC('m', fd);
1.1 lukem 714:
715: /* 'p': can remove files in directory */
1.4.2.3 lukem 716: if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode))
1.3 lukem 717: CPUTC('p', fd);
1.1 lukem 718:
719: /* 'r': can RETR file */
720: if (rok && S_ISREG(fe->stat->st_mode))
1.3 lukem 721: CPUTC('r', fd);
1.1 lukem 722:
723: /* 'w': can STOR file */
1.4.2.3 lukem 724: if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode))
1.3 lukem 725: CPUTC('w', fd);
1.1 lukem 726:
1.3 lukem 727: CPUTC(';', fd);
1.1 lukem 728: }
729:
730: static void
731: fact_size(const char *fact, FILE *fd, factelem *fe)
732: {
733:
734: if (S_ISREG(fe->stat->st_mode))
1.4.2.3 lukem 735: cprintf(fd, "%s=" LLF ";", fact, (LLT)fe->stat->st_size);
1.1 lukem 736: }
737:
738: static void
739: fact_type(const char *fact, FILE *fd, factelem *fe)
740: {
741:
1.3 lukem 742: cprintf(fd, "%s=", fact);
1.1 lukem 743: switch (fe->stat->st_mode & S_IFMT) {
744: case S_IFDIR:
1.4.2.5 ! jmc 745: if (fe->flags & FE_MLSD) {
! 746: if ((fe->flags & FE_ISCURDIR) || ISDOTDIR(fe->display))
! 747: cprintf(fd, "cdir");
! 748: else if (ISDOTDOTDIR(fe->display))
! 749: cprintf(fd, "pdir");
! 750: else
! 751: cprintf(fd, "dir");
! 752: } else {
1.3 lukem 753: cprintf(fd, "dir");
1.4.2.5 ! jmc 754: }
1.1 lukem 755: break;
756: case S_IFREG:
1.3 lukem 757: cprintf(fd, "file");
1.1 lukem 758: break;
759: case S_IFIFO:
1.3 lukem 760: cprintf(fd, "OS.unix=fifo");
1.1 lukem 761: break;
1.3 lukem 762: case S_IFLNK: /* XXX: probably a NO-OP with stat() */
763: cprintf(fd, "OS.unix=slink");
1.1 lukem 764: break;
765: case S_IFSOCK:
1.3 lukem 766: cprintf(fd, "OS.unix=socket");
1.1 lukem 767: break;
768: case S_IFBLK:
769: case S_IFCHR:
1.3 lukem 770: cprintf(fd, "OS.unix=%s-%d/%d",
1.1 lukem 771: S_ISBLK(fe->stat->st_mode) ? "blk" : "chr",
772: major(fe->stat->st_rdev), minor(fe->stat->st_rdev));
773: break;
774: default:
1.3 lukem 775: cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT);
1.1 lukem 776: break;
777: }
1.3 lukem 778: CPUTC(';', fd);
1.1 lukem 779: }
780:
781: static void
782: fact_unique(const char *fact, FILE *fd, factelem *fe)
783: {
1.4.2.1 thorpej 784: char obuf[(sizeof(dev_t) + sizeof(ino_t) + 2) * 4 / 3 + 2];
785: char tbuf[sizeof(dev_t) + sizeof(ino_t)];
1.1 lukem 786:
1.4.2.1 thorpej 787: memcpy(tbuf,
788: (char *)&(fe->stat->st_dev), sizeof(dev_t));
789: memcpy(tbuf + sizeof(dev_t),
790: (char *)&(fe->stat->st_ino), sizeof(ino_t));
791: base64_encode(tbuf, sizeof(dev_t) + sizeof(ino_t), obuf, 1);
792: cprintf(fd, "%s=%s;", fact, obuf);
1.1 lukem 793: }
794:
795: static int
1.3 lukem 796: matchgroup(gid_t gid)
1.1 lukem 797: {
798: int i;
799:
1.3 lukem 800: for (i = 0; i < gidcount; i++)
1.1 lukem 801: if (gid == gidlist[i])
802: return(1);
803: return (0);
804: }
805:
806: static void
807: mlsname(FILE *fp, factelem *fe)
808: {
1.4.2.5 ! jmc 809: char realfile[MAXPATHLEN];
! 810: int i, userf = 0;
1.1 lukem 811:
1.4 lukem 812: for (i = 0; i < FACTTABSIZE; i++) {
1.1 lukem 813: if (facttab[i].enabled)
814: (facttab[i].display)(facttab[i].name, fp, fe);
815: }
1.4.2.5 ! jmc 816: if ((fe->flags & FE_MLSD) &&
! 817: !(fe->flags & FE_ISCURDIR) && !ISDOTDIR(fe->display)) {
! 818: /* if MLSD and not "." entry, display as-is */
! 819: userf = 0;
! 820: } else {
! 821: /* if MLST, or MLSD and "." entry, realpath(3) it */
! 822: if (realpath(fe->display, realfile) != NULL)
! 823: userf = 1;
! 824: }
! 825: cprintf(fp, " %s\r\n", userf ? realfile : fe->display);
1.1 lukem 826: }
827:
828: static void
829: replydirname(const char *name, const char *message)
830: {
1.4.2.2 he 831: char *p, *ep;
1.4.2.3 lukem 832: char npath[MAXPATHLEN * 2];
1.1 lukem 833:
1.4.2.2 he 834: p = npath;
835: ep = &npath[sizeof(npath) - 1];
836: while (*name) {
1.4.2.3 lukem 837: if (*name == '"') {
838: if (ep - p < 2)
839: break;
1.4.2.2 he 840: *p++ = *name++;
841: *p++ = '"';
1.4.2.3 lukem 842: } else {
843: if (ep - p < 1)
844: break;
1.4.2.2 he 845: *p++ = *name++;
1.4.2.3 lukem 846: }
1.1 lukem 847: }
1.4.2.2 he 848: *p = '\0';
1.1 lukem 849: reply(257, "\"%s\" %s", npath, message);
850: }
1.4.2.5 ! jmc 851:
! 852: static void
! 853: discover_path(last_path, new_path)
! 854: char *last_path;
! 855: const char *new_path;
! 856: {
! 857: char tp[MAXPATHLEN + 1] = "";
! 858: char tq[MAXPATHLEN + 1] = "";
! 859: char *cp;
! 860: char *cq;
! 861: int sz1, sz2;
! 862: int nomorelink;
! 863: struct stat st1, st2;
! 864:
! 865: if (new_path[0] != '/') {
! 866: (void)strlcpy(tp, last_path, MAXPATHLEN);
! 867: (void)strlcat(tp, "/", MAXPATHLEN);
! 868: }
! 869: (void)strlcat(tp, new_path, MAXPATHLEN);
! 870: (void)strlcat(tp, "/", MAXPATHLEN);
! 871:
! 872: /*
! 873: * resolve symlinks. A symlink may introduce another symlink, so we
! 874: * loop trying to resolve symlinks until we don't find any of them.
! 875: */
! 876: do {
! 877: /* Collapse any // into / */
! 878: while ((cp = strstr(tp, "//")) != NULL)
! 879: (void)memmove(cp, cp + 1, strlen(cp) - 1 + 1);
! 880:
! 881: /* Collapse any /./ into / */
! 882: while ((cp = strstr(tp, "/./")) != NULL)
! 883: (void)memmove(cp, cp + 2, strlen(cp) - 2 + 1);
! 884:
! 885: cp = tp;
! 886: nomorelink = 1;
! 887:
! 888: while ((cp = strstr(++cp, "/")) != NULL) {
! 889: sz1 = (u_long)cp - (u_long)tp;
! 890: if (sz1 > MAXPATHLEN)
! 891: goto bad;
! 892: *cp = 0;
! 893: sz2 = readlink(tp, tq, MAXPATHLEN);
! 894: *cp = '/';
! 895:
! 896: /* If this is not a symlink, move to next / */
! 897: if (sz2 <= 0)
! 898: continue;
! 899:
! 900: /*
! 901: * We found a symlink, so we will have to
! 902: * do one more pass to check there is no
! 903: * more symlink in the path
! 904: */
! 905: nomorelink = 0;
! 906:
! 907: /*
! 908: * Null terminate the string and remove trailing /
! 909: */
! 910: tq[sz2] = 0;
! 911: sz2 = strlen(tq);
! 912: if (tq[sz2 - 1] == '/')
! 913: tq[--sz2] = 0;
! 914:
! 915: /*
! 916: * Is this an absolute link or a relative link?
! 917: */
! 918: if (tq[0] == '/') {
! 919: /* absolute link */
! 920: if (strlen(cp) + sz2 > MAXPATHLEN)
! 921: goto bad;
! 922: memmove(tp + sz2, cp, strlen(cp) + 1);
! 923: memcpy(tp, tq, sz2);
! 924: } else {
! 925: /* relative link */
! 926: for (cq = cp - 1; *cq != '/'; cq--);
! 927: if (strlen(tp) - ((u_long)cq - (u_long)cp)
! 928: + 1 + sz2 > MAXPATHLEN)
! 929: goto bad;
! 930: (void)memmove(cq + 1 + sz2,
! 931: cp, strlen(cp) + 1);
! 932: (void)memcpy(cq + 1, tq, sz2);
! 933: }
! 934:
! 935: /*
! 936: * start over, looking for new symlinks
! 937: */
! 938: break;
! 939: }
! 940: } while (nomorelink == 0);
! 941:
! 942: /* Collapse any /foo/../ into /foo/ */
! 943: while ((cp = strstr(tp, "/../")) != NULL) {
! 944: /* ^/../foo/ becomes ^/foo/ */
! 945: if (cp == tp) {
! 946: (void)memmove(cp, cp + 3,
! 947: strlen(cp) - 3 + 1);
! 948: } else {
! 949: for (cq = cp - 1; *cq != '/'; cq--);
! 950: (void)memmove(cq, cp + 3,
! 951: strlen(cp) - 3 + 1);
! 952: }
! 953: }
! 954:
! 955: /* strip strailing / */
! 956: if (strlen(tp) != 1)
! 957: tp[strlen(tp) - 1] = '\0';
! 958:
! 959: /* check that the path is correct */
! 960: stat(tp, &st1);
! 961: stat(".", &st2);
! 962: if ((st1.st_dev != st2.st_dev) || (st1.st_ino != st2.st_ino))
! 963: goto bad;
! 964:
! 965: (void)strlcpy(last_path, tp, MAXPATHLEN);
! 966: return;
! 967:
! 968: bad:
! 969: (void)strlcat(last_path, "/", MAXPATHLEN);
! 970: (void)strlcat(last_path, new_path, MAXPATHLEN);
! 971: return;
! 972: }
! 973:
CVSweb <webmaster@jp.NetBSD.org>