ftpd.c 32 KB

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