1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ip.h>
5 #include <mp.h>
6 #include <libsec.h>
7 #include <auth.h>
8 #include <fcall.h>
9 #include <ctype.h>
10 #include <String.h>
11 #include "ftpfs.h"
12
13 enum
14 {
15 /* return codes */
16 Extra= 1,
17 Success= 2,
18 Incomplete= 3,
19 TempFail= 4,
20 PermFail= 5,
21 Impossible= 6,
22 };
23
24 Node *remdir; /* current directory on remote machine */
25 Node *remroot; /* root directory on remote machine */
26
27 int ctlfd; /* fd for control connection */
28 Biobuf ctlin; /* input buffer for control connection */
29 Biobuf stdin; /* input buffer for standard input */
30 Biobuf dbuf; /* buffer for data connection */
31 char msg[512]; /* buffer for replies */
32 char net[Maxpath]; /* network for connections */
33 int listenfd; /* fd to listen on for connections */
34 char netdir[Maxpath];
35 int os, defos;
36 char topsdir[64]; /* name of listed directory for TOPS */
37 String *remrootpath; /* path on remote side to remote root */
38 char *user;
39 int nopassive;
40 long lastsend;
41 extern int usetls;
42
43 static void sendrequest(char*, char*);
44 static int getreply(Biobuf*, char*, int, int);
45 static int active(int, Biobuf**, char*, char*);
46 static int passive(int, Biobuf**, char*, char*);
47 static int data(int, Biobuf**, char*, char*);
48 static int port(void);
49 static void ascii(void);
50 static void image(void);
51 static void unixpath(Node*, String*);
52 static void vmspath(Node*, String*);
53 static void mvspath(Node*, String*);
54 static Node* vmsdir(char*);
55 static int getpassword(char*, char*);
56 static int nw_mode(char dirlet, char *s);
57
58 /*
59 * connect to remote server, default network is "tcp/ip"
60 */
61 void
hello(char * dest)62 hello(char *dest)
63 {
64 char *p;
65 char dir[Maxpath];
66 TLSconn conn;
67
68 Binit(&stdin, 0, OREAD); /* init for later use */
69
70 ctlfd = dial(netmkaddr(dest, "tcp", "ftp"), 0, dir, 0);
71 if(ctlfd < 0){
72 fprint(2, "can't dial %s: %r\n", dest);
73 exits("dialing");
74 }
75
76 Binit(&ctlin, ctlfd, OREAD);
77
78 /* remember network for the data connections */
79 p = strrchr(dir+1, '/');
80 if(p == 0)
81 fatal("wrong dial(2) linked with ftp");
82 *p = 0;
83 safecpy(net, dir, sizeof(net));
84
85 /* wait for hello from other side */
86 if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
87 fatal("bad hello");
88 if(strstr(msg, "Plan 9"))
89 os = Plan9;
90
91 if(usetls){
92 sendrequest("AUTH", "TLS");
93 if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
94 fatal("bad auth tls");
95
96 ctlfd = tlsClient(ctlfd, &conn);
97 if(ctlfd < 0)
98 fatal("starting tls: %r");
99 free(conn.cert);
100
101 Binit(&ctlin, ctlfd, OREAD);
102
103 sendrequest("PBSZ", "0");
104 if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
105 fatal("bad pbsz 0");
106 sendrequest("PROT", "P");
107 if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
108 fatal("bad prot p");
109 }
110 }
111
112 /*
113 * login to remote system
114 */
115 void
rlogin(char * rsys,char * keyspec)116 rlogin(char *rsys, char *keyspec)
117 {
118 char *line;
119 char pass[128];
120 UserPasswd *up;
121
122 up = nil;
123 for(;;){
124 if(up == nil && os != Plan9)
125 up = auth_getuserpasswd(auth_getkey, "proto=pass server=%s service=ftp %s", rsys, keyspec);
126 if(up != nil){
127 sendrequest("USER", up->user);
128 } else {
129 print("User[default = %s]: ", user);
130 line = Brdline(&stdin, '\n');
131 if(line == 0)
132 exits(0);
133 line[Blinelen(&stdin)-1] = 0;
134 if(*line){
135 free(user);
136 user = strdup(line);
137 }
138 sendrequest("USER", user);
139 }
140 switch(getreply(&ctlin, msg, sizeof(msg), 1)){
141 case Success:
142 goto out;
143 case Incomplete:
144 break;
145 case TempFail:
146 case PermFail:
147 continue;
148 }
149
150 if(up != nil){
151 sendrequest("PASS", up->passwd);
152 } else {
153 if(getpassword(pass, pass+sizeof(pass)) < 0)
154 exits(0);
155 sendrequest("PASS", pass);
156 }
157 if(getreply(&ctlin, msg, sizeof(msg), 1) == Success){
158 if(strstr(msg, "Sess#"))
159 defos = MVS;
160 break;
161 }
162 }
163 out:
164 if(up != nil){
165 memset(up, 0, sizeof(*up));
166 free(up);
167 }
168 }
169
170 /*
171 * login to remote system with given user name and password.
172 */
173 void
clogin(char * cuser,char * cpassword)174 clogin(char *cuser, char *cpassword)
175 {
176 free(user);
177 user = strdup(cuser);
178 if (strcmp(user, "anonymous") != 0 &&
179 strcmp(user, "ftp") != 0)
180 fatal("User must be 'anonymous' or 'ftp'");
181 sendrequest("USER", user);
182 switch(getreply(&ctlin, msg, sizeof(msg), 1)){
183 case Success:
184 return;
185 case Incomplete:
186 break;
187 case TempFail:
188 case PermFail:
189 fatal("login failed");
190 }
191 if (cpassword == 0)
192 fatal("password needed");
193 sendrequest("PASS", cpassword);
194 if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
195 fatal("password failed");
196 if(strstr(msg, "Sess#"))
197 defos = MVS;
198 return;
199 }
200
201 /*
202 * find out about the other side. go to it's root if requested. set
203 * image mode if a Plan9 system.
204 */
205 void
preamble(char * mountroot)206 preamble(char *mountroot)
207 {
208 char *p, *ep;
209 int rv;
210 OS *o;
211
212 /*
213 * create a root directory mirror
214 */
215 remroot = newnode(0, s_copy("/"));
216 remroot->d->qid.type = QTDIR;
217 remroot->d->mode = DMDIR|0777;
218 remdir = remroot;
219
220 /*
221 * get system type
222 */
223 sendrequest("SYST", nil);
224 switch(getreply(&ctlin, msg, sizeof(msg), 1)){
225 case Success:
226 for(o = oslist; o->os != Unknown; o++)
227 if(strncmp(msg+4, o->name, strlen(o->name)) == 0)
228 break;
229 os = o->os;
230 if(os == NT)
231 os = Unix;
232 break;
233 default:
234 os = defos;
235 break;
236 }
237 if(os == Unknown)
238 os = defos;
239
240 remrootpath = s_reset(remrootpath);
241 switch(os){
242 case NetWare:
243 /*
244 * Request long, rather than 8.3 filenames,
245 * where the Servers & Volume support them.
246 */
247 sendrequest("SITE LONG", nil);
248 getreply(&ctlin, msg, sizeof(msg), 0);
249 /* FALL THRU */
250 case Unix:
251 case Plan9:
252 /*
253 * go to the remote root, if asked
254 */
255 if(mountroot){
256 sendrequest("CWD", mountroot);
257 getreply(&ctlin, msg, sizeof(msg), 0);
258 } else {
259 s_append(remrootpath, "/usr/");
260 s_append(remrootpath, user);
261 }
262
263 /*
264 * get the root directory
265 */
266 sendrequest("PWD", nil);
267 rv = getreply(&ctlin, msg, sizeof(msg), 1);
268 if(rv == PermFail){
269 sendrequest("XPWD", nil);
270 rv = getreply(&ctlin, msg, sizeof(msg), 1);
271 }
272 if(rv == Success){
273 p = strchr(msg, '"');
274 if(p){
275 p++;
276 ep = strchr(p, '"');
277 if(ep){
278 *ep = 0;
279 s_append(s_reset(remrootpath), p);
280 }
281 }
282 }
283
284 break;
285 case Tops:
286 case VM:
287 /*
288 * top directory is a figment of our imagination.
289 * make it permanently cached & valid.
290 */
291 CACHED(remroot);
292 VALID(remroot);
293 remroot->d->atime = time(0) + 100000;
294
295 /*
296 * no initial directory. We are in the
297 * imaginary root.
298 */
299 remdir = newtopsdir("???");
300 topsdir[0] = 0;
301 if(os == Tops && readdir(remdir) >= 0){
302 CACHED(remdir);
303 if(*topsdir)
304 remdir->remname = s_copy(topsdir);
305 VALID(remdir);
306 }
307 break;
308 case VMS:
309 /*
310 * top directory is a figment of our imagination.
311 * make it permanently cached & valid.
312 */
313 CACHED(remroot);
314 VALID(remroot);
315 remroot->d->atime = time(0) + 100000;
316
317 /*
318 * get current directory
319 */
320 sendrequest("PWD", nil);
321 rv = getreply(&ctlin, msg, sizeof(msg), 1);
322 if(rv == PermFail){
323 sendrequest("XPWD", nil);
324 rv = getreply(&ctlin, msg, sizeof(msg), 1);
325 }
326 if(rv == Success){
327 p = strchr(msg, '"');
328 if(p){
329 p++;
330 ep = strchr(p, '"');
331 if(ep){
332 *ep = 0;
333 remroot = remdir = vmsdir(p);
334 }
335 }
336 }
337 break;
338 case MVS:
339 usenlst = 1;
340 break;
341 }
342
343 if(os == Plan9)
344 image();
345 }
346
347 static void
ascii(void)348 ascii(void)
349 {
350 sendrequest("TYPE A", nil);
351 switch(getreply(&ctlin, msg, sizeof(msg), 0)){
352 case Success:
353 break;
354 default:
355 fatal("can't set type to ascii");
356 }
357 }
358
359 static void
image(void)360 image(void)
361 {
362 sendrequest("TYPE I", nil);
363 switch(getreply(&ctlin, msg, sizeof(msg), 0)){
364 case Success:
365 break;
366 default:
367 fatal("can't set type to image/binary");
368 }
369 }
370
371 /*
372 * decode the time fields, return seconds since epoch began
373 */
374 char *monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
375 static Tm now;
376
377 static ulong
cracktime(char * month,char * day,char * yr,char * hms)378 cracktime(char *month, char *day, char *yr, char *hms)
379 {
380 Tm tm;
381 int i;
382 char *p;
383
384
385 /* default time */
386 if(now.year == 0)
387 now = *localtime(time(0));
388 tm = now;
389 tm.yday = 0;
390
391 /* convert ascii month to a number twixt 1 and 12 */
392 if(*month >= '0' && *month <= '9'){
393 tm.mon = atoi(month) - 1;
394 if(tm.mon < 0 || tm.mon > 11)
395 tm.mon = 5;
396 } else {
397 for(p = month; *p; p++)
398 *p = tolower(*p);
399 for(i = 0; i < 12; i++)
400 if(strncmp(&monthchars[i*3], month, 3) == 0){
401 tm.mon = i;
402 break;
403 }
404 }
405
406 tm.mday = atoi(day);
407
408 if(hms){
409 tm.hour = strtol(hms, &p, 0);
410 if(*p == ':'){
411 tm.min = strtol(p+1, &p, 0);
412 if(*p == ':')
413 tm.sec = strtol(p+1, &p, 0);
414 }
415 if(tolower(*p) == 'p')
416 tm.hour += 12;
417 }
418
419 if(yr){
420 tm.year = atoi(yr);
421 if(tm.year >= 1900)
422 tm.year -= 1900;
423 } else {
424 if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
425 tm.year--;
426 }
427
428 /* convert to epoch seconds */
429 return tm2sec(&tm);
430 }
431
432 /*
433 * decode a Unix or Plan 9 file mode
434 */
435 static ulong
crackmode(char * p)436 crackmode(char *p)
437 {
438 ulong flags;
439 ulong mode;
440 int i;
441
442 flags = 0;
443 switch(strlen(p)){
444 case 10: /* unix and new style plan 9 */
445 switch(*p){
446 case 'l':
447 return DMSYML|0777;
448 case 'd':
449 flags |= DMDIR;
450 case 'a':
451 flags |= DMAPPEND;
452 }
453 p++;
454 if(p[2] == 'l')
455 flags |= DMEXCL;
456 break;
457 case 11: /* old style plan 9 */
458 switch(*p++){
459 case 'd':
460 flags |= DMDIR;
461 break;
462 case 'a':
463 flags |= DMAPPEND;
464 break;
465 }
466 if(*p++ == 'l')
467 flags |= DMEXCL;
468 break;
469 default:
470 return DMDIR|0777;
471 }
472 mode = 0;
473 for(i = 0; i < 3; i++){
474 mode <<= 3;
475 if(*p++ == 'r')
476 mode |= DMREAD;
477 if(*p++ == 'w')
478 mode |= DMWRITE;
479 if(*p == 'x' || *p == 's' || *p == 'S')
480 mode |= DMEXEC;
481 p++;
482 }
483 return mode | flags;
484 }
485
486 /*
487 * find first punctuation
488 */
489 char*
strpunct(char * p)490 strpunct(char *p)
491 {
492 int c;
493
494 for(;c = *p; p++){
495 if(ispunct(c))
496 return p;
497 }
498 return 0;
499 }
500
501 /*
502 * decode a Unix or Plan 9 directory listing
503 */
504 static Dir*
crackdir(char * p,String ** remname)505 crackdir(char *p, String **remname)
506 {
507 char *field[15];
508 char *dfield[4];
509 char *cp;
510 String *s;
511 int dn, n;
512 Dir d, *dp;
513
514 memset(&d, 0, sizeof(d));
515
516 n = getfields(p, field, 15, 1, " \t");
517 if(n > 2 && strcmp(field[n-2], "->") == 0)
518 n -= 2;
519 switch(os){
520 case TSO:
521 cp = strchr(field[0], '.');
522 if(cp){
523 *cp++ = 0;
524 s = s_copy(cp);
525 d.uid = field[0];
526 } else {
527 s = s_copy(field[0]);
528 d.uid = "TSO";
529 }
530 d.gid = "TSO";
531 d.mode = 0666;
532 d.length = 0;
533 d.atime = 0;
534 break;
535 case OS½:
536 s = s_copy(field[n-1]);
537 d.uid = "OS½";
538 d.gid = d.uid;
539 d.mode = 0666;
540 switch(n){
541 case 5:
542 if(strcmp(field[1], "DIR") == 0)
543 d.mode |= DMDIR;
544 d.length = atoll(field[0]);
545 dn = getfields(field[2], dfield, 4, 1, "-");
546 if(dn == 3)
547 d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[3]);
548 break;
549 }
550 break;
551 case Tops:
552 if(n != 4){ /* tops directory name */
553 safecpy(topsdir, field[0], sizeof(topsdir));
554 return 0;
555 }
556 s = s_copy(field[3]);
557 d.length = atoll(field[0]);
558 d.mode = 0666;
559 d.uid = "Tops";
560 d.gid = d.uid;
561 dn = getfields(field[1], dfield, 4, 1, "-");
562 if(dn == 3)
563 d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[2]);
564 else
565 d.atime = time(0);
566 break;
567 case VM:
568 switch(n){
569 case 9:
570 s = s_copy(field[0]);
571 s_append(s, ".");
572 s_append(s, field[1]);
573 d.length = atoll(field[3]) * atoll(field[4]);
574 if(*field[2] == 'F')
575 d.mode = 0666;
576 else
577 d.mode = 0777;
578 d.uid = "VM";
579 d.gid = d.uid;
580 dn = getfields(field[6], dfield, 4, 1, "/-");
581 if(dn == 3)
582 d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[7]);
583 else
584 d.atime = time(0);
585 break;
586 case 1:
587 s = s_copy(field[0]);
588 d.uid = "VM";
589 d.gid = d.uid;
590 d.mode = 0777;
591 d.atime = 0;
592 break;
593 default:
594 return nil;
595 }
596 break;
597 case VMS:
598 switch(n){
599 case 6:
600 for(cp = field[0]; *cp; cp++)
601 *cp = tolower(*cp);
602 cp = strchr(field[0], ';');
603 if(cp)
604 *cp = 0;
605 d.mode = 0666;
606 cp = field[0] + strlen(field[0]) - 4;
607 if(strcmp(cp, ".dir") == 0){
608 d.mode |= DMDIR;
609 *cp = 0;
610 }
611 s = s_copy(field[0]);
612 d.length = atoll(field[1]) * 512;
613 field[4][strlen(field[4])-1] = 0;
614 d.uid = field[4]+1;
615 d.gid = d.uid;
616 dn = getfields(field[2], dfield, 4, 1, "/-");
617 if(dn == 3)
618 d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[3]);
619 else
620 d.atime = time(0);
621 break;
622 default:
623 return nil;
624 }
625 break;
626 case NetWare:
627 switch(n){
628 case 8: /* New style */
629 s = s_copy(field[7]);
630 d.uid = field[2];
631 d.gid = d.uid;
632 d.mode = nw_mode(field[0][0], field[1]);
633 d.length = atoll(field[3]);
634 if(strchr(field[6], ':'))
635 d.atime = cracktime(field[4], field[5], nil, field[6]);
636 else
637 d.atime = cracktime(field[4], field[5], field[6], nil);
638 break;
639 case 9:
640 s = s_copy(field[8]);
641 d.uid = field[2];
642 d.gid = d.uid;
643 d.mode = 0666;
644 if(*field[0] == 'd')
645 d.mode |= DMDIR;
646 d.length = atoll(field[3]);
647 d.atime = cracktime(field[4], field[5], field[6], field[7]);
648 break;
649 case 1:
650 s = s_copy(field[0]);
651 d.uid = "none";
652 d.gid = d.uid;
653 d.mode = 0777;
654 d.atime = 0;
655 break;
656 default:
657 return nil;
658 }
659 break;
660 case Unix:
661 case Plan9:
662 default:
663 switch(n){
664 case 8: /* ls -l */
665 s = s_copy(field[7]);
666 d.uid = field[2];
667 d.gid = d.uid;
668 d.mode = crackmode(field[0]);
669 d.length = atoll(field[3]);
670 if(strchr(field[6], ':'))
671 d.atime = cracktime(field[4], field[5], 0, field[6]);
672 else
673 d.atime = cracktime(field[4], field[5], field[6], 0);
674 break;
675 case 9: /* ls -lg */
676 s = s_copy(field[8]);
677 d.uid = field[2];
678 d.gid = field[3];
679 d.mode = crackmode(field[0]);
680 d.length = atoll(field[4]);
681 if(strchr(field[7], ':'))
682 d.atime = cracktime(field[5], field[6], 0, field[7]);
683 else
684 d.atime = cracktime(field[5], field[6], field[7], 0);
685 break;
686 case 10: /* plan 9 */
687 s = s_copy(field[9]);
688 d.uid = field[3];
689 d.gid = field[4];
690 d.mode = crackmode(field[0]);
691 d.length = atoll(field[5]);
692 if(strchr(field[8], ':'))
693 d.atime = cracktime(field[6], field[7], 0, field[8]);
694 else
695 d.atime = cracktime(field[6], field[7], field[8], 0);
696 break;
697 case 4: /* a Windows_NT version */
698 s = s_copy(field[3]);
699 d.uid = "NT";
700 d.gid = d.uid;
701 if(strcmp("<DIR>", field[2]) == 0){
702 d.length = 0;
703 d.mode = DMDIR|0777;
704 } else {
705 d.mode = 0666;
706 d.length = atoll(field[2]);
707 }
708 dn = getfields(field[0], dfield, 4, 1, "/-");
709 if(dn == 3)
710 d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[1]);
711 break;
712 case 1:
713 s = s_copy(field[0]);
714 d.uid = "none";
715 d.gid = d.uid;
716 d.mode = 0777;
717 d.atime = 0;
718 break;
719 default:
720 return nil;
721 }
722 }
723 d.muid = d.uid;
724 d.qid.type = (d.mode & DMDIR) ? QTDIR : QTFILE;
725 d.mtime = d.atime;
726 if(ext && (d.qid.type & QTDIR) == 0)
727 s_append(s, ext);
728 d.name = s_to_c(s);
729
730 /* allocate a freeable dir structure */
731 dp = reallocdir(&d, 0);
732 *remname = s;
733
734 return dp;
735 }
736
737 /*
738 * probe files in a directory to see if they are directories
739 */
740 /*
741 * read a remote directory
742 */
743 int
readdir(Node * node)744 readdir(Node *node)
745 {
746 Biobuf *bp;
747 char *line;
748 Node *np;
749 Dir *d;
750 long n;
751 int tries, x, files;
752 static int uselist;
753 int usenlist;
754 String *remname;
755
756 if(changedir(node) < 0)
757 return -1;
758
759 usenlist = 0;
760 for(tries = 0; tries < 3; tries++){
761 if(usenlist || usenlst)
762 x = data(OREAD, &bp, "NLST", nil);
763 else if(os == Unix && !uselist)
764 x = data(OREAD, &bp, "LIST -l", nil);
765 else
766 x = data(OREAD, &bp, "LIST", nil);
767 switch(x){
768 case Extra:
769 break;
770 /* case TempFail:
771 continue;
772 */
773 default:
774 if(os == Unix && uselist == 0){
775 uselist = 1;
776 continue;
777 }
778 return seterr(nosuchfile);
779 }
780 files = 0;
781 while(line = Brdline(bp, '\n')){
782 n = Blinelen(bp);
783 if(debug)
784 write(2, line, n);
785 if(n > 1 && line[n-2] == '\r')
786 n--;
787 line[n - 1] = 0;
788
789 d = crackdir(line, &remname);
790 if(d == nil)
791 continue;
792 files++;
793 np = extendpath(node, remname);
794 d->qid.path = np->d->qid.path;
795 d->qid.vers = np->d->qid.vers;
796 d->type = np->d->type;
797 d->dev = 1; /* mark node as valid */
798 if(os == MVS && node == remroot){
799 d->qid.type = QTDIR;
800 d->mode |= DMDIR;
801 }
802 free(np->d);
803 np->d = d;
804 }
805 close(Bfildes(bp));
806
807 switch(getreply(&ctlin, msg, sizeof(msg), 0)){
808 case Success:
809 if(files == 0 && !usenlst && !usenlist){
810 usenlist = 1;
811 continue;
812 }
813 if(files && usenlist)
814 usenlst = 1;
815 if(usenlst)
816 node->chdirunknown = 1;
817 return 0;
818 case TempFail:
819 break;
820 default:
821 return seterr(nosuchfile);
822 }
823 }
824 return seterr(nosuchfile);
825 }
826
827 /*
828 * create a remote directory
829 */
830 int
createdir(Node * node)831 createdir(Node *node)
832 {
833 if(changedir(node->parent) < 0)
834 return -1;
835
836 sendrequest("MKD", node->d->name);
837 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
838 return -1;
839 return 0;
840 }
841
842 /*
843 * change to a remote directory.
844 */
845 int
changedir(Node * node)846 changedir(Node *node)
847 {
848 Node *to;
849 String *cdpath;
850
851 to = node;
852 if(to == remdir)
853 return 0;
854
855 /* build an absolute path */
856 switch(os){
857 case Tops:
858 case VM:
859 switch(node->depth){
860 case 0:
861 remdir = node;
862 return 0;
863 case 1:
864 cdpath = s_clone(node->remname);
865 break;
866 default:
867 return seterr(nosuchfile);
868 }
869 break;
870 case VMS:
871 switch(node->depth){
872 case 0:
873 remdir = node;
874 return 0;
875 default:
876 cdpath = s_new();
877 vmspath(node, cdpath);
878 }
879 break;
880 case MVS:
881 if(node->depth == 0)
882 cdpath = s_clone(remrootpath);
883 else{
884 cdpath = s_new();
885 mvspath(node, cdpath);
886 }
887 break;
888 default:
889 if(node->depth == 0)
890 cdpath = s_clone(remrootpath);
891 else{
892 cdpath = s_new();
893 unixpath(node, cdpath);
894 }
895 break;
896 }
897
898 uncachedir(remdir, 0);
899
900 /*
901 * connect, if we need a password (Incomplete)
902 * act like it worked (best we can do).
903 */
904 sendrequest("CWD", s_to_c(cdpath));
905 s_free(cdpath);
906 switch(getreply(&ctlin, msg, sizeof(msg), 0)){
907 case Success:
908 case Incomplete:
909 remdir = node;
910 return 0;
911 default:
912 return seterr(nosuchfile);
913 }
914 }
915
916 /*
917 * read a remote file
918 */
919 int
readfile1(Node * node)920 readfile1(Node *node)
921 {
922 Biobuf *bp;
923 char buf[4*1024];
924 long off;
925 int n;
926 int tries;
927
928 if(changedir(node->parent) < 0)
929 return -1;
930
931 for(tries = 0; tries < 4; tries++){
932 switch(data(OREAD, &bp, "RETR", s_to_c(node->remname))){
933 case Extra:
934 break;
935 case TempFail:
936 continue;
937 default:
938 return seterr(nosuchfile);
939 }
940 off = 0;
941 while((n = read(Bfildes(bp), buf, sizeof buf)) > 0){
942 if(filewrite(node, buf, off, n) != n){
943 off = -1;
944 break;
945 }
946 off += n;
947 }
948 if(off < 0)
949 return -1;
950
951 /* make sure a file gets created even for a zero length file */
952 if(off == 0)
953 filewrite(node, buf, 0, 0);
954
955 close(Bfildes(bp));
956 switch(getreply(&ctlin, msg, sizeof(msg), 0)){
957 case Success:
958 return off;
959 case TempFail:
960 continue;
961 default:
962 return seterr(nosuchfile);
963 }
964 }
965 return seterr(nosuchfile);
966 }
967
968 int
readfile(Node * node)969 readfile(Node *node)
970 {
971 int rv, inimage;
972
973 switch(os){
974 case MVS:
975 case Plan9:
976 case Tops:
977 case TSO:
978 inimage = 0;
979 break;
980 default:
981 inimage = 1;
982 image();
983 break;
984 }
985
986 rv = readfile1(node);
987
988 if(inimage)
989 ascii();
990 return rv;
991 }
992
993 /*
994 * write back a file
995 */
996 int
createfile1(Node * node)997 createfile1(Node *node)
998 {
999 Biobuf *bp;
1000 char buf[4*1024];
1001 long off;
1002 int n;
1003
1004 if(changedir(node->parent) < 0)
1005 return -1;
1006
1007 if(data(OWRITE, &bp, "STOR", s_to_c(node->remname)) != Extra)
1008 return -1;
1009 for(off = 0; ; off += n){
1010 n = fileread(node, buf, off, sizeof(buf));
1011 if(n <= 0)
1012 break;
1013 write(Bfildes(bp), buf, n);
1014 }
1015 close(Bfildes(bp));
1016 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
1017 return -1;
1018 return off;
1019 }
1020
1021 int
createfile(Node * node)1022 createfile(Node *node)
1023 {
1024 int rv;
1025
1026 switch(os){
1027 case Plan9:
1028 case Tops:
1029 break;
1030 default:
1031 image();
1032 break;
1033 }
1034 rv = createfile1(node);
1035 switch(os){
1036 case Plan9:
1037 case Tops:
1038 break;
1039 default:
1040 ascii();
1041 break;
1042 }
1043 return rv;
1044 }
1045
1046 /*
1047 * remove a remote file
1048 */
1049 int
removefile(Node * node)1050 removefile(Node *node)
1051 {
1052 if(changedir(node->parent) < 0)
1053 return -1;
1054
1055 sendrequest("DELE", s_to_c(node->remname));
1056 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
1057 return -1;
1058 return 0;
1059 }
1060
1061 /*
1062 * remove a remote directory
1063 */
1064 int
removedir(Node * node)1065 removedir(Node *node)
1066 {
1067 if(changedir(node->parent) < 0)
1068 return -1;
1069
1070 sendrequest("RMD", s_to_c(node->remname));
1071 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
1072 return -1;
1073 return 0;
1074 }
1075
1076 /*
1077 * tell remote that we're exiting and then do it
1078 */
1079 void
quit(void)1080 quit(void)
1081 {
1082 sendrequest("QUIT", nil);
1083 getreply(&ctlin, msg, sizeof(msg), 0);
1084 exits(0);
1085 }
1086
1087 /*
1088 * send a request
1089 */
1090 static void
sendrequest(char * a,char * b)1091 sendrequest(char *a, char *b)
1092 {
1093 char buf[2*1024];
1094 int n;
1095
1096 n = strlen(a)+2+1;
1097 if(b != nil)
1098 n += strlen(b)+1;
1099 if(n >= sizeof(buf))
1100 fatal("proto request too long");
1101 strcpy(buf, a);
1102 if(b != nil){
1103 strcat(buf, " ");
1104 strcat(buf, b);
1105 }
1106 strcat(buf, "\r\n");
1107 n = strlen(buf);
1108 if(write(ctlfd, buf, n) != n)
1109 fatal("remote side hung up");
1110 if(debug)
1111 write(2, buf, n);
1112 lastsend = time(0);
1113 }
1114
1115 /*
1116 * replies codes are in the range [100, 999] and may contain multiple lines of
1117 * continuation.
1118 */
1119 static int
getreply(Biobuf * bp,char * msg,int len,int printreply)1120 getreply(Biobuf *bp, char *msg, int len, int printreply)
1121 {
1122 char *line;
1123 char *p;
1124 int rv;
1125 int i, n;
1126
1127 while(line = Brdline(bp, '\n')){
1128 /* add line to message buffer, strip off \r */
1129 n = Blinelen(bp);
1130 if(n > 1 && line[n-2] == '\r'){
1131 n--;
1132 line[n-1] = '\n';
1133 }
1134 if(printreply && !quiet)
1135 write(1, line, n);
1136 else if(debug)
1137 write(2, line, n);
1138 if(n > len - 1)
1139 i = len - 1;
1140 else
1141 i = n;
1142 if(i > 0){
1143 memmove(msg, line, i);
1144 msg += i;
1145 len -= i;
1146 *msg = 0;
1147 }
1148
1149 /* stop if not a continuation */
1150 rv = strtol(line, &p, 10);
1151 if(rv >= 100 && rv < 600 && p==line+3 && *p != '-')
1152 return rv/100;
1153
1154 /* tell user about continuations */
1155 if(!debug && !quiet && !printreply)
1156 write(2, line, n);
1157 }
1158
1159 fatal("remote side closed connection");
1160 return 0;
1161 }
1162
1163 /*
1164 * Announce on a local port and tell its address to the the remote side
1165 */
1166 static int
port(void)1167 port(void)
1168 {
1169 char buf[256];
1170 int n, fd;
1171 char *ptr;
1172 uchar ipaddr[IPaddrlen];
1173 int port;
1174
1175 /* get a channel to listen on, let kernel pick the port number */
1176 sprint(buf, "%s!*!0", net);
1177 listenfd = announce(buf, netdir);
1178 if(listenfd < 0)
1179 return seterr("can't announce");
1180
1181 /* get the local address and port number */
1182 sprint(buf, "%s/local", netdir);
1183 fd = open(buf, OREAD);
1184 if(fd < 0)
1185 return seterr("opening %s: %r", buf);
1186 n = read(fd, buf, sizeof(buf)-1);
1187 close(fd);
1188 if(n <= 0)
1189 return seterr("opening %s/local: %r", netdir);
1190 buf[n] = 0;
1191 ptr = strchr(buf, ' ');
1192 if(ptr)
1193 *ptr = 0;
1194 ptr = strchr(buf, '!')+1;
1195 port = atoi(ptr);
1196
1197 memset(ipaddr, 0, IPaddrlen);
1198 if (*net){
1199 strcpy(buf, net);
1200 ptr = strchr(buf +1, '/');
1201 if (ptr)
1202 *ptr = 0;
1203 myipaddr(ipaddr, buf);
1204 }
1205
1206 /* tell remote side */
1207 sprint(buf, "PORT %d,%d,%d,%d,%d,%d", ipaddr[IPv4off+0], ipaddr[IPv4off+1],
1208 ipaddr[IPv4off+2], ipaddr[IPv4off+3], port>>8, port&0xff);
1209 sendrequest(buf, nil);
1210 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
1211 return seterr(msg);
1212 return 0;
1213 }
1214
1215 /*
1216 * have server call back for a data connection
1217 */
1218 static int
active(int mode,Biobuf ** bpp,char * cmda,char * cmdb)1219 active(int mode, Biobuf **bpp, char *cmda, char *cmdb)
1220 {
1221 int cfd, dfd, rv;
1222 char newdir[Maxpath];
1223 char datafile[Maxpath + 6];
1224 TLSconn conn;
1225
1226 if(port() < 0)
1227 return TempFail;
1228
1229 sendrequest(cmda, cmdb);
1230
1231 rv = getreply(&ctlin, msg, sizeof(msg), 0);
1232 if(rv != Extra){
1233 close(listenfd);
1234 return rv;
1235 }
1236
1237 /* wait for a new call */
1238 cfd = listen(netdir, newdir);
1239 if(cfd < 0)
1240 fatal("waiting for data connection");
1241 close(listenfd);
1242
1243 /* open it's data connection and close the control connection */
1244 sprint(datafile, "%s/data", newdir);
1245 dfd = open(datafile, ORDWR);
1246 close(cfd);
1247 if(dfd < 0)
1248 fatal("opening data connection");
1249
1250 if(usetls){
1251 memset(&conn, 0, sizeof(conn));
1252 dfd = tlsClient(dfd, &conn);
1253 if(dfd < 0)
1254 fatal("starting tls: %r");
1255 free(conn.cert);
1256 }
1257
1258 Binit(&dbuf, dfd, mode);
1259 *bpp = &dbuf;
1260 return Extra;
1261 }
1262
1263 /*
1264 * call out for a data connection
1265 */
1266 static int
passive(int mode,Biobuf ** bpp,char * cmda,char * cmdb)1267 passive(int mode, Biobuf **bpp, char *cmda, char *cmdb)
1268 {
1269 char msg[1024];
1270 char ds[1024];
1271 char *f[6];
1272 char *p;
1273 int x, fd;
1274 TLSconn conn;
1275
1276 if(nopassive)
1277 return Impossible;
1278
1279 sendrequest("PASV", nil);
1280 if(getreply(&ctlin, msg, sizeof(msg), 0) != Success){
1281 nopassive = 1;
1282 return Impossible;
1283 }
1284
1285 /* get address and port number from reply, this is AI */
1286 p = strchr(msg, '(');
1287 if(p == 0){
1288 for(p = msg+3; *p; p++)
1289 if(isdigit(*p))
1290 break;
1291 } else
1292 p++;
1293 if(getfields(p, f, 6, 0, ",") < 6){
1294 if(debug)
1295 fprint(2, "passive mode protocol botch: %s\n", msg);
1296 werrstr("ftp protocol botch");
1297 nopassive = 1;
1298 return Impossible;
1299 }
1300 snprint(ds, sizeof(ds), "%s!%s.%s.%s.%s!%d", net,
1301 f[0], f[1], f[2], f[3],
1302 ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff));
1303
1304 /* open data connection */
1305 fd = dial(ds, 0, 0, 0);
1306 if(fd < 0){
1307 if(debug)
1308 fprint(2, "passive mode connect to %s failed: %r\n", ds);
1309 nopassive = 1;
1310 return TempFail;
1311 }
1312
1313 /* tell remote to send a file */
1314 sendrequest(cmda, cmdb);
1315 x = getreply(&ctlin, msg, sizeof(msg), 0);
1316 if(x != Extra){
1317 close(fd);
1318 if(debug)
1319 fprint(2, "passive mode retrieve failed: %s\n", msg);
1320 werrstr(msg);
1321 return x;
1322 }
1323
1324 if(usetls){
1325 memset(&conn, 0, sizeof(conn));
1326 fd = tlsClient(fd, &conn);
1327 if(fd < 0)
1328 fatal("starting tls: %r");
1329 free(conn.cert);
1330 }
1331 Binit(&dbuf, fd, mode);
1332
1333 *bpp = &dbuf;
1334 return Extra;
1335 }
1336
1337 static int
data(int mode,Biobuf ** bpp,char * cmda,char * cmdb)1338 data(int mode, Biobuf **bpp, char* cmda, char *cmdb)
1339 {
1340 int x;
1341
1342 x = passive(mode, bpp, cmda, cmdb);
1343 if(x != Impossible)
1344 return x;
1345 return active(mode, bpp, cmda, cmdb);
1346 }
1347
1348 /*
1349 * used for keep alives
1350 */
1351 void
nop(void)1352 nop(void)
1353 {
1354 if(lastsend - time(0) < 15)
1355 return;
1356 sendrequest("PWD", nil);
1357 getreply(&ctlin, msg, sizeof(msg), 0);
1358 }
1359
1360 /*
1361 * turn a vms spec into a path
1362 */
1363 static Node*
vmsextendpath(Node * np,char * name)1364 vmsextendpath(Node *np, char *name)
1365 {
1366 np = extendpath(np, s_copy(name));
1367 if(!ISVALID(np)){
1368 np->d->qid.type = QTDIR;
1369 np->d->atime = time(0);
1370 np->d->mtime = np->d->atime;
1371 strcpy(np->d->uid, "who");
1372 strcpy(np->d->gid, "cares");
1373 np->d->mode = DMDIR|0777;
1374 np->d->length = 0;
1375 if(changedir(np) >= 0)
1376 VALID(np);
1377 }
1378 return np;
1379 }
1380 static Node*
vmsdir(char * name)1381 vmsdir(char *name)
1382 {
1383 char *cp;
1384 Node *np;
1385 char *oname;
1386
1387 np = remroot;
1388 cp = strchr(name, '[');
1389 if(cp)
1390 strcpy(cp, cp+1);
1391 cp = strchr(name, ']');
1392 if(cp)
1393 *cp = 0;
1394 oname = name = strdup(name);
1395 if(name == 0)
1396 return 0;
1397
1398 while(cp = strchr(name, '.')){
1399 *cp = 0;
1400 np = vmsextendpath(np, name);
1401 name = cp+1;
1402 }
1403 np = vmsextendpath(np, name);
1404
1405 /*
1406 * walk back to first accessible directory
1407 */
1408 for(; np->parent != np; np = np->parent)
1409 if(ISVALID(np)){
1410 CACHED(np->parent);
1411 break;
1412 }
1413
1414 free(oname);
1415 return np;
1416 }
1417
1418 /*
1419 * walk up the tree building a VMS style path
1420 */
1421 static void
vmspath(Node * node,String * path)1422 vmspath(Node *node, String *path)
1423 {
1424 char *p;
1425 int n;
1426
1427 if(node->depth == 1){
1428 p = strchr(s_to_c(node->remname), ':');
1429 if(p){
1430 n = p - s_to_c(node->remname) + 1;
1431 s_nappend(path, s_to_c(node->remname), n);
1432 s_append(path, "[");
1433 s_append(path, p+1);
1434 } else {
1435 s_append(path, "[");
1436 s_append(path, s_to_c(node->remname));
1437 }
1438 s_append(path, "]");
1439 return;
1440 }
1441 vmspath(node->parent, path);
1442 s_append(path, ".");
1443 s_append(path, s_to_c(node->remname));
1444 }
1445
1446 /*
1447 * walk up the tree building a Unix style path
1448 */
1449 static void
unixpath(Node * node,String * path)1450 unixpath(Node *node, String *path)
1451 {
1452 if(node == node->parent){
1453 s_append(path, s_to_c(remrootpath));
1454 return;
1455 }
1456 unixpath(node->parent, path);
1457 if(s_len(path) > 0 && strcmp(s_to_c(path), "/") != 0)
1458 s_append(path, "/");
1459 s_append(path, s_to_c(node->remname));
1460 }
1461
1462 /*
1463 * walk up the tree building a MVS style path
1464 */
1465 static void
mvspath(Node * node,String * path)1466 mvspath(Node *node, String *path)
1467 {
1468 if(node == node->parent){
1469 s_append(path, s_to_c(remrootpath));
1470 return;
1471 }
1472 mvspath(node->parent, path);
1473 if(s_len(path) > 0)
1474 s_append(path, ".");
1475 s_append(path, s_to_c(node->remname));
1476 }
1477
1478 static int
getpassword(char * buf,char * e)1479 getpassword(char *buf, char *e)
1480 {
1481 char *p;
1482 int c;
1483 int consctl, rv = 0;
1484
1485 consctl = open("/dev/consctl", OWRITE);
1486 if(consctl >= 0)
1487 write(consctl, "rawon", 5);
1488 print("Password: ");
1489 e--;
1490 for(p = buf; p <= e; p++){
1491 c = Bgetc(&stdin);
1492 if(c < 0){
1493 rv = -1;
1494 goto out;
1495 }
1496 if(c == '\n' || c == '\r')
1497 break;
1498 *p = c;
1499 }
1500 *p = 0;
1501 print("\n");
1502
1503 out:
1504 if(consctl >= 0)
1505 close(consctl);
1506 return rv;
1507 }
1508
1509 /*
1510 * convert from latin1 to utf
1511 */
1512 static char*
fromlatin1(char * from)1513 fromlatin1(char *from)
1514 {
1515 char *p, *to;
1516 Rune r;
1517
1518 if(os == Plan9)
1519 return nil;
1520
1521 /* don't convert if we don't have to */
1522 for(p = from; *p; p++)
1523 if(*p & 0x80)
1524 break;
1525 if(*p == 0)
1526 return nil;
1527
1528 to = malloc(3*strlen(from)+2);
1529 if(to == nil)
1530 return nil;
1531 for(p = to; *from; from++){
1532 r = (*from) & 0xff;
1533 p += runetochar(p, &r);
1534 }
1535 *p = 0;
1536 return to;
1537 }
1538
1539 Dir*
reallocdir(Dir * d,int dofree)1540 reallocdir(Dir *d, int dofree)
1541 {
1542 Dir *dp;
1543 char *p;
1544 int nn, ng, nu, nm;
1545 char *utf;
1546
1547 if(d->name == nil)
1548 d->name = "?name?";
1549 if(d->uid == nil)
1550 d->uid = "?uid?";
1551 if(d->gid == nil)
1552 d->gid = d->uid;
1553 if(d->muid == nil)
1554 d->muid = d->uid;
1555
1556 utf = fromlatin1(d->name);
1557 if(utf != nil)
1558 d->name = utf;
1559
1560 nn = strlen(d->name)+1;
1561 nu = strlen(d->uid)+1;
1562 ng = strlen(d->gid)+1;
1563 nm = strlen(d->muid)+1;
1564 dp = malloc(sizeof(Dir)+nn+nu+ng+nm);
1565 if(dp == nil){
1566 if(dofree)
1567 free(d);
1568 if(utf != nil)
1569 free(utf);
1570 return nil;
1571 }
1572 *dp = *d;
1573 p = (char*)&dp[1];
1574 strcpy(p, d->name);
1575 dp->name = p;
1576 p += nn;
1577 strcpy(p, d->uid);
1578 dp->uid = p;
1579 p += nu;
1580 strcpy(p, d->gid);
1581 dp->gid = p;
1582 p += ng;
1583 strcpy(p, d->muid);
1584 dp->muid = p;
1585 if(dofree)
1586 free(d);
1587 if(utf != nil)
1588 free(utf);
1589 return dp;
1590 }
1591
1592 Dir*
dir_change_name(Dir * d,char * name)1593 dir_change_name(Dir *d, char *name)
1594 {
1595 if(d->name && strlen(d->name) >= strlen(name)){
1596 strcpy(d->name, name);
1597 return d;
1598 }
1599 d->name = name;
1600 return reallocdir(d, 1);
1601 }
1602
1603 Dir*
dir_change_uid(Dir * d,char * name)1604 dir_change_uid(Dir *d, char *name)
1605 {
1606 if(d->uid && strlen(d->uid) >= strlen(name)){
1607 strcpy(d->name, name);
1608 return d;
1609 }
1610 d->uid = name;
1611 return reallocdir(d, 1);
1612 }
1613
1614 Dir*
dir_change_gid(Dir * d,char * name)1615 dir_change_gid(Dir *d, char *name)
1616 {
1617 if(d->gid && strlen(d->gid) >= strlen(name)){
1618 strcpy(d->name, name);
1619 return d;
1620 }
1621 d->gid = name;
1622 return reallocdir(d, 1);
1623 }
1624
1625 Dir*
dir_change_muid(Dir * d,char * name)1626 dir_change_muid(Dir *d, char *name)
1627 {
1628 if(d->muid && strlen(d->muid) >= strlen(name)){
1629 strcpy(d->name, name);
1630 return d;
1631 }
1632 d->muid = name;
1633 return reallocdir(d, 1);
1634 }
1635
1636 static int
nw_mode(char dirlet,char * s)1637 nw_mode(char dirlet, char *s) /* NetWare file mode mapping */
1638 {
1639 int mode = 0777;
1640
1641 if(dirlet == 'd')
1642 mode |= DMDIR;
1643
1644 if (strlen(s) >= 10 && s[0] != '[' || s[9] != ']')
1645 return(mode);
1646
1647 if (s[1] == '-') /* can't read file */
1648 mode &= ~0444;
1649 if (dirlet == 'd' && s[6] == '-') /* cannot scan dir */
1650 mode &= ~0444;
1651 if (s[2] == '-') /* can't write file */
1652 mode &= ~0222;
1653 if (dirlet == 'd' && s[7] == '-' && s[3] == '-') /* cannot create in, or modify dir */
1654 mode &= ~0222;
1655
1656 return(mode);
1657 }
1658