ftpd.c 32 KB

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