ftpd.c 33 KB

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