1 /* $NetBSD: cmds.c,v 1.13 2001/04/25 01:46:25 lukem 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.13 2001/04/25 01:46:25 lukem 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 475 count = 0; 476 while((c=getc(fin)) != EOF) { 477 if (c == '\n') /* will get expanded to \r\n */ 478 count++; 479 count++; 480 } 481 (void) fclose(fin); 482 483 reply(213, LLF, (LLT)count); 484 break; 485 } 486 default: 487 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 488 } 489 } 490 491 void 492 statfilecmd(const char *filename) 493 { 494 FILE *fin; 495 int c; 496 char *argv[] = { INTERNAL_LS, "-lgA", "", NULL }; 497 498 argv[2] = (char *)filename; 499 fin = ftpd_popen(argv, "r", STDOUT_FILENO); 500 reply(-211, "status of %s:", filename); 501 /* XXX: use fgetln() or fparseln() here? */ 502 while ((c = getc(fin)) != EOF) { 503 if (c == '\n') { 504 if (ferror(stdout)){ 505 perror_reply(421, "control connection"); 506 (void) ftpd_pclose(fin); 507 dologout(1); 508 /* NOTREACHED */ 509 } 510 if (ferror(fin)) { 511 perror_reply(551, filename); 512 (void) ftpd_pclose(fin); 513 return; 514 } 515 CPUTC('\r', stdout); 516 } 517 CPUTC(c, stdout); 518 } 519 (void) ftpd_pclose(fin); 520 reply(211, "End of Status"); 521 } 522 523 /* -- */ 524 525 static void 526 ack(const char *s) 527 { 528 529 reply(250, "%s command successful.", s); 530 } 531 532 /* 533 * Encode len bytes starting at clear using base64 encoding into encoded, 534 * which should be at least ((len + 2) * 4 / 3 + 1) in size. 535 * If nulterm is non-zero, terminate with \0 otherwise pad to 3 byte boundary 536 * with `='. 537 */ 538 static void 539 base64_encode(const char *clear, size_t len, char *encoded, int nulterm) 540 { 541 static const char base64[] = 542 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 543 const char *c; 544 char *e, termchar; 545 int i; 546 547 /* determine whether to pad with '=' or NUL terminate */ 548 termchar = nulterm ? '\0' : '='; 549 c = clear; 550 e = encoded; 551 /* convert all but last 2 bytes */ 552 for (i = len; i > 2; i -= 3, c += 3) { 553 *e++ = base64[(c[0] >> 2) & 0x3f]; 554 *e++ = base64[((c[0] << 4) & 0x30) | ((c[1] >> 4) & 0x0f)]; 555 *e++ = base64[((c[1] << 2) & 0x3c) | ((c[2] >> 6) & 0x03)]; 556 *e++ = base64[(c[2]) & 0x3f]; 557 } 558 /* handle slop at end */ 559 if (i > 0) { 560 *e++ = base64[(c[0] >> 2) & 0x3f]; 561 *e++ = base64[((c[0] << 4) & 0x30) | 562 (i > 1 ? ((c[1] >> 4) & 0x0f) : 0)]; 563 *e++ = (i > 1) ? base64[(c[1] << 2) & 0x3c] : termchar; 564 *e++ = termchar; 565 } 566 *e = '\0'; 567 } 568 569 static void 570 fact_modify(const char *fact, FILE *fd, factelem *fe) 571 { 572 struct tm *t; 573 574 t = gmtime(&(fe->stat->st_mtime)); 575 cprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact, 576 TM_YEAR_BASE + t->tm_year, 577 t->tm_mon+1, t->tm_mday, 578 t->tm_hour, t->tm_min, t->tm_sec); 579 } 580 581 static void 582 fact_perm(const char *fact, FILE *fd, factelem *fe) 583 { 584 int rok, wok, xok, pdirwok; 585 struct stat *pdir; 586 587 if (fe->stat->st_uid == geteuid()) { 588 rok = ((fe->stat->st_mode & S_IRUSR) != 0); 589 wok = ((fe->stat->st_mode & S_IWUSR) != 0); 590 xok = ((fe->stat->st_mode & S_IXUSR) != 0); 591 } else if (matchgroup(fe->stat->st_gid)) { 592 rok = ((fe->stat->st_mode & S_IRGRP) != 0); 593 wok = ((fe->stat->st_mode & S_IWGRP) != 0); 594 xok = ((fe->stat->st_mode & S_IXGRP) != 0); 595 } else { 596 rok = ((fe->stat->st_mode & S_IROTH) != 0); 597 wok = ((fe->stat->st_mode & S_IWOTH) != 0); 598 xok = ((fe->stat->st_mode & S_IXOTH) != 0); 599 } 600 601 cprintf(fd, "%s=", fact); 602 603 /* 604 * if parent info not provided, look it up, but 605 * only if the current class has modify rights, 606 * since we only need this info in such a case. 607 */ 608 pdir = fe->pdirstat; 609 if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) { 610 size_t len; 611 char realdir[MAXPATHLEN], *p; 612 struct stat dir; 613 614 len = strlcpy(realdir, fe->path, sizeof(realdir)); 615 if (len < sizeof(realdir) - 4) { 616 if (S_ISDIR(fe->stat->st_mode)) 617 strlcat(realdir, "/..", sizeof(realdir)); 618 else { 619 /* if has a /, move back to it */ 620 /* otherwise use '..' */ 621 if ((p = strrchr(realdir, '/')) != NULL) { 622 if (p == realdir) 623 p++; 624 *p = '\0'; 625 } else 626 strlcpy(realdir, "..", sizeof(realdir)); 627 } 628 if (stat(realdir, &dir) == 0) 629 pdir = &dir; 630 } 631 } 632 pdirwok = 0; 633 if (pdir != NULL) { 634 if (pdir->st_uid == geteuid()) 635 pdirwok = ((pdir->st_mode & S_IWUSR) != 0); 636 else if (matchgroup(pdir->st_gid)) 637 pdirwok = ((pdir->st_mode & S_IWGRP) != 0); 638 else 639 pdirwok = ((pdir->st_mode & S_IWOTH) != 0); 640 } 641 642 /* 'a': can APPE to file */ 643 if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 644 CPUTC('a', fd); 645 646 /* 'c': can create or append to files in directory */ 647 if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 648 CPUTC('c', fd); 649 650 /* 'd': can delete file or directory */ 651 if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) { 652 int candel; 653 654 candel = 1; 655 if (S_ISDIR(fe->stat->st_mode)) { 656 DIR *dirp; 657 struct dirent *dp; 658 659 if ((dirp = opendir(fe->display)) == NULL) 660 candel = 0; 661 else { 662 while ((dp = readdir(dirp)) != NULL) { 663 if (ISDOTDIR(dp->d_name) || 664 ISDOTDOTDIR(dp->d_name)) 665 continue; 666 candel = 0; 667 break; 668 } 669 closedir(dirp); 670 } 671 } 672 if (candel) 673 CPUTC('d', fd); 674 } 675 676 /* 'e': can enter directory */ 677 if (xok && S_ISDIR(fe->stat->st_mode)) 678 CPUTC('e', fd); 679 680 /* 'f': can rename file or directory */ 681 if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) 682 CPUTC('f', fd); 683 684 /* 'l': can list directory */ 685 if (rok && xok && S_ISDIR(fe->stat->st_mode)) 686 CPUTC('l', fd); 687 688 /* 'm': can create directory */ 689 if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 690 CPUTC('m', fd); 691 692 /* 'p': can remove files in directory */ 693 if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 694 CPUTC('p', fd); 695 696 /* 'r': can RETR file */ 697 if (rok && S_ISREG(fe->stat->st_mode)) 698 CPUTC('r', fd); 699 700 /* 'w': can STOR file */ 701 if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 702 CPUTC('w', fd); 703 704 CPUTC(';', fd); 705 } 706 707 static void 708 fact_size(const char *fact, FILE *fd, factelem *fe) 709 { 710 711 if (S_ISREG(fe->stat->st_mode)) 712 cprintf(fd, "%s=" LLF ";", fact, (LLT)fe->stat->st_size); 713 } 714 715 static void 716 fact_type(const char *fact, FILE *fd, factelem *fe) 717 { 718 719 cprintf(fd, "%s=", fact); 720 switch (fe->stat->st_mode & S_IFMT) { 721 case S_IFDIR: 722 if (fe->iscurdir || ISDOTDIR(fe->display)) 723 cprintf(fd, "cdir"); 724 else if (ISDOTDOTDIR(fe->display)) 725 cprintf(fd, "pdir"); 726 else 727 cprintf(fd, "dir"); 728 break; 729 case S_IFREG: 730 cprintf(fd, "file"); 731 break; 732 case S_IFIFO: 733 cprintf(fd, "OS.unix=fifo"); 734 break; 735 case S_IFLNK: /* XXX: probably a NO-OP with stat() */ 736 cprintf(fd, "OS.unix=slink"); 737 break; 738 case S_IFSOCK: 739 cprintf(fd, "OS.unix=socket"); 740 break; 741 case S_IFBLK: 742 case S_IFCHR: 743 cprintf(fd, "OS.unix=%s-%d/%d", 744 S_ISBLK(fe->stat->st_mode) ? "blk" : "chr", 745 major(fe->stat->st_rdev), minor(fe->stat->st_rdev)); 746 break; 747 default: 748 cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT); 749 break; 750 } 751 CPUTC(';', fd); 752 } 753 754 static void 755 fact_unique(const char *fact, FILE *fd, factelem *fe) 756 { 757 char obuf[(sizeof(dev_t) + sizeof(ino_t) + 2) * 4 / 3 + 2]; 758 char tbuf[sizeof(dev_t) + sizeof(ino_t)]; 759 760 memcpy(tbuf, 761 (char *)&(fe->stat->st_dev), sizeof(dev_t)); 762 memcpy(tbuf + sizeof(dev_t), 763 (char *)&(fe->stat->st_ino), sizeof(ino_t)); 764 base64_encode(tbuf, sizeof(dev_t) + sizeof(ino_t), obuf, 1); 765 cprintf(fd, "%s=%s;", fact, obuf); 766 } 767 768 static int 769 matchgroup(gid_t gid) 770 { 771 int i; 772 773 for (i = 0; i < gidcount; i++) 774 if (gid == gidlist[i]) 775 return(1); 776 return (0); 777 } 778 779 static void 780 mlsname(FILE *fp, factelem *fe) 781 { 782 int i; 783 784 for (i = 0; i < FACTTABSIZE; i++) { 785 if (facttab[i].enabled) 786 (facttab[i].display)(facttab[i].name, fp, fe); 787 } 788 cprintf(fp, " %s\r\n", fe->display); 789 } 790 791 static void 792 replydirname(const char *name, const char *message) 793 { 794 char *p, *ep; 795 char npath[MAXPATHLEN * 2]; 796 797 p = npath; 798 ep = &npath[sizeof(npath) - 1]; 799 while (*name) { 800 if (*name == '"') { 801 if (ep - p < 2) 802 break; 803 *p++ = *name++; 804 *p++ = '"'; 805 } else { 806 if (ep - p < 1) 807 break; 808 *p++ = *name++; 809 } 810 } 811 *p = '\0'; 812 reply(257, "\"%s\" %s", npath, message); 813 } 814