cmds.c revision 108746
1/* $NetBSD: cmds.c,v 1.18 2002/10/12 08:35:16 darrenr Exp $ */ 2 3/* 4 * Copyright (c) 1999-2001 The NetBSD Foundation, Inc. 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. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed by the University of 54 * California, Berkeley and its contributors. 55 * 4. Neither the name of the University nor the names of its contributors 56 * may be used to endorse or promote products derived from this software 57 * without specific prior written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 * SUCH DAMAGE. 70 */ 71 72/* 73 * Copyright (C) 1997 and 1998 WIDE Project. 74 * All rights reserved. 75 * 76 * Redistribution and use in source and binary forms, with or without 77 * modification, are permitted provided that the following conditions 78 * are met: 79 * 1. Redistributions of source code must retain the above copyright 80 * notice, this list of conditions and the following disclaimer. 81 * 2. Redistributions in binary form must reproduce the above copyright 82 * notice, this list of conditions and the following disclaimer in the 83 * documentation and/or other materials provided with the distribution. 84 * 3. Neither the name of the project nor the names of its contributors 85 * may be used to endorse or promote products derived from this software 86 * without specific prior written permission. 87 * 88 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 89 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 90 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 91 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 92 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 93 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 94 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 95 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 96 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 97 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 98 * SUCH DAMAGE. 99 */ 100 101 102#include <sys/cdefs.h> 103#ifndef lint 104__RCSID("$NetBSD: cmds.c,v 1.18 2002/10/12 08:35:16 darrenr Exp $"); 105#endif /* not lint */ 106 107#include <sys/param.h> 108#include <sys/stat.h> 109 110#include <arpa/ftp.h> 111 112#include <dirent.h> 113#include <errno.h> 114#include <setjmp.h> 115#include <stdio.h> 116#include <stdlib.h> 117#include <string.h> 118#include <tzfile.h> 119#include <unistd.h> 120#include <ctype.h> 121 122#ifdef KERBEROS5 123#include <krb5/krb5.h> 124#endif 125 126#include "extern.h" 127 128typedef enum { 129 FE_MLSD = 1<<0, /* if op is MLSD (MLST otherwise ) */ 130 FE_ISCURDIR = 1<<1, /* if name is the current directory */ 131} factflag_t; 132 133typedef struct { 134 const char *path; /* full pathname */ 135 const char *display; /* name to display */ 136 struct stat *stat; /* stat of path */ 137 struct stat *pdirstat; /* stat of path's parent dir */ 138 factflag_t flags; /* flags */ 139} factelem; 140 141static void ack(const char *); 142static void base64_encode(const char *, size_t, char *, int); 143static void fact_type(const char *, FILE *, factelem *); 144static void fact_size(const char *, FILE *, factelem *); 145static void fact_modify(const char *, FILE *, factelem *); 146static void fact_perm(const char *, FILE *, factelem *); 147static void fact_unique(const char *, FILE *, factelem *); 148static int matchgroup(gid_t); 149static void mlsname(FILE *, factelem *); 150static void replydirname(const char *, const char *); 151 152struct ftpfact { 153 const char *name; /* name of fact */ 154 int enabled; /* if fact is enabled */ 155 void (*display)(const char *, FILE *, factelem *); 156 /* function to display fact */ 157}; 158 159struct ftpfact facttab[] = { 160 { "Type", 1, fact_type }, 161#define FACT_TYPE 0 162 { "Size", 1, fact_size }, 163 { "Modify", 1, fact_modify }, 164 { "Perm", 1, fact_perm }, 165 { "Unique", 1, fact_unique }, 166 /* "Create" */ 167 /* "Lang" */ 168 /* "Media-Type" */ 169 /* "CharSet" */ 170}; 171 172#define FACTTABSIZE (sizeof(facttab) / sizeof(struct ftpfact)) 173 174 175void 176cwd(const char *path) 177{ 178 179 if (chdir(path) < 0) 180 perror_reply(550, path); 181 else { 182 show_chdir_messages(250); 183 ack("CWD"); 184 } 185} 186 187void 188delete(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"); 197 logxfer("delete", -1, name, NULL, NULL, p); 198} 199 200void 201feat(void) 202{ 203 int i; 204 205 reply(-211, "Features supported"); 206 cprintf(stdout, " MDTM\r\n"); 207 cprintf(stdout, " MLST "); 208 for (i = 0; i < FACTTABSIZE; i++) 209 cprintf(stdout, "%s%s;", facttab[i].name, 210 facttab[i].enabled ? "*" : ""); 211 cprintf(stdout, "\r\n"); 212 cprintf(stdout, " REST STREAM\r\n"); 213 cprintf(stdout, " SIZE\r\n"); 214 cprintf(stdout, " TVFS\r\n"); 215 reply(211, "End"); 216} 217 218void 219makedir(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."); 228 logxfer("mkdir", -1, name, NULL, NULL, p); 229} 230 231void 232mlsd(const char *path) 233{ 234 struct dirent *dp; 235 struct stat sb, pdirstat; 236 factelem f; 237 FILE *dout; 238 DIR *dirp; 239 char name[MAXPATHLEN]; 240 int hastypefact; 241 242 hastypefact = facttab[FACT_TYPE].enabled; 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; 252 perror_reply(501, path); 253 return; 254 } 255 if ((dirp = opendir(path)) == NULL) 256 goto mlsdperror; 257 258 dout = dataconn("MLSD", (off_t)-1, "w"); 259 if (dout == NULL) 260 return; 261 262 memset(&f, 0, sizeof(f)); 263 f.stat = &sb; 264 f.flags |= FE_MLSD; 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: */ 268 if (! hastypefact) 269 continue; 270 f.pdirstat = NULL; /* require stat of parent */ 271 f.display = path; /* set name to real name */ 272 f.flags |= FE_ISCURDIR; /* flag name is curdir */ 273 } else { 274 if (ISDOTDOTDIR(dp->d_name)) { 275 if (! hastypefact) 276 continue; 277 f.pdirstat = NULL; 278 } else 279 f.pdirstat = &pdirstat; /* cache parent stat */ 280 f.display = dp->d_name; 281 f.flags &= ~FE_ISCURDIR; 282 } 283 if (stat(name, &sb) == -1) 284 continue; 285 f.path = name; 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."); 294 closedataconn(dout); 295 total_xfers_out++; 296 total_xfers++; 297} 298 299void 300mlst(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 } 311 reply(-250, "MLST %s", path); 312 memset(&f, 0, sizeof(f)); 313 f.path = path; 314 f.display = path; 315 f.stat = &sb; 316 f.pdirstat = NULL; 317 CPUTC(' ', stdout); 318 mlsname(stdout, &f); 319 reply(250, "End"); 320} 321 322 323void 324opts(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) { 333 reply(502, "Unknown command '%s'.", command); 334 return; 335 } 336 if (! CMD_IMPLEMENTED(c)) { 337 reply(502, "%s command not implemented.", c->name); 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) { 348 int enabled[FACTTABSIZE]; 349 int i, onedone; 350 size_t len; 351 char *p; 352 353 for (i = 0; i < sizeof(enabled) / sizeof(int); i++) 354 enabled[i] = 0; 355 if (ep == NULL || *ep == '\0') 356 goto displaymlstopts; 357 358 /* don't like spaces, and need trailing ; */ 359 len = strlen(ep); 360 if (strchr(ep, ' ') != NULL || ep[len - 1] != ';') { 361 badmlstopt: 362 reply(501, "Invalid MLST options"); 363 return; 364 } 365 ep[len - 1] = '\0'; 366 while ((p = strsep(&ep, ";")) != NULL) { 367 if (*p == '\0') 368 goto badmlstopt; 369 for (i = 0; i < FACTTABSIZE; i++) 370 if (strcasecmp(p, facttab[i].name) == 0) { 371 enabled[i] = 1; 372 break; 373 } 374 } 375 376 displaymlstopts: 377 for (i = 0; i < FACTTABSIZE; i++) 378 facttab[i].enabled = enabled[i]; 379 cprintf(stdout, "200 MLST OPTS"); 380 for (i = onedone = 0; i < FACTTABSIZE; i++) { 381 if (facttab[i].enabled) { 382 cprintf(stdout, "%s%s;", onedone ? "" : " ", 383 facttab[i].name); 384 onedone++; 385 } 386 } 387 cprintf(stdout, "\r\n"); 388 fflush(stdout); 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 402void 403pwd(void) 404{ 405 char path[MAXPATHLEN]; 406 407 if (getcwd(path, sizeof(path) - 1) == NULL) 408 reply(550, "Can't get the current directory: %s.", 409 strerror(errno)); 410 else 411 replydirname(path, "is the current directory."); 412} 413 414void 415removedir(const char *name) 416{ 417 char *p = NULL; 418 419 if (rmdir(name) < 0) { 420 p = strerror(errno); 421 perror_reply(550, name); 422 } else 423 ack("RMD"); 424 logxfer("rmdir", -1, name, NULL, NULL, p); 425} 426 427char * 428renamefrom(const char *name) 429{ 430 struct stat st; 431 432 if (stat(name, &st) < 0) { 433 perror_reply(550, name); 434 return (NULL); 435 } 436 reply(350, "File exists, ready for destination name"); 437 return (xstrdup(name)); 438} 439 440void 441renamecmd(const char *from, const char *to) 442{ 443 char *p = NULL; 444 445 if (rename(from, to) < 0) { 446 p = strerror(errno); 447 perror_reply(550, "rename"); 448 } else 449 ack("RNTO"); 450 logxfer("rename", -1, from, to, NULL, p); 451} 452 453void 454sizecmd(const char *filename) 455{ 456 switch (type) { 457 case TYPE_L: 458 case TYPE_I: 459 { 460 struct stat stbuf; 461 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 462 reply(550, "%s: not a plain file.", filename); 463 else 464 reply(213, ULLF, (ULLT)stbuf.st_size); 465 break; 466 } 467 case TYPE_A: 468 { 469 FILE *fin; 470 int c; 471 off_t count; 472 struct stat stbuf; 473 fin = fopen(filename, "r"); 474 if (fin == NULL) { 475 perror_reply(550, filename); 476 return; 477 } 478 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 479 reply(550, "%s: not a plain file.", filename); 480 (void) fclose(fin); 481 return; 482 } 483 if (stbuf.st_size > 10240) { 484 reply(550, "%s: file too large for SIZE.", filename); 485 (void) fclose(fin); 486 return; 487 } 488 489 count = 0; 490 while((c = getc(fin)) != EOF) { 491 if (c == '\n') /* will get expanded to \r\n */ 492 count++; 493 count++; 494 } 495 (void) fclose(fin); 496 497 reply(213, LLF, (LLT)count); 498 break; 499 } 500 default: 501 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 502 } 503} 504 505void 506statfilecmd(const char *filename) 507{ 508 FILE *fin; 509 int c; 510 int atstart; 511 char *argv[] = { INTERNAL_LS, "-lgA", "", NULL }; 512 513 argv[2] = (char *)filename; 514 fin = ftpd_popen(argv, "r", STDOUT_FILENO); 515 reply(-211, "status of %s:", filename); 516/* XXX: use fgetln() or fparseln() here? */ 517 atstart = 1; 518 while ((c = getc(fin)) != EOF) { 519 if (c == '\n') { 520 if (ferror(stdout)){ 521 perror_reply(421, "control connection"); 522 (void) ftpd_pclose(fin); 523 dologout(1); 524 /* NOTREACHED */ 525 } 526 if (ferror(fin)) { 527 perror_reply(551, filename); 528 (void) ftpd_pclose(fin); 529 return; 530 } 531 CPUTC('\r', stdout); 532 } 533 if (atstart && isdigit(c)) 534 CPUTC(' ', stdout); 535 CPUTC(c, stdout); 536 atstart = (c == '\n'); 537 } 538 (void) ftpd_pclose(fin); 539 reply(211, "End of Status"); 540} 541 542/* -- */ 543 544static void 545ack(const char *s) 546{ 547 548 reply(250, "%s command successful.", s); 549} 550 551/* 552 * Encode len bytes starting at clear using base64 encoding into encoded, 553 * which should be at least ((len + 2) * 4 / 3 + 1) in size. 554 * If nulterm is non-zero, terminate with \0 otherwise pad to 3 byte boundary 555 * with `='. 556 */ 557static void 558base64_encode(const char *clear, size_t len, char *encoded, int nulterm) 559{ 560 static const char base64[] = 561 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 562 const char *c; 563 char *e, termchar; 564 int i; 565 566 /* determine whether to pad with '=' or NUL terminate */ 567 termchar = nulterm ? '\0' : '='; 568 c = clear; 569 e = encoded; 570 /* convert all but last 2 bytes */ 571 for (i = len; i > 2; i -= 3, c += 3) { 572 *e++ = base64[(c[0] >> 2) & 0x3f]; 573 *e++ = base64[((c[0] << 4) & 0x30) | ((c[1] >> 4) & 0x0f)]; 574 *e++ = base64[((c[1] << 2) & 0x3c) | ((c[2] >> 6) & 0x03)]; 575 *e++ = base64[(c[2]) & 0x3f]; 576 } 577 /* handle slop at end */ 578 if (i > 0) { 579 *e++ = base64[(c[0] >> 2) & 0x3f]; 580 *e++ = base64[((c[0] << 4) & 0x30) | 581 (i > 1 ? ((c[1] >> 4) & 0x0f) : 0)]; 582 *e++ = (i > 1) ? base64[(c[1] << 2) & 0x3c] : termchar; 583 *e++ = termchar; 584 } 585 *e = '\0'; 586} 587 588static void 589fact_modify(const char *fact, FILE *fd, factelem *fe) 590{ 591 struct tm *t; 592 593 t = gmtime(&(fe->stat->st_mtime)); 594 cprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact, 595 TM_YEAR_BASE + t->tm_year, 596 t->tm_mon+1, t->tm_mday, 597 t->tm_hour, t->tm_min, t->tm_sec); 598} 599 600static void 601fact_perm(const char *fact, FILE *fd, factelem *fe) 602{ 603 int rok, wok, xok, pdirwok; 604 struct stat *pdir; 605 606 if (fe->stat->st_uid == geteuid()) { 607 rok = ((fe->stat->st_mode & S_IRUSR) != 0); 608 wok = ((fe->stat->st_mode & S_IWUSR) != 0); 609 xok = ((fe->stat->st_mode & S_IXUSR) != 0); 610 } else if (matchgroup(fe->stat->st_gid)) { 611 rok = ((fe->stat->st_mode & S_IRGRP) != 0); 612 wok = ((fe->stat->st_mode & S_IWGRP) != 0); 613 xok = ((fe->stat->st_mode & S_IXGRP) != 0); 614 } else { 615 rok = ((fe->stat->st_mode & S_IROTH) != 0); 616 wok = ((fe->stat->st_mode & S_IWOTH) != 0); 617 xok = ((fe->stat->st_mode & S_IXOTH) != 0); 618 } 619 620 cprintf(fd, "%s=", fact); 621 622 /* 623 * if parent info not provided, look it up, but 624 * only if the current class has modify rights, 625 * since we only need this info in such a case. 626 */ 627 pdir = fe->pdirstat; 628 if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) { 629 size_t len; 630 char realdir[MAXPATHLEN], *p; 631 struct stat dir; 632 633 len = strlcpy(realdir, fe->path, sizeof(realdir)); 634 if (len < sizeof(realdir) - 4) { 635 if (S_ISDIR(fe->stat->st_mode)) 636 strlcat(realdir, "/..", sizeof(realdir)); 637 else { 638 /* if has a /, move back to it */ 639 /* otherwise use '..' */ 640 if ((p = strrchr(realdir, '/')) != NULL) { 641 if (p == realdir) 642 p++; 643 *p = '\0'; 644 } else 645 strlcpy(realdir, "..", sizeof(realdir)); 646 } 647 if (stat(realdir, &dir) == 0) 648 pdir = &dir; 649 } 650 } 651 pdirwok = 0; 652 if (pdir != NULL) { 653 if (pdir->st_uid == geteuid()) 654 pdirwok = ((pdir->st_mode & S_IWUSR) != 0); 655 else if (matchgroup(pdir->st_gid)) 656 pdirwok = ((pdir->st_mode & S_IWGRP) != 0); 657 else 658 pdirwok = ((pdir->st_mode & S_IWOTH) != 0); 659 } 660 661 /* 'a': can APPE to file */ 662 if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 663 CPUTC('a', fd); 664 665 /* 'c': can create or append to files in directory */ 666 if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 667 CPUTC('c', fd); 668 669 /* 'd': can delete file or directory */ 670 if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) { 671 int candel; 672 673 candel = 1; 674 if (S_ISDIR(fe->stat->st_mode)) { 675 DIR *dirp; 676 struct dirent *dp; 677 678 if ((dirp = opendir(fe->display)) == NULL) 679 candel = 0; 680 else { 681 while ((dp = readdir(dirp)) != NULL) { 682 if (ISDOTDIR(dp->d_name) || 683 ISDOTDOTDIR(dp->d_name)) 684 continue; 685 candel = 0; 686 break; 687 } 688 closedir(dirp); 689 } 690 } 691 if (candel) 692 CPUTC('d', fd); 693 } 694 695 /* 'e': can enter directory */ 696 if (xok && S_ISDIR(fe->stat->st_mode)) 697 CPUTC('e', fd); 698 699 /* 'f': can rename file or directory */ 700 if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) 701 CPUTC('f', fd); 702 703 /* 'l': can list directory */ 704 if (rok && xok && S_ISDIR(fe->stat->st_mode)) 705 CPUTC('l', fd); 706 707 /* 'm': can create directory */ 708 if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 709 CPUTC('m', fd); 710 711 /* 'p': can remove files in directory */ 712 if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 713 CPUTC('p', fd); 714 715 /* 'r': can RETR file */ 716 if (rok && S_ISREG(fe->stat->st_mode)) 717 CPUTC('r', fd); 718 719 /* 'w': can STOR file */ 720 if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 721 CPUTC('w', fd); 722 723 CPUTC(';', fd); 724} 725 726static void 727fact_size(const char *fact, FILE *fd, factelem *fe) 728{ 729 730 if (S_ISREG(fe->stat->st_mode)) 731 cprintf(fd, "%s=" LLF ";", fact, (LLT)fe->stat->st_size); 732} 733 734static void 735fact_type(const char *fact, FILE *fd, factelem *fe) 736{ 737 738 cprintf(fd, "%s=", fact); 739 switch (fe->stat->st_mode & S_IFMT) { 740 case S_IFDIR: 741 if (fe->flags & FE_MLSD) { 742 if ((fe->flags & FE_ISCURDIR) || ISDOTDIR(fe->display)) 743 cprintf(fd, "cdir"); 744 else if (ISDOTDOTDIR(fe->display)) 745 cprintf(fd, "pdir"); 746 else 747 cprintf(fd, "dir"); 748 } else { 749 cprintf(fd, "dir"); 750 } 751 break; 752 case S_IFREG: 753 cprintf(fd, "file"); 754 break; 755 case S_IFIFO: 756 cprintf(fd, "OS.unix=fifo"); 757 break; 758 case S_IFLNK: /* XXX: probably a NO-OP with stat() */ 759 cprintf(fd, "OS.unix=slink"); 760 break; 761 case S_IFSOCK: 762 cprintf(fd, "OS.unix=socket"); 763 break; 764 case S_IFBLK: 765 case S_IFCHR: 766 cprintf(fd, "OS.unix=%s-%d/%d", 767 S_ISBLK(fe->stat->st_mode) ? "blk" : "chr", 768 major(fe->stat->st_rdev), minor(fe->stat->st_rdev)); 769 break; 770 default: 771 cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT); 772 break; 773 } 774 CPUTC(';', fd); 775} 776 777static void 778fact_unique(const char *fact, FILE *fd, factelem *fe) 779{ 780 char obuf[(sizeof(dev_t) + sizeof(ino_t) + 2) * 4 / 3 + 2]; 781 char tbuf[sizeof(dev_t) + sizeof(ino_t)]; 782 783 memcpy(tbuf, 784 (char *)&(fe->stat->st_dev), sizeof(dev_t)); 785 memcpy(tbuf + sizeof(dev_t), 786 (char *)&(fe->stat->st_ino), sizeof(ino_t)); 787 base64_encode(tbuf, sizeof(dev_t) + sizeof(ino_t), obuf, 1); 788 cprintf(fd, "%s=%s;", fact, obuf); 789} 790 791static int 792matchgroup(gid_t gid) 793{ 794 int i; 795 796 for (i = 0; i < gidcount; i++) 797 if (gid == gidlist[i]) 798 return(1); 799 return (0); 800} 801 802static void 803mlsname(FILE *fp, factelem *fe) 804{ 805 char realfile[MAXPATHLEN]; 806 int i, userf; 807 808 for (i = 0; i < FACTTABSIZE; i++) { 809 if (facttab[i].enabled) 810 (facttab[i].display)(facttab[i].name, fp, fe); 811 } 812 if ((fe->flags & FE_MLSD) && 813 !(fe->flags & FE_ISCURDIR) && !ISDOTDIR(fe->display)) { 814 /* if MLSD and not "." entry, display as-is */ 815 userf = 0; 816 } else { 817 /* if MLST, or MLSD and "." entry, realpath(3) it */ 818 if (realpath(fe->display, realfile) != NULL) 819 userf = 1; 820 } 821 cprintf(fp, " %s\r\n", userf ? realfile : fe->display); 822} 823 824static void 825replydirname(const char *name, const char *message) 826{ 827 char *p, *ep; 828 char npath[MAXPATHLEN * 2]; 829 830 p = npath; 831 ep = &npath[sizeof(npath) - 1]; 832 while (*name) { 833 if (*name == '"') { 834 if (ep - p < 2) 835 break; 836 *p++ = *name++; 837 *p++ = '"'; 838 } else { 839 if (ep - p < 1) 840 break; 841 *p++ = *name++; 842 } 843 } 844 *p = '\0'; 845 reply(257, "\"%s\" %s", npath, message); 846} 847