1 /* $NetBSD: cmds.c,v 1.14 2002/01/17 05:21:47 itojun 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.14 2002/01/17 05:21:47 itojun 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 121 #ifdef KERBEROS5 122 #include <krb5/krb5.h> 123 #endif 124 125 #include "extern.h" 126 127 typedef struct { 128 const char *path; /* full pathname */ 129 const char *display; /* name to display */ 130 struct stat *stat; /* stat of path */ 131 struct stat *pdirstat; /* stat of path's parent dir */ 132 int iscurdir; /* nonzero if name is the current dir */ 133 } factelem; 134 135 static void ack(const char *); 136 static void base64_encode(const char *, size_t, char *, int); 137 static void fact_type(const char *, FILE *, factelem *); 138 static void fact_size(const char *, FILE *, factelem *); 139 static void fact_modify(const char *, FILE *, factelem *); 140 static void fact_perm(const char *, FILE *, factelem *); 141 static void fact_unique(const char *, FILE *, factelem *); 142 static int matchgroup(gid_t); 143 static void mlsname(FILE *, factelem *); 144 static void replydirname(const char *, const char *); 145 146 struct ftpfact { 147 const char *name; /* name of fact */ 148 int enabled; /* if fact is enabled */ 149 void (*display)(const char *, FILE *, factelem *); 150 /* function to display fact */ 151 }; 152 153 struct ftpfact facttab[] = { 154 { "Type", 1, fact_type }, 155 #define FACT_TYPE 0 156 { "Size", 1, fact_size }, 157 { "Modify", 1, fact_modify }, 158 { "Perm", 1, fact_perm }, 159 { "Unique", 1, fact_unique }, 160 /* "Create" */ 161 /* "Lang" */ 162 /* "Media-Type" */ 163 /* "CharSet" */ 164 }; 165 166 #define FACTTABSIZE (sizeof(facttab) / sizeof(struct ftpfact)) 167 168 169 void 170 cwd(const char *path) 171 { 172 173 if (chdir(path) < 0) 174 perror_reply(550, path); 175 else { 176 show_chdir_messages(250); 177 ack("CWD"); 178 } 179 } 180 181 void 182 delete(const char *name) 183 { 184 char *p = NULL; 185 186 if (remove(name) < 0) { 187 p = strerror(errno); 188 perror_reply(550, name); 189 } else 190 ack("DELE"); 191 logxfer("delete", -1, name, NULL, NULL, p); 192 } 193 194 void 195 feat(void) 196 { 197 int i; 198 199 reply(-211, "Features supported"); 200 cprintf(stdout, " MDTM\r\n"); 201 cprintf(stdout, " MLST "); 202 for (i = 0; i < FACTTABSIZE; i++) 203 cprintf(stdout, "%s%s;", facttab[i].name, 204 facttab[i].enabled ? "*" : ""); 205 cprintf(stdout, "\r\n"); 206 cprintf(stdout, " REST STREAM\r\n"); 207 cprintf(stdout, " SIZE\r\n"); 208 cprintf(stdout, " TVFS\r\n"); 209 reply(211, "End"); 210 } 211 212 void 213 makedir(const char *name) 214 { 215 char *p = NULL; 216 217 if (mkdir(name, 0777) < 0) { 218 p = strerror(errno); 219 perror_reply(550, name); 220 } else 221 replydirname(name, "directory created."); 222 logxfer("mkdir", -1, name, NULL, NULL, p); 223 } 224 225 void 226 mlsd(const char *path) 227 { 228 struct dirent *dp; 229 struct stat sb, pdirstat; 230 factelem f; 231 FILE *dout; 232 DIR *dirp; 233 char name[MAXPATHLEN]; 234 int hastypefact; 235 236 hastypefact = facttab[FACT_TYPE].enabled; 237 if (path == NULL) 238 path = "."; 239 if (stat(path, &pdirstat) == -1) { 240 mlsdperror: 241 perror_reply(550, path); 242 return; 243 } 244 if (! S_ISDIR(pdirstat.st_mode)) { 245 errno = ENOTDIR; 246 perror_reply(501, path); 247 return; 248 } 249 dout = dataconn("MLSD", (off_t)-1, "w"); 250 if (dout == NULL) 251 return; 252 253 if ((dirp = opendir(path)) == NULL) 254 goto mlsdperror; 255 f.stat = &sb; 256 while ((dp = readdir(dirp)) != NULL) { 257 snprintf(name, sizeof(name), "%s/%s", path, dp->d_name); 258 if (ISDOTDIR(dp->d_name)) { /* special case curdir: */ 259 if (! hastypefact) 260 continue; 261 f.pdirstat = NULL; /* require stat of parent */ 262 f.display = path; /* set name to real name */ 263 f.iscurdir = 1; /* flag name is curdir */ 264 } else { 265 if (ISDOTDOTDIR(dp->d_name)) { 266 if (! hastypefact) 267 continue; 268 f.pdirstat = NULL; 269 } else 270 f.pdirstat = &pdirstat; /* cache parent stat */ 271 f.display = dp->d_name; 272 f.iscurdir = 0; 273 } 274 if (stat(name, &sb) == -1) 275 continue; 276 f.path = name; 277 mlsname(dout, &f); 278 } 279 (void)closedir(dirp); 280 281 if (ferror(dout) != 0) 282 perror_reply(550, "Data connection"); 283 else 284 reply(226, "MLSD complete."); 285 closedataconn(dout); 286 total_xfers_out++; 287 total_xfers++; 288 } 289 290 void 291 mlst(const char *path) 292 { 293 struct stat sb; 294 factelem f; 295 296 if (path == NULL) 297 path = "."; 298 if (stat(path, &sb) == -1) { 299 perror_reply(550, path); 300 return; 301 } 302 reply(-250, "MLST %s", path); 303 f.path = path; 304 f.display = path; 305 f.stat = &sb; 306 f.pdirstat = NULL; 307 f.iscurdir = 0; 308 CPUTC(' ', stdout); 309 mlsname(stdout, &f); 310 reply(250, "End"); 311 } 312 313 314 void 315 opts(const char *command) 316 { 317 struct tab *c; 318 char *ep; 319 320 if ((ep = strchr(command, ' ')) != NULL) 321 *ep++ = '\0'; 322 c = lookup(cmdtab, command); 323 if (c == NULL) { 324 reply(502, "Unknown command %s.", command); 325 return; 326 } 327 if (! CMD_IMPLEMENTED(c)) { 328 reply(501, "%s command not implemented.", c->name); 329 return; 330 } 331 if (! CMD_HAS_OPTIONS(c)) { 332 reply(501, "%s command does not support persistent options.", 333 c->name); 334 return; 335 } 336 337 /* special case: MLST */ 338 if (strcasecmp(command, "MLST") == 0) { 339 int enabled[FACTTABSIZE]; 340 int i, onedone; 341 size_t len; 342 char *p; 343 344 for (i = 0; i < sizeof(enabled) / sizeof(int); i++) 345 enabled[i] = 0; 346 if (ep == NULL || *ep == '\0') 347 goto displaymlstopts; 348 349 /* don't like spaces, and need trailing ; */ 350 len = strlen(ep); 351 if (strchr(ep, ' ') != NULL || ep[len - 1] != ';') { 352 badmlstopt: 353 reply(501, "Invalid MLST options"); 354 return; 355 } 356 ep[len - 1] = '\0'; 357 while ((p = strsep(&ep, ";")) != NULL) { 358 if (*p == '\0') 359 goto badmlstopt; 360 for (i = 0; i < FACTTABSIZE; i++) 361 if (strcasecmp(p, facttab[i].name) == 0) { 362 enabled[i] = 1; 363 break; 364 } 365 } 366 367 displaymlstopts: 368 for (i = 0; i < FACTTABSIZE; i++) 369 facttab[i].enabled = enabled[i]; 370 cprintf(stdout, "200 MLST OPTS"); 371 for (i = onedone = 0; i < FACTTABSIZE; i++) { 372 if (facttab[i].enabled) { 373 cprintf(stdout, "%s%s;", onedone ? "" : " ", 374 facttab[i].name); 375 onedone++; 376 } 377 } 378 cprintf(stdout, "\r\n"); 379 fflush(stdout); 380 return; 381 } 382 383 /* default cases */ 384 if (ep != NULL && *ep != '\0') 385 REASSIGN(c->options, xstrdup(ep)); 386 if (c->options != NULL) 387 reply(200, "Options for %s are '%s'.", c->name, 388 c->options); 389 else 390 reply(200, "No options defined for %s.", c->name); 391 } 392 393 void 394 pwd(void) 395 { 396 char path[MAXPATHLEN]; 397 398 if (getcwd(path, sizeof(path) - 1) == NULL) 399 reply(550, "Can't get the current directory: %s.", 400 strerror(errno)); 401 else 402 replydirname(path, "is the current directory."); 403 } 404 405 void 406 removedir(const char *name) 407 { 408 char *p = NULL; 409 410 if (rmdir(name) < 0) { 411 p = strerror(errno); 412 perror_reply(550, name); 413 } else 414 ack("RMD"); 415 logxfer("rmdir", -1, name, NULL, NULL, p); 416 } 417 418 char * 419 renamefrom(const char *name) 420 { 421 struct stat st; 422 423 if (stat(name, &st) < 0) { 424 perror_reply(550, name); 425 return (NULL); 426 } 427 reply(350, "File exists, ready for destination name"); 428 return (xstrdup(name)); 429 } 430 431 void 432 renamecmd(const char *from, const char *to) 433 { 434 char *p = NULL; 435 436 if (rename(from, to) < 0) { 437 p = strerror(errno); 438 perror_reply(550, "rename"); 439 } else 440 ack("RNTO"); 441 logxfer("rename", -1, from, to, NULL, p); 442 } 443 444 void 445 sizecmd(const char *filename) 446 { 447 switch (type) { 448 case TYPE_L: 449 case TYPE_I: 450 { 451 struct stat stbuf; 452 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 453 reply(550, "%s: not a plain file.", filename); 454 else 455 reply(213, ULLF, (ULLT)stbuf.st_size); 456 break; 457 } 458 case TYPE_A: 459 { 460 FILE *fin; 461 int c; 462 off_t count; 463 struct stat stbuf; 464 fin = fopen(filename, "r"); 465 if (fin == NULL) { 466 perror_reply(550, filename); 467 return; 468 } 469 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 470 reply(550, "%s: not a plain file.", filename); 471 (void) fclose(fin); 472 return; 473 } 474 if (stbuf.st_size > 10240) { 475 reply(550, "%s: file too large for SIZE.", filename); 476 (void) fclose(fin); 477 return; 478 } 479 480 count = 0; 481 while((c = getc(fin)) != EOF) { 482 if (c == '\n') /* will get expanded to \r\n */ 483 count++; 484 count++; 485 } 486 (void) fclose(fin); 487 488 reply(213, LLF, (LLT)count); 489 break; 490 } 491 default: 492 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 493 } 494 } 495 496 void 497 statfilecmd(const char *filename) 498 { 499 FILE *fin; 500 int c; 501 char *argv[] = { INTERNAL_LS, "-lgA", "", NULL }; 502 503 argv[2] = (char *)filename; 504 fin = ftpd_popen(argv, "r", STDOUT_FILENO); 505 reply(-211, "status of %s:", filename); 506 /* XXX: use fgetln() or fparseln() here? */ 507 while ((c = getc(fin)) != EOF) { 508 if (c == '\n') { 509 if (ferror(stdout)){ 510 perror_reply(421, "control connection"); 511 (void) ftpd_pclose(fin); 512 dologout(1); 513 /* NOTREACHED */ 514 } 515 if (ferror(fin)) { 516 perror_reply(551, filename); 517 (void) ftpd_pclose(fin); 518 return; 519 } 520 CPUTC('\r', stdout); 521 } 522 CPUTC(c, stdout); 523 } 524 (void) ftpd_pclose(fin); 525 reply(211, "End of Status"); 526 } 527 528 /* -- */ 529 530 static void 531 ack(const char *s) 532 { 533 534 reply(250, "%s command successful.", s); 535 } 536 537 /* 538 * Encode len bytes starting at clear using base64 encoding into encoded, 539 * which should be at least ((len + 2) * 4 / 3 + 1) in size. 540 * If nulterm is non-zero, terminate with \0 otherwise pad to 3 byte boundary 541 * with `='. 542 */ 543 static void 544 base64_encode(const char *clear, size_t len, char *encoded, int nulterm) 545 { 546 static const char base64[] = 547 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 548 const char *c; 549 char *e, termchar; 550 int i; 551 552 /* determine whether to pad with '=' or NUL terminate */ 553 termchar = nulterm ? '\0' : '='; 554 c = clear; 555 e = encoded; 556 /* convert all but last 2 bytes */ 557 for (i = len; i > 2; i -= 3, c += 3) { 558 *e++ = base64[(c[0] >> 2) & 0x3f]; 559 *e++ = base64[((c[0] << 4) & 0x30) | ((c[1] >> 4) & 0x0f)]; 560 *e++ = base64[((c[1] << 2) & 0x3c) | ((c[2] >> 6) & 0x03)]; 561 *e++ = base64[(c[2]) & 0x3f]; 562 } 563 /* handle slop at end */ 564 if (i > 0) { 565 *e++ = base64[(c[0] >> 2) & 0x3f]; 566 *e++ = base64[((c[0] << 4) & 0x30) | 567 (i > 1 ? ((c[1] >> 4) & 0x0f) : 0)]; 568 *e++ = (i > 1) ? base64[(c[1] << 2) & 0x3c] : termchar; 569 *e++ = termchar; 570 } 571 *e = '\0'; 572 } 573 574 static void 575 fact_modify(const char *fact, FILE *fd, factelem *fe) 576 { 577 struct tm *t; 578 579 t = gmtime(&(fe->stat->st_mtime)); 580 cprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact, 581 TM_YEAR_BASE + t->tm_year, 582 t->tm_mon+1, t->tm_mday, 583 t->tm_hour, t->tm_min, t->tm_sec); 584 } 585 586 static void 587 fact_perm(const char *fact, FILE *fd, factelem *fe) 588 { 589 int rok, wok, xok, pdirwok; 590 struct stat *pdir; 591 592 if (fe->stat->st_uid == geteuid()) { 593 rok = ((fe->stat->st_mode & S_IRUSR) != 0); 594 wok = ((fe->stat->st_mode & S_IWUSR) != 0); 595 xok = ((fe->stat->st_mode & S_IXUSR) != 0); 596 } else if (matchgroup(fe->stat->st_gid)) { 597 rok = ((fe->stat->st_mode & S_IRGRP) != 0); 598 wok = ((fe->stat->st_mode & S_IWGRP) != 0); 599 xok = ((fe->stat->st_mode & S_IXGRP) != 0); 600 } else { 601 rok = ((fe->stat->st_mode & S_IROTH) != 0); 602 wok = ((fe->stat->st_mode & S_IWOTH) != 0); 603 xok = ((fe->stat->st_mode & S_IXOTH) != 0); 604 } 605 606 cprintf(fd, "%s=", fact); 607 608 /* 609 * if parent info not provided, look it up, but 610 * only if the current class has modify rights, 611 * since we only need this info in such a case. 612 */ 613 pdir = fe->pdirstat; 614 if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) { 615 size_t len; 616 char realdir[MAXPATHLEN], *p; 617 struct stat dir; 618 619 len = strlcpy(realdir, fe->path, sizeof(realdir)); 620 if (len < sizeof(realdir) - 4) { 621 if (S_ISDIR(fe->stat->st_mode)) 622 strlcat(realdir, "/..", sizeof(realdir)); 623 else { 624 /* if has a /, move back to it */ 625 /* otherwise use '..' */ 626 if ((p = strrchr(realdir, '/')) != NULL) { 627 if (p == realdir) 628 p++; 629 *p = '\0'; 630 } else 631 strlcpy(realdir, "..", sizeof(realdir)); 632 } 633 if (stat(realdir, &dir) == 0) 634 pdir = &dir; 635 } 636 } 637 pdirwok = 0; 638 if (pdir != NULL) { 639 if (pdir->st_uid == geteuid()) 640 pdirwok = ((pdir->st_mode & S_IWUSR) != 0); 641 else if (matchgroup(pdir->st_gid)) 642 pdirwok = ((pdir->st_mode & S_IWGRP) != 0); 643 else 644 pdirwok = ((pdir->st_mode & S_IWOTH) != 0); 645 } 646 647 /* 'a': can APPE to file */ 648 if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 649 CPUTC('a', fd); 650 651 /* 'c': can create or append to files in directory */ 652 if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 653 CPUTC('c', fd); 654 655 /* 'd': can delete file or directory */ 656 if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) { 657 int candel; 658 659 candel = 1; 660 if (S_ISDIR(fe->stat->st_mode)) { 661 DIR *dirp; 662 struct dirent *dp; 663 664 if ((dirp = opendir(fe->display)) == NULL) 665 candel = 0; 666 else { 667 while ((dp = readdir(dirp)) != NULL) { 668 if (ISDOTDIR(dp->d_name) || 669 ISDOTDOTDIR(dp->d_name)) 670 continue; 671 candel = 0; 672 break; 673 } 674 closedir(dirp); 675 } 676 } 677 if (candel) 678 CPUTC('d', fd); 679 } 680 681 /* 'e': can enter directory */ 682 if (xok && S_ISDIR(fe->stat->st_mode)) 683 CPUTC('e', fd); 684 685 /* 'f': can rename file or directory */ 686 if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) 687 CPUTC('f', fd); 688 689 /* 'l': can list directory */ 690 if (rok && xok && S_ISDIR(fe->stat->st_mode)) 691 CPUTC('l', fd); 692 693 /* 'm': can create directory */ 694 if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 695 CPUTC('m', fd); 696 697 /* 'p': can remove files in directory */ 698 if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 699 CPUTC('p', fd); 700 701 /* 'r': can RETR file */ 702 if (rok && S_ISREG(fe->stat->st_mode)) 703 CPUTC('r', fd); 704 705 /* 'w': can STOR file */ 706 if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 707 CPUTC('w', fd); 708 709 CPUTC(';', fd); 710 } 711 712 static void 713 fact_size(const char *fact, FILE *fd, factelem *fe) 714 { 715 716 if (S_ISREG(fe->stat->st_mode)) 717 cprintf(fd, "%s=" LLF ";", fact, (LLT)fe->stat->st_size); 718 } 719 720 static void 721 fact_type(const char *fact, FILE *fd, factelem *fe) 722 { 723 724 cprintf(fd, "%s=", fact); 725 switch (fe->stat->st_mode & S_IFMT) { 726 case S_IFDIR: 727 if (fe->iscurdir || ISDOTDIR(fe->display)) 728 cprintf(fd, "cdir"); 729 else if (ISDOTDOTDIR(fe->display)) 730 cprintf(fd, "pdir"); 731 else 732 cprintf(fd, "dir"); 733 break; 734 case S_IFREG: 735 cprintf(fd, "file"); 736 break; 737 case S_IFIFO: 738 cprintf(fd, "OS.unix=fifo"); 739 break; 740 case S_IFLNK: /* XXX: probably a NO-OP with stat() */ 741 cprintf(fd, "OS.unix=slink"); 742 break; 743 case S_IFSOCK: 744 cprintf(fd, "OS.unix=socket"); 745 break; 746 case S_IFBLK: 747 case S_IFCHR: 748 cprintf(fd, "OS.unix=%s-%d/%d", 749 S_ISBLK(fe->stat->st_mode) ? "blk" : "chr", 750 major(fe->stat->st_rdev), minor(fe->stat->st_rdev)); 751 break; 752 default: 753 cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT); 754 break; 755 } 756 CPUTC(';', fd); 757 } 758 759 static void 760 fact_unique(const char *fact, FILE *fd, factelem *fe) 761 { 762 char obuf[(sizeof(dev_t) + sizeof(ino_t) + 2) * 4 / 3 + 2]; 763 char tbuf[sizeof(dev_t) + sizeof(ino_t)]; 764 765 memcpy(tbuf, 766 (char *)&(fe->stat->st_dev), sizeof(dev_t)); 767 memcpy(tbuf + sizeof(dev_t), 768 (char *)&(fe->stat->st_ino), sizeof(ino_t)); 769 base64_encode(tbuf, sizeof(dev_t) + sizeof(ino_t), obuf, 1); 770 cprintf(fd, "%s=%s;", fact, obuf); 771 } 772 773 static int 774 matchgroup(gid_t gid) 775 { 776 int i; 777 778 for (i = 0; i < gidcount; i++) 779 if (gid == gidlist[i]) 780 return(1); 781 return (0); 782 } 783 784 static void 785 mlsname(FILE *fp, factelem *fe) 786 { 787 int i; 788 789 for (i = 0; i < FACTTABSIZE; i++) { 790 if (facttab[i].enabled) 791 (facttab[i].display)(facttab[i].name, fp, fe); 792 } 793 cprintf(fp, " %s\r\n", fe->display); 794 } 795 796 static void 797 replydirname(const char *name, const char *message) 798 { 799 char *p, *ep; 800 char npath[MAXPATHLEN * 2]; 801 802 p = npath; 803 ep = &npath[sizeof(npath) - 1]; 804 while (*name) { 805 if (*name == '"') { 806 if (ep - p < 2) 807 break; 808 *p++ = *name++; 809 *p++ = '"'; 810 } else { 811 if (ep - p < 1) 812 break; 813 *p++ = *name++; 814 } 815 } 816 *p = '\0'; 817 reply(257, "\"%s\" %s", npath, message); 818 } 819