xref: /aosp_15_r20/external/toybox/toys/net/ftpget.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1*cf5a6c84SAndroid Build Coastguard Worker /* ftpget.c - Fetch file(s) from ftp server
2*cf5a6c84SAndroid Build Coastguard Worker  *
3*cf5a6c84SAndroid Build Coastguard Worker  * Copyright 2016 Rob Landley <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker  *
5*cf5a6c84SAndroid Build Coastguard Worker  * No standard for the command, but see https://www.ietf.org/rfc/rfc959.txt
6*cf5a6c84SAndroid Build Coastguard Worker  * TODO: local can be -
7*cf5a6c84SAndroid Build Coastguard Worker  * TEST: -g -s (when local and remote exist) -gc, -sc
8*cf5a6c84SAndroid Build Coastguard Worker  * zero length file
9*cf5a6c84SAndroid Build Coastguard Worker 
10*cf5a6c84SAndroid Build Coastguard Worker USE_FTPGET(NEWTOY(ftpget, "<2>3P:cp:u:vgslLmMdD[-gs][!gslLmMdD][!clL]", TOYFLAG_USR|TOYFLAG_BIN))
11*cf5a6c84SAndroid Build Coastguard Worker USE_FTPPUT(OLDTOY(ftpput, ftpget, TOYFLAG_USR|TOYFLAG_BIN))
12*cf5a6c84SAndroid Build Coastguard Worker 
13*cf5a6c84SAndroid Build Coastguard Worker config FTPGET
14*cf5a6c84SAndroid Build Coastguard Worker   bool "ftpget"
15*cf5a6c84SAndroid Build Coastguard Worker   default y
16*cf5a6c84SAndroid Build Coastguard Worker   help
17*cf5a6c84SAndroid Build Coastguard Worker     usage: ftpget [-cvgslLmMdD] [-P PORT] [-p PASSWORD] [-u USER] HOST [LOCAL] REMOTE
18*cf5a6c84SAndroid Build Coastguard Worker 
19*cf5a6c84SAndroid Build Coastguard Worker     Talk to ftp server. By default get REMOTE file via passive anonymous
20*cf5a6c84SAndroid Build Coastguard Worker     transfer, optionally saving under a LOCAL name. Can also send, list, etc.
21*cf5a6c84SAndroid Build Coastguard Worker 
22*cf5a6c84SAndroid Build Coastguard Worker     -c	Continue partial transfer
23*cf5a6c84SAndroid Build Coastguard Worker     -p	Use PORT instead of "21"
24*cf5a6c84SAndroid Build Coastguard Worker     -P	Use PASSWORD instead of "ftpget@"
25*cf5a6c84SAndroid Build Coastguard Worker     -u	Use USER instead of "anonymous"
26*cf5a6c84SAndroid Build Coastguard Worker     -v	Verbose
27*cf5a6c84SAndroid Build Coastguard Worker 
28*cf5a6c84SAndroid Build Coastguard Worker     Ways to interact with FTP server:
29*cf5a6c84SAndroid Build Coastguard Worker     -d	Delete file
30*cf5a6c84SAndroid Build Coastguard Worker     -D	Remove directory
31*cf5a6c84SAndroid Build Coastguard Worker     -g	Get file (default)
32*cf5a6c84SAndroid Build Coastguard Worker     -l	List directory
33*cf5a6c84SAndroid Build Coastguard Worker     -L	List (filenames only)
34*cf5a6c84SAndroid Build Coastguard Worker     -m	Move file on server from LOCAL to REMOTE
35*cf5a6c84SAndroid Build Coastguard Worker     -M	mkdir
36*cf5a6c84SAndroid Build Coastguard Worker     -s	Send file
37*cf5a6c84SAndroid Build Coastguard Worker 
38*cf5a6c84SAndroid Build Coastguard Worker config FTPPUT
39*cf5a6c84SAndroid Build Coastguard Worker   bool "ftpput"
40*cf5a6c84SAndroid Build Coastguard Worker   default y
41*cf5a6c84SAndroid Build Coastguard Worker   help
42*cf5a6c84SAndroid Build Coastguard Worker     An ftpget that defaults to -s instead of -g
43*cf5a6c84SAndroid Build Coastguard Worker */
44*cf5a6c84SAndroid Build Coastguard Worker 
45*cf5a6c84SAndroid Build Coastguard Worker #define FOR_ftpget
46*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
47*cf5a6c84SAndroid Build Coastguard Worker 
48*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
49*cf5a6c84SAndroid Build Coastguard Worker   char *u, *p, *P;
50*cf5a6c84SAndroid Build Coastguard Worker 
51*cf5a6c84SAndroid Build Coastguard Worker   int fd;
52*cf5a6c84SAndroid Build Coastguard Worker )
53*cf5a6c84SAndroid Build Coastguard Worker 
54*cf5a6c84SAndroid Build Coastguard Worker // we should get one line of data, but it may be in multiple chunks
xread2line(int fd,char * buf,int len)55*cf5a6c84SAndroid Build Coastguard Worker static int xread2line(int fd, char *buf, int len)
56*cf5a6c84SAndroid Build Coastguard Worker {
57*cf5a6c84SAndroid Build Coastguard Worker   int i, total = 0;
58*cf5a6c84SAndroid Build Coastguard Worker 
59*cf5a6c84SAndroid Build Coastguard Worker   len--;
60*cf5a6c84SAndroid Build Coastguard Worker   while (total<len && (i = xread(fd, buf, len-total))) {
61*cf5a6c84SAndroid Build Coastguard Worker     total += i;
62*cf5a6c84SAndroid Build Coastguard Worker     if (buf[total-1] == '\n') break;
63*cf5a6c84SAndroid Build Coastguard Worker   }
64*cf5a6c84SAndroid Build Coastguard Worker   if (total>=len) error_exit("overflow");
65*cf5a6c84SAndroid Build Coastguard Worker   while (total--)
66*cf5a6c84SAndroid Build Coastguard Worker     if (buf[total]=='\r' || buf[total]=='\n') buf[total] = 0;
67*cf5a6c84SAndroid Build Coastguard Worker     else break;
68*cf5a6c84SAndroid Build Coastguard Worker   if (FLAG(v)) fprintf(stderr, "%s\n", toybuf);
69*cf5a6c84SAndroid Build Coastguard Worker 
70*cf5a6c84SAndroid Build Coastguard Worker   return total+1;
71*cf5a6c84SAndroid Build Coastguard Worker }
72*cf5a6c84SAndroid Build Coastguard Worker 
ftp_line(char * cmd,char * arg,int must)73*cf5a6c84SAndroid Build Coastguard Worker static int ftp_line(char *cmd, char *arg, int must)
74*cf5a6c84SAndroid Build Coastguard Worker {
75*cf5a6c84SAndroid Build Coastguard Worker   int rc = 0;
76*cf5a6c84SAndroid Build Coastguard Worker 
77*cf5a6c84SAndroid Build Coastguard Worker   if (cmd) {
78*cf5a6c84SAndroid Build Coastguard Worker     char *s = "%s %s\r\n"+3*(!arg);
79*cf5a6c84SAndroid Build Coastguard Worker     if (FLAG(v)) fprintf(stderr, s, cmd, arg);
80*cf5a6c84SAndroid Build Coastguard Worker     dprintf(TT.fd, s, cmd, arg);
81*cf5a6c84SAndroid Build Coastguard Worker   }
82*cf5a6c84SAndroid Build Coastguard Worker   if (must>=0) {
83*cf5a6c84SAndroid Build Coastguard Worker     xread2line(TT.fd, toybuf, sizeof(toybuf));
84*cf5a6c84SAndroid Build Coastguard Worker     if (!sscanf(toybuf, "%d", &rc) || (must && rc != must))
85*cf5a6c84SAndroid Build Coastguard Worker       error_exit_raw(toybuf);
86*cf5a6c84SAndroid Build Coastguard Worker   }
87*cf5a6c84SAndroid Build Coastguard Worker 
88*cf5a6c84SAndroid Build Coastguard Worker   return rc;
89*cf5a6c84SAndroid Build Coastguard Worker }
90*cf5a6c84SAndroid Build Coastguard Worker 
ftpget_main(void)91*cf5a6c84SAndroid Build Coastguard Worker void ftpget_main(void)
92*cf5a6c84SAndroid Build Coastguard Worker {
93*cf5a6c84SAndroid Build Coastguard Worker   struct sockaddr_in6 si6;
94*cf5a6c84SAndroid Build Coastguard Worker   int rc, ii = 1, port = 0;
95*cf5a6c84SAndroid Build Coastguard Worker   socklen_t sl = sizeof(si6);
96*cf5a6c84SAndroid Build Coastguard Worker   char *s, *remote = toys.optargs[2];
97*cf5a6c84SAndroid Build Coastguard Worker   unsigned long long lenl = 0, lenr;
98*cf5a6c84SAndroid Build Coastguard Worker 
99*cf5a6c84SAndroid Build Coastguard Worker   if (!(toys.optflags&(FLAG_v-1)))
100*cf5a6c84SAndroid Build Coastguard Worker     toys.optflags |= (toys.which->name[3]=='g') ? FLAG_g : FLAG_s;
101*cf5a6c84SAndroid Build Coastguard Worker 
102*cf5a6c84SAndroid Build Coastguard Worker   if (!TT.u) TT.u = "anonymous";
103*cf5a6c84SAndroid Build Coastguard Worker   if (!TT.P) TT.P = "ftpget@";
104*cf5a6c84SAndroid Build Coastguard Worker   if (!TT.p) TT.p = "21";
105*cf5a6c84SAndroid Build Coastguard Worker   if (!remote) remote = toys.optargs[1];
106*cf5a6c84SAndroid Build Coastguard Worker 
107*cf5a6c84SAndroid Build Coastguard Worker   // connect
108*cf5a6c84SAndroid Build Coastguard Worker   TT.fd = xconnectany(xgetaddrinfo(*toys.optargs, TT.p, 0, SOCK_STREAM, 0,
109*cf5a6c84SAndroid Build Coastguard Worker     AI_ADDRCONFIG));
110*cf5a6c84SAndroid Build Coastguard Worker   if (getpeername(TT.fd, (void *)&si6, &sl)) perror_exit("getpeername");
111*cf5a6c84SAndroid Build Coastguard Worker 
112*cf5a6c84SAndroid Build Coastguard Worker   // Login
113*cf5a6c84SAndroid Build Coastguard Worker   ftp_line(0, 0, 220);
114*cf5a6c84SAndroid Build Coastguard Worker   rc = ftp_line("USER", TT.u, 0);
115*cf5a6c84SAndroid Build Coastguard Worker   if (rc == 331) rc = ftp_line("PASS", TT.P, 0);
116*cf5a6c84SAndroid Build Coastguard Worker   if (rc != 230) error_exit_raw(toybuf);
117*cf5a6c84SAndroid Build Coastguard Worker 
118*cf5a6c84SAndroid Build Coastguard Worker   if (FLAG(m)) {
119*cf5a6c84SAndroid Build Coastguard Worker     if (toys.optc != 3) error_exit("-m FROM TO");
120*cf5a6c84SAndroid Build Coastguard Worker     ftp_line("RNFR", toys.optargs[1], 350);
121*cf5a6c84SAndroid Build Coastguard Worker     ftp_line("RNTO", toys.optargs[2], 250);
122*cf5a6c84SAndroid Build Coastguard Worker   } else if (FLAG(M)) ftp_line("MKD", toys.optargs[1], 257);
123*cf5a6c84SAndroid Build Coastguard Worker   else if (FLAG(d)) ftp_line("DELE", toys.optargs[1], 250);
124*cf5a6c84SAndroid Build Coastguard Worker   else if (FLAG(D)) ftp_line("RMD", toys.optargs[1], 250);
125*cf5a6c84SAndroid Build Coastguard Worker   else {
126*cf5a6c84SAndroid Build Coastguard Worker     int get = !FLAG(s), cnt = FLAG(c);
127*cf5a6c84SAndroid Build Coastguard Worker     char *cmd;
128*cf5a6c84SAndroid Build Coastguard Worker 
129*cf5a6c84SAndroid Build Coastguard Worker     // Only do passive binary transfers
130*cf5a6c84SAndroid Build Coastguard Worker     ftp_line("TYPE", "I", 0);
131*cf5a6c84SAndroid Build Coastguard Worker     rc = ftp_line("PASV", 0, 0);
132*cf5a6c84SAndroid Build Coastguard Worker 
133*cf5a6c84SAndroid Build Coastguard Worker     // PASV means the server opens a port you connect to instead of the server
134*cf5a6c84SAndroid Build Coastguard Worker     // dialing back to the client. (Still insane, but less so.) So need port #
135*cf5a6c84SAndroid Build Coastguard Worker 
136*cf5a6c84SAndroid Build Coastguard Worker     // PASV output is "227 PASV ok (x,x,x,x,p1,p2)" where x,x,x,x is the IP addr
137*cf5a6c84SAndroid Build Coastguard Worker     // (must match the server you're talking to???) and port is (256*p1)+p2
138*cf5a6c84SAndroid Build Coastguard Worker     s = 0;
139*cf5a6c84SAndroid Build Coastguard Worker     if (rc==227) for (s = toybuf; (s = strchr(s, ',')); s++) {
140*cf5a6c84SAndroid Build Coastguard Worker       int p1, got = 0;
141*cf5a6c84SAndroid Build Coastguard Worker 
142*cf5a6c84SAndroid Build Coastguard Worker       sscanf(s, ",%u,%u)%n", &p1, &port, &got);
143*cf5a6c84SAndroid Build Coastguard Worker       if (!got) continue;
144*cf5a6c84SAndroid Build Coastguard Worker       port += 256*p1;
145*cf5a6c84SAndroid Build Coastguard Worker       break;
146*cf5a6c84SAndroid Build Coastguard Worker     }
147*cf5a6c84SAndroid Build Coastguard Worker     if (!s || port<1 || port>65535) error_exit_raw(toybuf);
148*cf5a6c84SAndroid Build Coastguard Worker     si6.sin6_port = SWAP_BE16(port); // same field size/offset for v4 and v6
149*cf5a6c84SAndroid Build Coastguard Worker     port = xsocket(si6.sin6_family, SOCK_STREAM, 0);
150*cf5a6c84SAndroid Build Coastguard Worker     xconnect(port, (void *)&si6, sizeof(si6));
151*cf5a6c84SAndroid Build Coastguard Worker 
152*cf5a6c84SAndroid Build Coastguard Worker     // RETR blocks until file data read from data port, so use SIZE to check
153*cf5a6c84SAndroid Build Coastguard Worker     // if file exists before creating local copy
154*cf5a6c84SAndroid Build Coastguard Worker     lenr = 0;
155*cf5a6c84SAndroid Build Coastguard Worker     if (FLAG(s)|FLAG(g)) {
156*cf5a6c84SAndroid Build Coastguard Worker       if (ftp_line("SIZE", remote, 0) == 213)
157*cf5a6c84SAndroid Build Coastguard Worker         sscanf(toybuf, "%*u %llu", &lenr);
158*cf5a6c84SAndroid Build Coastguard Worker       else if (get) error_exit("no %s", remote);
159*cf5a6c84SAndroid Build Coastguard Worker 
160*cf5a6c84SAndroid Build Coastguard Worker       // Open file for reading or writing
161*cf5a6c84SAndroid Build Coastguard Worker       if (strcmp(toys.optargs[1], "-"))
162*cf5a6c84SAndroid Build Coastguard Worker         ii = xcreate(toys.optargs[1],
163*cf5a6c84SAndroid Build Coastguard Worker           get ? (cnt ? O_APPEND : O_TRUNC)|O_CREAT|O_WRONLY : O_RDONLY, 0666);
164*cf5a6c84SAndroid Build Coastguard Worker       lenl = fdlength(ii);
165*cf5a6c84SAndroid Build Coastguard Worker     }
166*cf5a6c84SAndroid Build Coastguard Worker     if (get) {
167*cf5a6c84SAndroid Build Coastguard Worker       cmd = "RETR";
168*cf5a6c84SAndroid Build Coastguard Worker       if (FLAG(l)) cmd = "LIST";
169*cf5a6c84SAndroid Build Coastguard Worker       if (FLAG(L)) cmd = "NLST";
170*cf5a6c84SAndroid Build Coastguard Worker       if (cnt) {
171*cf5a6c84SAndroid Build Coastguard Worker         char buf[32];
172*cf5a6c84SAndroid Build Coastguard Worker 
173*cf5a6c84SAndroid Build Coastguard Worker         if (lenl>=lenr) goto done;
174*cf5a6c84SAndroid Build Coastguard Worker         sprintf(buf, "%llu", lenl);
175*cf5a6c84SAndroid Build Coastguard Worker         ftp_line("REST", buf, 350);
176*cf5a6c84SAndroid Build Coastguard Worker       } else lenl = 0;
177*cf5a6c84SAndroid Build Coastguard Worker 
178*cf5a6c84SAndroid Build Coastguard Worker       ftp_line(cmd, remote, -1);
179*cf5a6c84SAndroid Build Coastguard Worker       lenl += xsendfile(port, ii);
180*cf5a6c84SAndroid Build Coastguard Worker       ftp_line(0, 0, FLAG(g) ? 226 : 150);
181*cf5a6c84SAndroid Build Coastguard Worker     } else if (FLAG(s)) {
182*cf5a6c84SAndroid Build Coastguard Worker       cmd = "STOR";
183*cf5a6c84SAndroid Build Coastguard Worker       if (cnt && lenr) {
184*cf5a6c84SAndroid Build Coastguard Worker         cmd = "APPE";
185*cf5a6c84SAndroid Build Coastguard Worker         xlseek(ii, lenl, SEEK_SET);
186*cf5a6c84SAndroid Build Coastguard Worker       } else lenr = 0;
187*cf5a6c84SAndroid Build Coastguard Worker       ftp_line(cmd, remote, 150);
188*cf5a6c84SAndroid Build Coastguard Worker       lenr += xsendfile(ii, port);
189*cf5a6c84SAndroid Build Coastguard Worker       close(port);
190*cf5a6c84SAndroid Build Coastguard Worker     }
191*cf5a6c84SAndroid Build Coastguard Worker     if (FLAG(s)|FLAG(g))
192*cf5a6c84SAndroid Build Coastguard Worker       if (lenl != lenr) error_exit("short %lld/%lld", lenl, lenr);
193*cf5a6c84SAndroid Build Coastguard Worker   }
194*cf5a6c84SAndroid Build Coastguard Worker   ftp_line("QUIT", 0, 0);
195*cf5a6c84SAndroid Build Coastguard Worker 
196*cf5a6c84SAndroid Build Coastguard Worker done:
197*cf5a6c84SAndroid Build Coastguard Worker   if (CFG_TOYBOX_FREE) {
198*cf5a6c84SAndroid Build Coastguard Worker     if (ii!=1) xclose(ii);
199*cf5a6c84SAndroid Build Coastguard Worker     xclose(port);
200*cf5a6c84SAndroid Build Coastguard Worker     xclose(TT.fd);
201*cf5a6c84SAndroid Build Coastguard Worker   }
202*cf5a6c84SAndroid Build Coastguard Worker }
203