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