ftpd.c 32 KB

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