1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include <ip.h> 6 #include <libsec.h> 7 #include <String.h> 8 9 #include "glob.h" 10 11 enum 12 { 13 /* telnet control character */ 14 Iac= 255, 15 16 /* representation types */ 17 Tascii= 0, 18 Timage= 1, 19 20 /* transmission modes */ 21 Mstream= 0, 22 Mblock= 1, 23 Mpage= 2, 24 25 /* file structure */ 26 Sfile= 0, 27 Sblock= 1, 28 Scompressed= 2, 29 30 /* read/write buffer size */ 31 Nbuf= 4096, 32 33 /* maximum ms we'll wait for a command */ 34 Maxwait= 1000*60*30, /* inactive for 30 minutes, we hang up */ 35 36 Maxerr= 128, 37 Maxpath= 512, 38 }; 39 40 int abortcmd(char*); 41 int appendcmd(char*); 42 int cdupcmd(char*); 43 int cwdcmd(char*); 44 int delcmd(char*); 45 int helpcmd(char*); 46 int listcmd(char*); 47 int mdtmcmd(char*); 48 int mkdircmd(char*); 49 int modecmd(char*); 50 int namelistcmd(char*); 51 int nopcmd(char*); 52 int passcmd(char*); 53 int pasvcmd(char*); 54 int portcmd(char*); 55 int pwdcmd(char*); 56 int quitcmd(char*); 57 int rnfrcmd(char*); 58 int rntocmd(char*); 59 int reply(char*, ...); 60 int restartcmd(char*); 61 int retrievecmd(char*); 62 int sitecmd(char*); 63 int sizecmd(char*); 64 int storecmd(char*); 65 int storeucmd(char*); 66 int structcmd(char*); 67 int systemcmd(char*); 68 int typecmd(char*); 69 int usercmd(char*); 70 71 int dialdata(void); 72 char* abspath(char*); 73 int crlfwrite(int, char*, int); 74 int sodoff(void); 75 int accessok(char*); 76 77 typedef struct Cmd Cmd; 78 struct Cmd 79 { 80 char *name; 81 int (*f)(char*); 82 int needlogin; 83 }; 84 85 Cmd cmdtab[] = 86 { 87 { "abor", abortcmd, 0, }, 88 { "appe", appendcmd, 1, }, 89 { "cdup", cdupcmd, 1, }, 90 { "cwd", cwdcmd, 1, }, 91 { "dele", delcmd, 1, }, 92 { "help", helpcmd, 0, }, 93 { "list", listcmd, 1, }, 94 { "mdtm", mdtmcmd, 1, }, 95 { "mkd", mkdircmd, 1, }, 96 { "mode", modecmd, 0, }, 97 { "nlst", namelistcmd, 1, }, 98 { "noop", nopcmd, 0, }, 99 { "pass", passcmd, 0, }, 100 { "pasv", pasvcmd, 1, }, 101 { "pwd", pwdcmd, 0, }, 102 { "port", portcmd, 1, }, 103 { "quit", quitcmd, 0, }, 104 { "rest", restartcmd, 1, }, 105 { "retr", retrievecmd, 1, }, 106 { "rmd", delcmd, 1, }, 107 { "rnfr", rnfrcmd, 1, }, 108 { "rnto", rntocmd, 1, }, 109 { "site", sitecmd, 1, }, 110 { "size", sizecmd, 1, }, 111 { "stor", storecmd, 1, }, 112 { "stou", storeucmd, 1, }, 113 { "stru", structcmd, 1, }, 114 { "syst", systemcmd, 0, }, 115 { "type", typecmd, 0, }, 116 { "user", usercmd, 0, }, 117 { 0, 0, 0 }, 118 }; 119 120 #define NONENS "/lib/namespace.ftp" /* default ns for none */ 121 122 char user[Maxpath]; /* logged in user */ 123 char curdir[Maxpath]; /* current directory path */ 124 Chalstate *ch; 125 int loggedin; 126 int type; /* transmission type */ 127 int mode; /* transmission mode */ 128 int structure; /* file structure */ 129 char data[64]; /* data address */ 130 int pid; /* transfer process */ 131 int encryption; /* encryption state */ 132 int isnone, anon_ok, anon_only, anon_everybody; 133 char cputype[Maxpath]; /* the environment variable of the same name */ 134 char bindir[Maxpath]; /* bin directory for this architecture */ 135 char mailaddr[Maxpath]; 136 char *namespace = NONENS; 137 int debug; 138 NetConnInfo *nci; 139 int createperm = 0660; 140 int isnoworld; 141 vlong offset; /* from restart command */ 142 143 ulong id; 144 145 typedef struct Passive Passive; 146 struct Passive 147 { 148 int inuse; 149 char adir[40]; 150 int afd; 151 int port; 152 uchar ipaddr[IPaddrlen]; 153 } passive; 154 155 #define FTPLOG "ftp" 156 157 void 158 logit(char *fmt, ...) 159 { 160 char buf[8192]; 161 va_list arg; 162 char errstr[128]; 163 164 rerrstr(errstr, sizeof errstr); 165 va_start(arg, fmt); 166 vseprint(buf, buf+sizeof(buf), fmt, arg); 167 va_end(arg); 168 syslog(0, FTPLOG, "%s.%s %s", nci->rsys, nci->rserv, buf); 169 werrstr(errstr, sizeof errstr); 170 } 171 172 static void 173 usage(void) 174 { 175 syslog(0, "ftp", "usage: %s [-aAde] [-n nsfile]", argv0); 176 fprint(2, "usage: %s [-aAde] [-n nsfile]\n", argv0); 177 exits("usage"); 178 } 179 180 /* 181 * read commands from the control stream and dispatch 182 */ 183 void 184 main(int argc, char **argv) 185 { 186 char *cmd; 187 char *arg; 188 char *p; 189 Cmd *t; 190 Biobuf in; 191 int i; 192 193 ARGBEGIN{ 194 case 'a': /* anonymous OK */ 195 anon_ok = 1; 196 break; 197 case 'A': 198 anon_ok = 1; 199 anon_only = 1; 200 break; 201 case 'd': 202 debug++; 203 break; 204 case 'e': 205 anon_ok = 1; 206 anon_everybody = 1; 207 break; 208 case 'n': 209 namespace = EARGF(usage()); 210 break; 211 default: 212 usage(); 213 }ARGEND 214 215 /* open log file before doing a newns */ 216 syslog(0, FTPLOG, nil); 217 218 /* find out who is calling */ 219 if(argc < 1) 220 nci = getnetconninfo(nil, 0); 221 else 222 nci = getnetconninfo(argv[argc-1], 0); 223 if(nci == nil) 224 sysfatal("ftpd needs a network address"); 225 226 strcpy(mailaddr, "?"); 227 id = getpid(); 228 229 /* figure out which binaries to bind in later (only for none) */ 230 arg = getenv("cputype"); 231 if(arg) 232 strecpy(cputype, cputype+sizeof cputype, arg); 233 else 234 strcpy(cputype, "mips"); 235 /* shurely /%s/bin */ 236 snprint(bindir, sizeof(bindir), "/bin/%s/bin", cputype); 237 238 Binit(&in, 0, OREAD); 239 reply("220 Plan 9 FTP server ready"); 240 alarm(Maxwait); 241 while(cmd = Brdline(&in, '\n')){ 242 alarm(0); 243 244 /* 245 * strip out trailing cr's & lf and delimit with null 246 */ 247 i = Blinelen(&in)-1; 248 cmd[i] = 0; 249 if(debug) 250 logit("%s", cmd); 251 while(i > 0 && cmd[i-1] == '\r') 252 cmd[--i] = 0; 253 254 /* 255 * hack for GatorFTP+, look for a 0x10 used as a delimiter 256 */ 257 p = strchr(cmd, 0x10); 258 if(p) 259 *p = 0; 260 261 /* 262 * get rid of telnet control sequences (we don't need them) 263 */ 264 while(*cmd && (uchar)*cmd == Iac){ 265 cmd++; 266 if(*cmd) 267 cmd++; 268 } 269 270 /* 271 * parse the message (command arg) 272 */ 273 arg = strchr(cmd, ' '); 274 if(arg){ 275 *arg++ = 0; 276 while(*arg == ' ') 277 arg++; 278 } 279 280 /* 281 * ignore blank commands 282 */ 283 if(*cmd == 0) 284 continue; 285 286 /* 287 * lookup the command and do it 288 */ 289 for(p = cmd; *p; p++) 290 *p = tolower(*p); 291 for(t = cmdtab; t->name; t++) 292 if(strcmp(cmd, t->name) == 0){ 293 if(t->needlogin && !loggedin) 294 sodoff(); 295 else if((*t->f)(arg) < 0) 296 exits(0); 297 break; 298 } 299 if(t->f != restartcmd){ 300 /* 301 * the file offset is set to zero following 302 * all commands except the restart command 303 */ 304 offset = 0; 305 } 306 if(t->name == 0){ 307 /* 308 * the OOB bytes preceding an abort from UCB machines 309 * comes out as something unrecognizable instead of 310 * IAC's. Certainly a Plan 9 bug but I can't find it. 311 * This is a major hack to avoid the problem. -- presotto 312 */ 313 i = strlen(cmd); 314 if(i > 4 && strcmp(cmd+i-4, "abor") == 0){ 315 abortcmd(0); 316 } else{ 317 logit("%s (%s) command not implemented", cmd, arg?arg:""); 318 reply("502 %s command not implemented", cmd); 319 } 320 } 321 alarm(Maxwait); 322 } 323 if(pid) 324 postnote(PNPROC, pid, "kill"); 325 } 326 327 /* 328 * reply to a command 329 */ 330 int 331 reply(char *fmt, ...) 332 { 333 va_list arg; 334 char buf[8192], *s; 335 336 va_start(arg, fmt); 337 s = vseprint(buf, buf+sizeof(buf)-3, fmt, arg); 338 va_end(arg); 339 if(debug){ 340 *s = 0; 341 logit("%s", buf); 342 } 343 *s++ = '\r'; 344 *s++ = '\n'; 345 write(1, buf, s - buf); 346 return 0; 347 } 348 349 int 350 sodoff(void) 351 { 352 return reply("530 Sod off, service requires login"); 353 } 354 355 /* 356 * run a command in a separate process 357 */ 358 int 359 asproc(void (*f)(char*, int), char *arg, int arg2) 360 { 361 int i; 362 363 if(pid){ 364 /* wait for previous command to finish */ 365 for(;;){ 366 i = waitpid(); 367 if(i == pid || i < 0) 368 break; 369 } 370 } 371 372 switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){ 373 case -1: 374 return reply("450 Out of processes: %r"); 375 case 0: 376 (*f)(arg, arg2); 377 exits(0); 378 default: 379 break; 380 } 381 return 0; 382 } 383 384 /* 385 * run a command to filter a tail 386 */ 387 int 388 transfer(char *cmd, char *a1, char *a2, char *a3, int image) 389 { 390 int n, dfd, fd, bytes, eofs, pid; 391 int pfd[2]; 392 char buf[Nbuf], *p; 393 Waitmsg *w; 394 395 reply("150 Opening data connection for %s (%s)", cmd, data); 396 dfd = dialdata(); 397 if(dfd < 0) 398 return reply("425 Error opening data connection: %r"); 399 400 if(pipe(pfd) < 0) 401 return reply("520 Internal Error: %r"); 402 403 bytes = 0; 404 switch(pid = rfork(RFFDG|RFPROC|RFNAMEG)){ 405 case -1: 406 return reply("450 Out of processes: %r"); 407 case 0: 408 logit("running %s %s %s %s pid %d", 409 cmd, a1?a1:"", a2?a2:"" , a3?a3:"",getpid()); 410 close(pfd[1]); 411 close(dfd); 412 dup(pfd[0], 1); 413 dup(pfd[0], 2); 414 if(isnone){ 415 fd = open("#s/boot", ORDWR); 416 if(fd < 0 417 || bind("#/", "/", MAFTER) < 0 418 || amount(fd, "/bin", MREPL, "") < 0 419 || bind("#c", "/dev", MAFTER) < 0 420 || bind(bindir, "/bin", MREPL) < 0) 421 exits("building name space"); 422 close(fd); 423 } 424 execl(cmd, cmd, a1, a2, a3, nil); 425 exits(cmd); 426 default: 427 close(pfd[0]); 428 eofs = 0; 429 while((n = read(pfd[1], buf, sizeof buf)) >= 0){ 430 if(n == 0){ 431 if(eofs++ > 5) 432 break; 433 else 434 continue; 435 } 436 eofs = 0; 437 p = buf; 438 if(offset > 0){ 439 if(n > offset){ 440 p = buf+offset; 441 n -= offset; 442 offset = 0; 443 } else { 444 offset -= n; 445 continue; 446 } 447 } 448 if(!image) 449 n = crlfwrite(dfd, p, n); 450 else 451 n = write(dfd, p, n); 452 if(n < 0){ 453 postnote(PNPROC, pid, "kill"); 454 bytes = -1; 455 break; 456 } 457 bytes += n; 458 } 459 close(pfd[1]); 460 close(dfd); 461 break; 462 } 463 464 /* wait for this command to finish */ 465 for(;;){ 466 w = wait(); 467 if(w == nil || w->pid == pid) 468 break; 469 free(w); 470 } 471 if(w != nil && w->msg != nil && w->msg[0] != 0){ 472 bytes = -1; 473 logit("%s", w->msg); 474 logit("%s %s %s %s failed %s", cmd, a1?a1:"", a2?a2:"" , a3?a3:"", w->msg); 475 } 476 free(w); 477 reply("226 Transfer complete"); 478 return bytes; 479 } 480 481 482 /* 483 * just reply OK 484 */ 485 int 486 nopcmd(char *arg) 487 { 488 USED(arg); 489 reply("510 Plan 9 FTP daemon still alive"); 490 return 0; 491 } 492 493 /* 494 * login as user 495 */ 496 int 497 loginuser(char *user, char *nsfile, int gotoslash) 498 { 499 logit("login %s %s %s %s", user, mailaddr, nci->rsys, nsfile); 500 if(nsfile != nil && newns(user, nsfile) < 0){ 501 logit("namespace file %s does not exist", nsfile); 502 return reply("530 Not logged in: login out of service"); 503 } 504 getwd(curdir, sizeof(curdir)); 505 if(gotoslash){ 506 chdir("/"); 507 strcpy(curdir, "/"); 508 } 509 putenv("service", "ftp"); 510 loggedin = 1; 511 if(debug == 0) 512 reply("230- If you have problems, send mail to 'postmaster'."); 513 return reply("230 Logged in"); 514 } 515 516 static void 517 slowdown(void) 518 { 519 static ulong pause; 520 521 if (pause) { 522 sleep(pause); /* deter guessers */ 523 if (pause < (1UL << 20)) 524 pause *= 2; 525 } else 526 pause = 1000; 527 } 528 529 /* 530 * get a user id, reply with a challenge. The users 'anonymous' 531 * and 'ftp' are equivalent to 'none'. The user 'none' requires 532 * no challenge. 533 */ 534 int 535 usercmd(char *name) 536 { 537 slowdown(); 538 539 logit("user %s %s", name, nci->rsys); 540 if(loggedin) 541 return reply("530 Already logged in as %s", user); 542 if(name == 0 || *name == 0) 543 return reply("530 user command needs user name"); 544 isnoworld = 0; 545 if(*name == ':'){ 546 debug = 1; 547 name++; 548 } 549 strncpy(user, name, sizeof(user)); 550 if(debug) 551 logit("debugging"); 552 user[sizeof(user)-1] = 0; 553 if(strcmp(user, "anonymous") == 0 || strcmp(user, "ftp") == 0) 554 strcpy(user, "none"); 555 else if(anon_everybody) 556 strcpy(user,"none"); 557 558 if(strcmp(user, "Administrator") == 0 || strcmp(user, "admin") == 0) 559 return reply("530 go away, script kiddie"); 560 else if(strcmp(user, "*none") == 0){ 561 if(!anon_ok) 562 return reply("530 Not logged in: anonymous disallowed"); 563 return loginuser("none", namespace, 1); 564 } 565 else if(strcmp(user, "none") == 0){ 566 if(!anon_ok) 567 return reply("530 Not logged in: anonymous disallowed"); 568 return reply("331 Send email address as password"); 569 } 570 else if(anon_only) 571 return reply("530 Not logged in: anonymous access only"); 572 573 isnoworld = noworld(name); 574 if(isnoworld) 575 return reply("331 OK"); 576 577 /* consult the auth server */ 578 if(ch) 579 auth_freechal(ch); 580 if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil) 581 return reply("421 %r"); 582 return reply("331 encrypt challenge, %s, as a password", ch->chal); 583 } 584 585 /* 586 * get a password, set up user if it works. 587 */ 588 int 589 passcmd(char *response) 590 { 591 char namefile[128]; 592 AuthInfo *ai; 593 594 if(response == nil) 595 response = ""; 596 597 if(strcmp(user, "none") == 0 || strcmp(user, "*none") == 0){ 598 /* for none, accept anything as a password */ 599 isnone = 1; 600 strncpy(mailaddr, response, sizeof(mailaddr)-1); 601 return loginuser("none", namespace, 1); 602 } 603 604 if(isnoworld){ 605 /* noworld gets a password in the clear */ 606 if(login(user, response, "/lib/namespace.noworld") < 0) 607 return reply("530 Not logged in"); 608 createperm = 0664; 609 /* login has already setup the namespace */ 610 return loginuser(user, nil, 0); 611 } else { 612 /* for everyone else, do challenge response */ 613 if(ch == nil) 614 return reply("531 Send user id before encrypted challenge"); 615 ch->resp = response; 616 ch->nresp = strlen(response); 617 ai = auth_response(ch); 618 if(ai == nil || auth_chuid(ai, nil) < 0) { 619 slowdown(); 620 return reply("530 Not logged in: %r"); 621 } 622 auth_freechal(ch); 623 ch = nil; 624 625 /* if the user has specified a namespace for ftp, use it */ 626 snprint(namefile, sizeof(namefile), "/usr/%s/lib/namespace.ftp", user); 627 strcpy(mailaddr, user); 628 createperm = 0660; 629 if(access(namefile, 0) == 0) 630 return loginuser(user, namefile, 0); 631 else 632 return loginuser(user, "/lib/namespace", 0); 633 } 634 } 635 636 /* 637 * print working directory 638 */ 639 int 640 pwdcmd(char *arg) 641 { 642 if(arg) 643 return reply("550 Pwd takes no argument"); 644 return reply("257 \"%s\" is the current directory", curdir); 645 } 646 647 /* 648 * chdir 649 */ 650 int 651 cwdcmd(char *dir) 652 { 653 char *rp; 654 char buf[Maxpath]; 655 656 /* shell cd semantics */ 657 if(dir == 0 || *dir == 0){ 658 if(isnone) 659 rp = "/"; 660 else { 661 snprint(buf, sizeof buf, "/usr/%s", user); 662 rp = buf; 663 } 664 if(accessok(rp) == 0) 665 rp = nil; 666 } else 667 rp = abspath(dir); 668 669 if(rp == nil) 670 return reply("550 Permission denied"); 671 672 if(chdir(rp) < 0) 673 return reply("550 Cwd failed: %r"); 674 strcpy(curdir, rp); 675 return reply("250 directory changed to %s", curdir); 676 } 677 678 /* 679 * chdir .. 680 */ 681 int 682 cdupcmd(char *dp) 683 { 684 USED(dp); 685 return cwdcmd(".."); 686 } 687 688 int 689 quitcmd(char *arg) 690 { 691 USED(arg); 692 reply("200 Bye"); 693 if(pid) 694 postnote(PNPROC, pid, "kill"); 695 return -1; 696 } 697 698 int 699 typecmd(char *arg) 700 { 701 int c; 702 char *x; 703 704 x = arg; 705 if(arg == 0) 706 return reply("501 Type command needs arguments"); 707 708 while(c = *arg++){ 709 switch(tolower(c)){ 710 case 'a': 711 type = Tascii; 712 break; 713 case 'i': 714 case 'l': 715 type = Timage; 716 break; 717 case '8': 718 case ' ': 719 case 'n': 720 case 't': 721 case 'c': 722 break; 723 default: 724 return reply("501 Unimplemented type %s", x); 725 } 726 } 727 return reply("200 Type %s", type==Tascii ? "Ascii" : "Image"); 728 } 729 730 int 731 modecmd(char *arg) 732 { 733 if(arg == 0) 734 return reply("501 Mode command needs arguments"); 735 while(*arg){ 736 switch(tolower(*arg)){ 737 case 's': 738 mode = Mstream; 739 break; 740 default: 741 return reply("501 Unimplemented mode %c", *arg); 742 } 743 arg++; 744 } 745 return reply("200 Stream mode"); 746 } 747 748 int 749 structcmd(char *arg) 750 { 751 if(arg == 0) 752 return reply("501 Struct command needs arguments"); 753 for(; *arg; arg++){ 754 switch(tolower(*arg)){ 755 case 'f': 756 structure = Sfile; 757 break; 758 default: 759 return reply("501 Unimplemented structure %c", *arg); 760 } 761 } 762 return reply("200 File structure"); 763 } 764 765 int 766 portcmd(char *arg) 767 { 768 char *field[7]; 769 int n; 770 771 if(arg == 0) 772 return reply("501 Port command needs arguments"); 773 n = getfields(arg, field, 7, 0, ", "); 774 if(n != 6) 775 return reply("501 Incorrect port specification"); 776 snprint(data, sizeof data, "tcp!%.3s.%.3s.%.3s.%.3s!%d", field[0], field[1], field[2], 777 field[3], atoi(field[4])*256 + atoi(field[5])); 778 return reply("200 Data port is %s", data); 779 } 780 781 int 782 mountnet(void) 783 { 784 int rv; 785 786 rv = 0; 787 788 if(bind("#/", "/", MAFTER) < 0){ 789 logit("can't bind #/ to /: %r"); 790 return reply("500 can't bind #/ to /: %r"); 791 } 792 793 if(bind(nci->spec, "/net", MBEFORE) < 0){ 794 logit("can't bind %s to /net: %r", nci->spec); 795 rv = reply("500 can't bind %s to /net: %r", nci->spec); 796 unmount("#/", "/"); 797 } 798 799 return rv; 800 } 801 802 void 803 unmountnet(void) 804 { 805 unmount(0, "/net"); 806 unmount("#/", "/"); 807 } 808 809 int 810 pasvcmd(char *arg) 811 { 812 NetConnInfo *nnci; 813 Passive *p; 814 815 USED(arg); 816 p = &passive; 817 818 if(p->inuse){ 819 close(p->afd); 820 p->inuse = 0; 821 } 822 823 if(mountnet() < 0) 824 return 0; 825 826 p->afd = announce("tcp!*!0", passive.adir); 827 if(p->afd < 0){ 828 unmountnet(); 829 return reply("500 No free ports"); 830 } 831 nnci = getnetconninfo(p->adir, -1); 832 unmountnet(); 833 834 /* parse the local address */ 835 if(debug) 836 logit("local sys is %s", nci->lsys); 837 parseip(p->ipaddr, nci->lsys); 838 if(ipcmp(p->ipaddr, v4prefix) == 0 || ipcmp(p->ipaddr, IPnoaddr) == 0) 839 parseip(p->ipaddr, nci->lsys); 840 p->port = atoi(nnci->lserv); 841 842 freenetconninfo(nnci); 843 p->inuse = 1; 844 845 return reply("227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)", 846 p->ipaddr[IPv4off+0], p->ipaddr[IPv4off+1], p->ipaddr[IPv4off+2], p->ipaddr[IPv4off+3], 847 p->port>>8, p->port&0xff); 848 } 849 850 enum 851 { 852 Narg=32, 853 }; 854 int Cflag, rflag, tflag, Rflag; 855 int maxnamelen; 856 int col; 857 858 char* 859 mode2asc(int m) 860 { 861 static char asc[12]; 862 char *p; 863 864 strcpy(asc, "----------"); 865 if(DMDIR & m) 866 asc[0] = 'd'; 867 if(DMAPPEND & m) 868 asc[0] = 'a'; 869 else if(DMEXCL & m) 870 asc[3] = 'l'; 871 872 for(p = asc+1; p < asc + 10; p += 3, m<<=3){ 873 if(m & 0400) 874 p[0] = 'r'; 875 if(m & 0200) 876 p[1] = 'w'; 877 if(m & 0100) 878 p[2] = 'x'; 879 } 880 return asc; 881 } 882 void 883 listfile(Biobufhdr *b, char *name, int lflag, char *dname) 884 { 885 char ts[32]; 886 int n, links, pad; 887 long now; 888 char *x; 889 Dir *d; 890 891 x = abspath(name); 892 if(x == nil) 893 return; 894 d = dirstat(x); 895 if(d == nil) 896 return; 897 if(isnone){ 898 if(strncmp(x, "/incoming/", sizeof("/incoming/")-1) != 0) 899 d->mode &= ~0222; 900 d->uid = "none"; 901 d->gid = "none"; 902 } 903 904 strcpy(ts, ctime(d->mtime)); 905 ts[16] = 0; 906 now = time(0); 907 if(now - d->mtime > 6*30*24*60*60) 908 memmove(ts+11, ts+23, 5); 909 if(lflag){ 910 /* Unix style long listing */ 911 if(DMDIR&d->mode){ 912 links = 2; 913 d->length = 512; 914 } else 915 links = 1; 916 917 Bprint(b, "%s %3d %-8s %-8s %7lld %s ", 918 mode2asc(d->mode), links, 919 d->uid, d->gid, d->length, ts+4); 920 } 921 if(Cflag && maxnamelen < 40){ 922 n = strlen(name); 923 pad = ((col+maxnamelen)/(maxnamelen+1))*(maxnamelen+1); 924 if(pad+maxnamelen+1 < 60){ 925 Bprint(b, "%*s", pad-col+n, name); 926 col = pad+n; 927 } 928 else{ 929 Bprint(b, "\r\n%s", name); 930 col = n; 931 } 932 } 933 else{ 934 if(dname) 935 Bprint(b, "%s/", dname); 936 Bprint(b, "%s\r\n", name); 937 } 938 free(d); 939 } 940 int 941 dircomp(void *va, void *vb) 942 { 943 int rv; 944 Dir *a, *b; 945 946 a = va; 947 b = vb; 948 949 if(tflag) 950 rv = b->mtime - a->mtime; 951 else 952 rv = strcmp(a->name, b->name); 953 return (rflag?-1:1)*rv; 954 } 955 void 956 listdir(char *name, Biobufhdr *b, int lflag, int *printname, Globlist *gl) 957 { 958 Dir *p; 959 int fd, n, i, l; 960 char *dname; 961 uvlong total; 962 963 col = 0; 964 965 fd = open(name, OREAD); 966 if(fd < 0){ 967 Bprint(b, "can't read %s: %r\r\n", name); 968 return; 969 } 970 dname = 0; 971 if(*printname){ 972 if(Rflag || lflag) 973 Bprint(b, "\r\n%s:\r\n", name); 974 else 975 dname = name; 976 } 977 n = dirreadall(fd, &p); 978 close(fd); 979 if(Cflag){ 980 for(i = 0; i < n; i++){ 981 l = strlen(p[i].name); 982 if(l > maxnamelen) 983 maxnamelen = l; 984 } 985 } 986 987 /* Unix style total line */ 988 if(lflag){ 989 total = 0; 990 for(i = 0; i < n; i++){ 991 if(p[i].qid.type & QTDIR) 992 total += 512; 993 else 994 total += p[i].length; 995 } 996 Bprint(b, "total %ulld\r\n", total/512); 997 } 998 999 qsort(p, n, sizeof(Dir), dircomp); 1000 for(i = 0; i < n; i++){ 1001 if(Rflag && (p[i].qid.type & QTDIR)){ 1002 *printname = 1; 1003 globadd(gl, name, p[i].name); 1004 } 1005 listfile(b, p[i].name, lflag, dname); 1006 } 1007 free(p); 1008 } 1009 void 1010 list(char *arg, int lflag) 1011 { 1012 Dir *d; 1013 Globlist *gl; 1014 Glob *g; 1015 int dfd, printname; 1016 int i, n, argc; 1017 char *alist[Narg]; 1018 char **argv; 1019 Biobufhdr bh; 1020 uchar buf[512]; 1021 char *p, *s; 1022 1023 if(arg == 0) 1024 arg = ""; 1025 1026 if(debug) 1027 logit("ls %s (. = %s)", arg, curdir); 1028 1029 /* process arguments, understand /bin/ls -l option */ 1030 argv = alist; 1031 argv[0] = "/bin/ls"; 1032 argc = getfields(arg, argv+1, Narg-2, 1, " \t") + 1; 1033 argv[argc] = 0; 1034 rflag = 0; 1035 tflag = 0; 1036 Rflag = 0; 1037 Cflag = 0; 1038 col = 0; 1039 ARGBEGIN{ 1040 case 'l': 1041 lflag++; 1042 break; 1043 case 'R': 1044 Rflag++; 1045 break; 1046 case 'C': 1047 Cflag++; 1048 break; 1049 case 'r': 1050 rflag++; 1051 break; 1052 case 't': 1053 tflag++; 1054 break; 1055 }ARGEND; 1056 if(Cflag) 1057 lflag = 0; 1058 1059 dfd = dialdata(); 1060 if(dfd < 0){ 1061 reply("425 Error opening data connection:%r"); 1062 return; 1063 } 1064 reply("150 Opened data connection (%s)", data); 1065 1066 Binits(&bh, dfd, OWRITE, buf, sizeof(buf)); 1067 if(argc == 0){ 1068 argc = 1; 1069 argv = alist; 1070 argv[0] = "."; 1071 } 1072 1073 for(i = 0; i < argc; i++){ 1074 chdir(curdir); 1075 gl = glob(argv[i]); 1076 if(gl == nil) 1077 continue; 1078 1079 printname = gl->first != nil && gl->first->next != nil; 1080 maxnamelen = 8; 1081 1082 if(Cflag) 1083 for(g = gl->first; g; g = g->next) 1084 if(g->glob && (n = strlen(s_to_c(g->glob))) > maxnamelen) 1085 maxnamelen = n; 1086 while(s = globiter(gl)){ 1087 if(debug) 1088 logit("glob %s", s); 1089 p = abspath(s); 1090 if(p == nil){ 1091 free(s); 1092 continue; 1093 } 1094 d = dirstat(p); 1095 if(d == nil){ 1096 free(s); 1097 continue; 1098 } 1099 if(d->qid.type & QTDIR) 1100 listdir(s, &bh, lflag, &printname, gl); 1101 else 1102 listfile(&bh, s, lflag, 0); 1103 free(s); 1104 free(d); 1105 } 1106 globlistfree(gl); 1107 } 1108 if(Cflag) 1109 Bprint(&bh, "\r\n"); 1110 Bflush(&bh); 1111 close(dfd); 1112 1113 reply("226 Transfer complete (list %s)", arg); 1114 } 1115 int 1116 namelistcmd(char *arg) 1117 { 1118 return asproc(list, arg, 0); 1119 } 1120 int 1121 listcmd(char *arg) 1122 { 1123 return asproc(list, arg, 1); 1124 } 1125 1126 /* 1127 * fuse compatability 1128 */ 1129 int 1130 oksiteuser(void) 1131 { 1132 char buf[64]; 1133 int fd, n; 1134 1135 fd = open("#c/user", OREAD); 1136 if(fd < 0) 1137 return 1; 1138 n = read(fd, buf, sizeof buf - 1); 1139 if(n > 0){ 1140 buf[n] = 0; 1141 if(strcmp(buf, "none") == 0) 1142 n = -1; 1143 } 1144 close(fd); 1145 return n > 0; 1146 } 1147 1148 int 1149 sitecmd(char *arg) 1150 { 1151 char *f[4]; 1152 int nf, r; 1153 Dir *d; 1154 1155 if(arg == 0) 1156 return reply("501 bad site command"); 1157 nf = tokenize(arg, f, nelem(f)); 1158 if(nf != 3 || cistrcmp(f[0], "chmod") != 0) 1159 return reply("501 bad site command"); 1160 if(!oksiteuser()) 1161 return reply("550 Permission denied"); 1162 d = dirstat(f[2]); 1163 if(d == nil) 1164 return reply("501 site chmod: file does not exist"); 1165 d->mode &= ~0777; 1166 d->mode |= strtoul(f[1], 0, 8) & 0777; 1167 r = dirwstat(f[2], d); 1168 free(d); 1169 if(r < 0) 1170 return reply("550 Permission denied %r"); 1171 return reply("200 very well, then"); 1172 } 1173 1174 /* 1175 * return the size of the file 1176 */ 1177 int 1178 sizecmd(char *arg) 1179 { 1180 Dir *d; 1181 int rv; 1182 1183 if(arg == 0) 1184 return reply("501 Size command requires pathname"); 1185 arg = abspath(arg); 1186 d = dirstat(arg); 1187 if(d == nil) 1188 return reply("501 %r accessing %s", arg); 1189 rv = reply("213 %lld", d->length); 1190 free(d); 1191 return rv; 1192 } 1193 1194 /* 1195 * return the modify time of the file 1196 */ 1197 int 1198 mdtmcmd(char *arg) 1199 { 1200 Dir *d; 1201 Tm *t; 1202 int rv; 1203 1204 if(arg == 0) 1205 return reply("501 Mdtm command requires pathname"); 1206 if(arg == 0) 1207 return reply("550 Permission denied"); 1208 d = dirstat(arg); 1209 if(d == nil) 1210 return reply("501 %r accessing %s", arg); 1211 t = gmtime(d->mtime); 1212 rv = reply("213 %4.4d%2.2d%2.2d%2.2d%2.2d%2.2d", 1213 t->year+1900, t->mon+1, t->mday, 1214 t->hour, t->min, t->sec); 1215 free(d); 1216 return rv; 1217 } 1218 1219 /* 1220 * set an offset to start reading a file from 1221 * only lasts for one command 1222 */ 1223 int 1224 restartcmd(char *arg) 1225 { 1226 if(arg == 0) 1227 return reply("501 Restart command requires offset"); 1228 offset = atoll(arg); 1229 if(offset < 0){ 1230 offset = 0; 1231 return reply("501 Bad offset"); 1232 } 1233 1234 return reply("350 Restarting at %lld. Send STORE or RETRIEVE", offset); 1235 } 1236 1237 /* 1238 * send a file to the user 1239 */ 1240 int 1241 crlfwrite(int fd, char *p, int n) 1242 { 1243 char *ep, *np; 1244 char buf[2*Nbuf]; 1245 1246 for(np = buf, ep = p + n; p < ep; p++){ 1247 if(*p == '\n') 1248 *np++ = '\r'; 1249 *np++ = *p; 1250 } 1251 if(write(fd, buf, np - buf) == np - buf) 1252 return n; 1253 else 1254 return -1; 1255 } 1256 void 1257 retrievedir(char *arg) 1258 { 1259 int n; 1260 char *p; 1261 String *file; 1262 1263 if(type != Timage){ 1264 reply("550 This file requires type binary/image"); 1265 return; 1266 } 1267 1268 file = s_copy(arg); 1269 p = strrchr(s_to_c(file), '/'); 1270 if(p != s_to_c(file)){ 1271 *p++ = 0; 1272 chdir(s_to_c(file)); 1273 } else { 1274 chdir("/"); 1275 p = s_to_c(file)+1; 1276 } 1277 1278 n = transfer("/bin/tar", "c", p, 0, 1); 1279 if(n < 0) 1280 logit("get %s failed", arg); 1281 else 1282 logit("get %s OK %d", arg, n); 1283 s_free(file); 1284 } 1285 void 1286 retrieve(char *arg, int arg2) 1287 { 1288 int dfd, fd, n, i, bytes; 1289 Dir *d; 1290 char buf[Nbuf]; 1291 char *p, *ep; 1292 1293 USED(arg2); 1294 1295 p = strchr(arg, '\r'); 1296 if(p){ 1297 logit("cr in file name", arg); 1298 *p = 0; 1299 } 1300 1301 fd = open(arg, OREAD); 1302 if(fd == -1){ 1303 n = strlen(arg); 1304 if(n > 4 && strcmp(arg+n-4, ".tar") == 0){ 1305 *(arg+n-4) = 0; 1306 d = dirstat(arg); 1307 if(d != nil){ 1308 if(d->qid.type & QTDIR){ 1309 retrievedir(arg); 1310 free(d); 1311 return; 1312 } 1313 free(d); 1314 } 1315 } 1316 logit("get %s failed", arg); 1317 reply("550 Error opening %s: %r", arg); 1318 return; 1319 } 1320 if(offset != 0) 1321 if(seek(fd, offset, 0) < 0){ 1322 reply("550 %s: seek to %lld failed", arg, offset); 1323 close(fd); 1324 return; 1325 } 1326 d = dirfstat(fd); 1327 if(d != nil){ 1328 if(d->qid.type & QTDIR){ 1329 reply("550 %s: not a plain file.", arg); 1330 close(fd); 1331 free(d); 1332 return; 1333 } 1334 free(d); 1335 } 1336 1337 n = read(fd, buf, sizeof(buf)); 1338 if(n < 0){ 1339 logit("get %s failed", arg, mailaddr, nci->rsys); 1340 reply("550 Error reading %s: %r", arg); 1341 close(fd); 1342 return; 1343 } 1344 1345 if(type != Timage) 1346 for(p = buf, ep = &buf[n]; p < ep; p++) 1347 if(*p & 0x80){ 1348 close(fd); 1349 reply("550 This file requires type binary/image"); 1350 return; 1351 } 1352 1353 reply("150 Opening data connection for %s (%s)", arg, data); 1354 dfd = dialdata(); 1355 if(dfd < 0){ 1356 reply("425 Error opening data connection:%r"); 1357 close(fd); 1358 return; 1359 } 1360 1361 bytes = 0; 1362 do { 1363 switch(type){ 1364 case Timage: 1365 i = write(dfd, buf, n); 1366 break; 1367 default: 1368 i = crlfwrite(dfd, buf, n); 1369 break; 1370 } 1371 if(i != n){ 1372 close(fd); 1373 close(dfd); 1374 logit("get %s %r to data connection after %d", arg, bytes); 1375 reply("550 Error writing to data connection: %r"); 1376 return; 1377 } 1378 bytes += n; 1379 } while((n = read(fd, buf, sizeof(buf))) > 0); 1380 1381 if(n < 0) 1382 logit("get %s %r after %d", arg, bytes); 1383 1384 close(fd); 1385 close(dfd); 1386 reply("226 Transfer complete"); 1387 logit("get %s OK %d", arg, bytes); 1388 } 1389 int 1390 retrievecmd(char *arg) 1391 { 1392 if(arg == 0) 1393 return reply("501 Retrieve command requires an argument"); 1394 arg = abspath(arg); 1395 if(arg == 0) 1396 return reply("550 Permission denied"); 1397 1398 return asproc(retrieve, arg, 0); 1399 } 1400 1401 /* 1402 * get a file from the user 1403 */ 1404 int 1405 lfwrite(int fd, char *p, int n) 1406 { 1407 char *ep, *np; 1408 char buf[Nbuf]; 1409 1410 for(np = buf, ep = p + n; p < ep; p++){ 1411 if(*p != '\r') 1412 *np++ = *p; 1413 } 1414 if(write(fd, buf, np - buf) == np - buf) 1415 return n; 1416 else 1417 return -1; 1418 } 1419 void 1420 store(char *arg, int fd) 1421 { 1422 int dfd, n, i; 1423 char buf[Nbuf]; 1424 1425 reply("150 Opening data connection for %s (%s)", arg, data); 1426 dfd = dialdata(); 1427 if(dfd < 0){ 1428 reply("425 Error opening data connection:%r"); 1429 close(fd); 1430 return; 1431 } 1432 1433 while((n = read(dfd, buf, sizeof(buf))) > 0){ 1434 switch(type){ 1435 case Timage: 1436 i = write(fd, buf, n); 1437 break; 1438 default: 1439 i = lfwrite(fd, buf, n); 1440 break; 1441 } 1442 if(i != n){ 1443 close(fd); 1444 close(dfd); 1445 reply("550 Error writing file"); 1446 return; 1447 } 1448 } 1449 close(fd); 1450 close(dfd); 1451 logit("put %s OK", arg); 1452 reply("226 Transfer complete"); 1453 } 1454 int 1455 storecmd(char *arg) 1456 { 1457 int fd, rv; 1458 1459 if(arg == 0) 1460 return reply("501 Store command requires an argument"); 1461 arg = abspath(arg); 1462 if(arg == 0) 1463 return reply("550 Permission denied"); 1464 if(isnone && strncmp(arg, "/incoming/", sizeof("/incoming/")-1)) 1465 return reply("550 Permission denied"); 1466 if(offset){ 1467 fd = open(arg, OWRITE); 1468 if(fd == -1) 1469 return reply("550 Error opening %s: %r", arg); 1470 if(seek(fd, offset, 0) == -1) 1471 return reply("550 Error seeking %s to %d: %r", 1472 arg, offset); 1473 } else { 1474 fd = create(arg, OWRITE, createperm); 1475 if(fd == -1) 1476 return reply("550 Error creating %s: %r", arg); 1477 } 1478 1479 rv = asproc(store, arg, fd); 1480 close(fd); 1481 return rv; 1482 } 1483 int 1484 appendcmd(char *arg) 1485 { 1486 int fd, rv; 1487 1488 if(arg == 0) 1489 return reply("501 Append command requires an argument"); 1490 if(isnone) 1491 return reply("550 Permission denied"); 1492 arg = abspath(arg); 1493 if(arg == 0) 1494 return reply("550 Error creating %s: Permission denied", arg); 1495 fd = open(arg, OWRITE); 1496 if(fd == -1){ 1497 fd = create(arg, OWRITE, createperm); 1498 if(fd == -1) 1499 return reply("550 Error creating %s: %r", arg); 1500 } 1501 seek(fd, 0, 2); 1502 1503 rv = asproc(store, arg, fd); 1504 close(fd); 1505 return rv; 1506 } 1507 int 1508 storeucmd(char *arg) 1509 { 1510 int fd, rv; 1511 char name[Maxpath]; 1512 1513 USED(arg); 1514 if(isnone) 1515 return reply("550 Permission denied"); 1516 strncpy(name, "ftpXXXXXXXXXXX", sizeof name); 1517 mktemp(name); 1518 fd = create(name, OWRITE, createperm); 1519 if(fd == -1) 1520 return reply("550 Error creating %s: %r", name); 1521 1522 rv = asproc(store, name, fd); 1523 close(fd); 1524 return rv; 1525 } 1526 1527 int 1528 mkdircmd(char *name) 1529 { 1530 int fd; 1531 1532 if(name == 0) 1533 return reply("501 Mkdir command requires an argument"); 1534 if(isnone) 1535 return reply("550 Permission denied"); 1536 name = abspath(name); 1537 if(name == 0) 1538 return reply("550 Permission denied"); 1539 fd = create(name, OREAD, DMDIR|0775); 1540 if(fd < 0) 1541 return reply("550 Can't create %s: %r", name); 1542 close(fd); 1543 return reply("226 %s created", name); 1544 } 1545 1546 int 1547 delcmd(char *name) 1548 { 1549 if(name == 0) 1550 return reply("501 Rmdir/delete command requires an argument"); 1551 if(isnone) 1552 return reply("550 Permission denied"); 1553 name = abspath(name); 1554 if(name == 0) 1555 return reply("550 Permission denied"); 1556 if(remove(name) < 0) 1557 return reply("550 Can't remove %s: %r", name); 1558 else 1559 return reply("226 %s removed", name); 1560 } 1561 1562 /* 1563 * kill off the last transfer (if the process still exists) 1564 */ 1565 int 1566 abortcmd(char *arg) 1567 { 1568 USED(arg); 1569 1570 logit("abort pid %d", pid); 1571 if(pid){ 1572 if(postnote(PNPROC, pid, "kill") == 0) 1573 reply("426 Command aborted"); 1574 else 1575 logit("postnote pid %d %r", pid); 1576 } 1577 return reply("226 Abort processed"); 1578 } 1579 1580 int 1581 systemcmd(char *arg) 1582 { 1583 USED(arg); 1584 return reply("215 UNIX Type: L8 Version: Plan 9"); 1585 } 1586 1587 int 1588 helpcmd(char *arg) 1589 { 1590 int i; 1591 char buf[80]; 1592 char *p, *e; 1593 1594 USED(arg); 1595 reply("214- the following commands are implemented:"); 1596 p = buf; 1597 e = buf+sizeof buf; 1598 for(i = 0; cmdtab[i].name; i++){ 1599 if((i%8) == 0){ 1600 reply("214-%s", buf); 1601 p = buf; 1602 } 1603 p = seprint(p, e, " %-5.5s", cmdtab[i].name); 1604 } 1605 if(p != buf) 1606 reply("214-%s", buf); 1607 reply("214 "); 1608 return 0; 1609 } 1610 1611 /* 1612 * renaming a file takes two commands 1613 */ 1614 static String *filepath; 1615 1616 int 1617 rnfrcmd(char *from) 1618 { 1619 if(isnone) 1620 return reply("550 Permission denied"); 1621 if(from == 0) 1622 return reply("501 Rename command requires an argument"); 1623 from = abspath(from); 1624 if(from == 0) 1625 return reply("550 Permission denied"); 1626 if(filepath == nil) 1627 filepath = s_copy(from); 1628 else{ 1629 s_reset(filepath); 1630 s_append(filepath, from); 1631 } 1632 return reply("350 Rename %s to ...", s_to_c(filepath)); 1633 } 1634 int 1635 rntocmd(char *to) 1636 { 1637 int r; 1638 Dir nd; 1639 char *fp, *tp; 1640 1641 if(isnone) 1642 return reply("550 Permission denied"); 1643 if(to == 0) 1644 return reply("501 Rename command requires an argument"); 1645 to = abspath(to); 1646 if(to == 0) 1647 return reply("550 Permission denied"); 1648 if(filepath == nil || *(s_to_c(filepath)) == 0) 1649 return reply("503 Rnto must be preceeded by an rnfr"); 1650 1651 tp = strrchr(to, '/'); 1652 fp = strrchr(s_to_c(filepath), '/'); 1653 if((tp && fp == 0) || (fp && tp == 0) 1654 || (fp && tp && (fp-s_to_c(filepath) != tp-to || memcmp(s_to_c(filepath), to, tp-to)))) 1655 return reply("550 Rename can't change directory"); 1656 if(tp) 1657 to = tp+1; 1658 1659 nulldir(&nd); 1660 nd.name = to; 1661 if(dirwstat(s_to_c(filepath), &nd) < 0) 1662 r = reply("550 Can't rename %s to %s: %r\n", s_to_c(filepath), to); 1663 else 1664 r = reply("250 %s now %s", s_to_c(filepath), to); 1665 s_reset(filepath); 1666 1667 return r; 1668 } 1669 1670 /* 1671 * to dial out we need the network file system in our 1672 * name space. 1673 */ 1674 int 1675 dialdata(void) 1676 { 1677 int fd, cfd; 1678 char ldir[40]; 1679 char err[Maxerr]; 1680 1681 if(mountnet() < 0) 1682 return -1; 1683 1684 if(!passive.inuse){ 1685 fd = dial(data, "20", 0, 0); 1686 errstr(err, sizeof err); 1687 } else { 1688 alarm(5*60*1000); 1689 cfd = listen(passive.adir, ldir); 1690 alarm(0); 1691 errstr(err, sizeof err); 1692 if(cfd < 0) 1693 return -1; 1694 fd = accept(cfd, ldir); 1695 errstr(err, sizeof err); 1696 close(cfd); 1697 } 1698 if(fd < 0) 1699 logit("can't dial %s: %s", data, err); 1700 1701 unmountnet(); 1702 werrstr(err, sizeof err); 1703 return fd; 1704 } 1705 1706 int 1707 postnote(int group, int pid, char *note) 1708 { 1709 char file[128]; 1710 int f, r; 1711 1712 /* 1713 * Use #p because /proc may not be in the namespace. 1714 */ 1715 switch(group) { 1716 case PNPROC: 1717 sprint(file, "#p/%d/note", pid); 1718 break; 1719 case PNGROUP: 1720 sprint(file, "#p/%d/notepg", pid); 1721 break; 1722 default: 1723 return -1; 1724 } 1725 1726 f = open(file, OWRITE); 1727 if(f < 0) 1728 return -1; 1729 1730 r = strlen(note); 1731 if(write(f, note, r) != r) { 1732 close(f); 1733 return -1; 1734 } 1735 close(f); 1736 return 0; 1737 } 1738 1739 /* 1740 * to circumscribe the accessible files we have to eliminate ..'s 1741 * and resolve all names from the root. We also remove any /bin/rc 1742 * special characters to avoid later problems with executed commands. 1743 */ 1744 char *special = "`;| "; 1745 1746 char* 1747 abspath(char *origpath) 1748 { 1749 char *p, *sp, *path; 1750 static String *rpath; 1751 1752 if(rpath == nil) 1753 rpath = s_new(); 1754 else 1755 s_reset(rpath); 1756 1757 if(origpath == nil) 1758 s_append(rpath, curdir); 1759 else{ 1760 if(*origpath != '/'){ 1761 s_append(rpath, curdir); 1762 s_append(rpath, "/"); 1763 } 1764 s_append(rpath, origpath); 1765 } 1766 path = s_to_c(rpath); 1767 1768 for(sp = special; *sp; sp++){ 1769 p = strchr(path, *sp); 1770 if(p) 1771 *p = 0; 1772 } 1773 1774 cleanname(s_to_c(rpath)); 1775 rpath->ptr = rpath->base+strlen(rpath->base); 1776 1777 if(!accessok(s_to_c(rpath))) 1778 return nil; 1779 1780 return s_to_c(rpath); 1781 } 1782 1783 typedef struct Path Path; 1784 struct Path { 1785 Path *next; 1786 String *path; 1787 int inuse; 1788 int ok; 1789 }; 1790 1791 enum 1792 { 1793 Maxlevel = 16, 1794 Maxperlevel= 8, 1795 }; 1796 1797 Path *pathlevel[Maxlevel]; 1798 1799 Path* 1800 unlinkpath(char *path, int level) 1801 { 1802 String *s; 1803 Path **l, *p; 1804 int n; 1805 1806 n = 0; 1807 for(l = &pathlevel[level]; *l; l = &(*l)->next){ 1808 p = *l; 1809 /* hit */ 1810 if(strcmp(s_to_c(p->path), path) == 0){ 1811 *l = p->next; 1812 p->next = nil; 1813 return p; 1814 } 1815 /* reuse */ 1816 if(++n >= Maxperlevel){ 1817 *l = p->next; 1818 s = p->path; 1819 s_reset(p->path); 1820 memset(p, 0, sizeof *p); 1821 p->path = s_append(s, path); 1822 return p; 1823 } 1824 } 1825 1826 /* allocate */ 1827 p = mallocz(sizeof *p, 1); 1828 p->path = s_copy(path); 1829 return p; 1830 } 1831 1832 void 1833 linkpath(Path *p, int level) 1834 { 1835 p->next = pathlevel[level]; 1836 pathlevel[level] = p; 1837 p->inuse = 1; 1838 } 1839 1840 void 1841 addpath(Path *p, int level, int ok) 1842 { 1843 p->ok = ok; 1844 p->next = pathlevel[level]; 1845 pathlevel[level] = p; 1846 } 1847 1848 int 1849 _accessok(String *s, int level) 1850 { 1851 Path *p; 1852 char *cp; 1853 int lvl, offset; 1854 static char httplogin[] = "/.httplogin"; 1855 1856 if(level < 0) 1857 return 1; 1858 lvl = level; 1859 if(lvl >= Maxlevel) 1860 lvl = Maxlevel - 1; 1861 1862 p = unlinkpath(s_to_c(s), lvl); 1863 if(p->inuse){ 1864 /* move to front */ 1865 linkpath(p, lvl); 1866 return p->ok; 1867 } 1868 cp = strrchr(s_to_c(s), '/'); 1869 if(cp == nil) 1870 offset = 0; 1871 else 1872 offset = cp - s_to_c(s); 1873 s_append(s, httplogin); 1874 if(access(s_to_c(s), AEXIST) == 0){ 1875 addpath(p, lvl, 0); 1876 return 0; 1877 } 1878 1879 /* 1880 * There's no way to shorten a String without 1881 * knowing the implementation. 1882 */ 1883 s->ptr = s->base+offset; 1884 s_terminate(s); 1885 addpath(p, lvl, _accessok(s, level-1)); 1886 1887 return p->ok; 1888 } 1889 1890 /* 1891 * check for a subdirectory containing .httplogin 1892 * at each level of the path. 1893 */ 1894 int 1895 accessok(char *path) 1896 { 1897 int level, r; 1898 char *p; 1899 String *npath; 1900 1901 npath = s_copy(path); 1902 p = s_to_c(npath)+1; 1903 for(level = 1; level < Maxlevel; level++){ 1904 p = strchr(p, '/'); 1905 if(p == nil) 1906 break; 1907 p++; 1908 } 1909 1910 r = _accessok(npath, level-1); 1911 s_free(npath); 1912 1913 return r; 1914 } 1915