aboutsummaryrefslogtreecommitdiff
path: root/networking/ftpgetput.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-03-29 07:40:35 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-03-29 07:40:35 +0000
commit7cb808e1c5d6184dc3846f65344c7879f60de4f3 (patch)
tree6481bc073acb40bc64629bdaa62f765e415b23a9 /networking/ftpgetput.c
parent0e7940ae906ea9f29050323fced2bc48f70008af (diff)
downloadbusybox-w32-7cb808e1c5d6184dc3846f65344c7879f60de4f3.tar.gz
busybox-w32-7cb808e1c5d6184dc3846f65344c7879f60de4f3.tar.bz2
busybox-w32-7cb808e1c5d6184dc3846f65344c7879f60de4f3.zip
ftpgetput: move control_stream to "struct globals";
unify common data pumpung stage for upload and download; other small shrinkage. EPSV still not implemented... function old new delta pump_data_and_QUIT - 82 +82 xconnect_ftpdata 127 156 +29 ftpcmd 301 304 +3 ftp_die 59 55 -4 ftpgetput_main 352 344 -8 ftp_send 185 91 -94 ftp_receive 394 293 -101 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/4 up/down: 114/-207) Total: -93 bytes
Diffstat (limited to 'networking/ftpgetput.c')
-rw-r--r--networking/ftpgetput.c195
1 files changed, 85 insertions, 110 deletions
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index 57adb52c1..be3d5a673 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -19,21 +19,23 @@ struct globals {
19 const char *user; 19 const char *user;
20 const char *password; 20 const char *password;
21 struct len_and_sockaddr *lsa; 21 struct len_and_sockaddr *lsa;
22 FILE *control_stream;
22 int verbose_flag; 23 int verbose_flag;
23 int do_continue; 24 int do_continue;
24 char buf[1]; /* actually [BUF_SIZE] */ 25 char buf[1]; /* actually [BUFSZ] */
25}; 26};
26#define G (*(struct globals*)&bb_common_bufsiz1) 27#define G (*(struct globals*)&bb_common_bufsiz1)
27enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) }; 28enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) };
28struct BUG_G_too_big { 29struct BUG_G_too_big {
29 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; 30 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
30}; 31};
31#define user (G.user ) 32#define user (G.user )
32#define password (G.password ) 33#define password (G.password )
33#define lsa (G.lsa ) 34#define lsa (G.lsa )
34#define verbose_flag (G.verbose_flag) 35#define control_stream (G.control_stream)
35#define do_continue (G.do_continue ) 36#define verbose_flag (G.verbose_flag )
36#define buf (G.buf ) 37#define do_continue (G.do_continue )
38#define buf (G.buf )
37#define INIT_G() do { \ 39#define INIT_G() do { \
38} while (0) 40} while (0)
39 41
@@ -41,36 +43,34 @@ struct BUG_G_too_big {
41static void ftp_die(const char *msg) ATTRIBUTE_NORETURN; 43static void ftp_die(const char *msg) ATTRIBUTE_NORETURN;
42static void ftp_die(const char *msg) 44static void ftp_die(const char *msg)
43{ 45{
44 const char *cp = buf; /* buf holds peer's response */ 46 char *cp = buf; /* buf holds peer's response */
45 47
46 /* Guard against garbage from remote server */ 48 /* Guard against garbage from remote server */
47 while (*cp >= ' ' && *cp < '\x7f') 49 while (*cp >= ' ' && *cp < '\x7f')
48 cp++; 50 cp++;
49 bb_error_msg_and_die("unexpected server response%s%s: %.*s", 51 *cp = '\0';
50 msg ? " to " : "", msg ? msg : "", 52 bb_error_msg_and_die("unexpected server response%s%s: %s",
51 (int)(cp - buf), buf); 53 (msg ? " to " : ""), (msg ? msg : ""), buf);
52} 54}
53 55
54static int ftpcmd(const char *s1, const char *s2, FILE *stream) 56static int ftpcmd(const char *s1, const char *s2)
55{ 57{
56 unsigned n; 58 unsigned n;
59
57 if (verbose_flag) { 60 if (verbose_flag) {
58 bb_error_msg("cmd %s %s", s1, s2); 61 bb_error_msg("cmd %s %s", s1, s2);
59 } 62 }
60 63
61 if (s1) { 64 if (s1) {
62 fprintf(stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3), s1, s2); 65 fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3),
66 s1, s2);
67 fflush(control_stream);
63 } 68 }
64 69
65 do { 70 do {
66 char *buf_ptr; 71 strcpy(buf, "EOF");
67 72 if (fgets(buf, BUFSZ - 2, control_stream) == NULL) {
68 if (fgets(buf, BUFSZ - 2, stream) == NULL) { 73 ftp_die(NULL);
69 bb_perror_msg_and_die("fgets");
70 }
71 buf_ptr = strstr(buf, "\r\n");
72 if (buf_ptr) {
73 *buf_ptr = '\0';
74 } 74 }
75 } while (!isdigit(buf[0]) || buf[3] != ' '); 75 } while (!isdigit(buf[0]) || buf[3] != ' ');
76 76
@@ -80,10 +80,8 @@ static int ftpcmd(const char *s1, const char *s2, FILE *stream)
80 return n; 80 return n;
81} 81}
82 82
83static FILE *ftp_login(void) 83static void ftp_login(void)
84{ 84{
85 FILE *control_stream;
86
87 /* Connect to the command socket */ 85 /* Connect to the command socket */
88 control_stream = fdopen(xconnect_stream(lsa), "r+"); 86 control_stream = fdopen(xconnect_stream(lsa), "r+");
89 if (control_stream == NULL) { 87 if (control_stream == NULL) {
@@ -91,16 +89,16 @@ static FILE *ftp_login(void)
91 bb_perror_nomsg_and_die(); 89 bb_perror_nomsg_and_die();
92 } 90 }
93 91
94 if (ftpcmd(NULL, NULL, control_stream) != 220) { 92 if (ftpcmd(NULL, NULL) != 220) {
95 ftp_die(NULL); 93 ftp_die(NULL);
96 } 94 }
97 95
98 /* Login to the server */ 96 /* Login to the server */
99 switch (ftpcmd("USER", user, control_stream)) { 97 switch (ftpcmd("USER", user)) {
100 case 230: 98 case 230:
101 break; 99 break;
102 case 331: 100 case 331:
103 if (ftpcmd("PASS", password, control_stream) != 230) { 101 if (ftpcmd("PASS", password) != 230) {
104 ftp_die("PASS"); 102 ftp_die("PASS");
105 } 103 }
106 break; 104 break;
@@ -108,9 +106,7 @@ static FILE *ftp_login(void)
108 ftp_die("USER"); 106 ftp_die("USER");
109 } 107 }
110 108
111 ftpcmd("TYPE I", NULL, control_stream); 109 ftpcmd("TYPE I", NULL);
112
113 return control_stream;
114} 110}
115 111
116static int xconnect_ftpdata(void) 112static int xconnect_ftpdata(void)
@@ -118,6 +114,30 @@ static int xconnect_ftpdata(void)
118 char *buf_ptr; 114 char *buf_ptr;
119 unsigned port_num; 115 unsigned port_num;
120 116
117/*
118TODO: PASV command will not work for IPv6. RFC2428 describes
119IPv6-capable "extended PASV" - EPSV.
120
121"EPSV [protocol]" asks server to bind to and listen on a data port
122in specified protocol. Protocol is 1 for IPv4, 2 for IPv6.
123If not specified, defaults to "same as used for control connection".
124If server understood you, it should answer "229 <some text>(|||port|)"
125where "|" are literal pipe chars and "port" is ASCII decimal port#.
126
127There is also an IPv6-capable replacement for PORT (EPRT),
128but we don't need that.
129
130NB: PASV may still work for some servers even over IPv6.
131For example, vsftp happily answers
132"227 Entering Passive Mode (0,0,0,0,n,n)" and proceeds as usual.
133
134TODO2: need to stop ignoring IP address in PASV response.
135*/
136
137 if (ftpcmd("PASV", NULL) != 227) {
138 ftp_die("PASV");
139 }
140
121 /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage] 141 /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage]
122 * Server's IP is N1.N2.N3.N4 (we ignore it) 142 * Server's IP is N1.N2.N3.N4 (we ignore it)
123 * Server's port for data connection is P1*256+P2 */ 143 * Server's port for data connection is P1*256+P2 */
@@ -136,44 +156,41 @@ static int xconnect_ftpdata(void)
136 return xconnect_stream(lsa); 156 return xconnect_stream(lsa);
137} 157}
138 158
159static int pump_data_and_QUIT(int from, int to)
160{
161 /* copy the file */
162 if (bb_copyfd_eof(from, to) == -1) {
163 /* error msg is already printed by bb_copyfd_eof */
164 return EXIT_FAILURE;
165 }
166
167 /* close data connection */
168 close(from); /* don't know which one is that, so we close both */
169 close(to);
170
171 /* does server confirm that transfer is finished? */
172 if (ftpcmd(NULL, NULL) != 226) {
173 ftp_die(NULL);
174 }
175 ftpcmd("QUIT", NULL);
176
177 return EXIT_SUCCESS;
178}
179
139#if !ENABLE_FTPGET 180#if !ENABLE_FTPGET
140int ftp_receive(FILE *control_stream, 181int ftp_receive(const char *local_path, char *server_path);
141 const char *local_path, char *server_path);
142#else 182#else
143static 183static
144int ftp_receive(FILE *control_stream, 184int ftp_receive(const char *local_path, char *server_path)
145 const char *local_path, char *server_path)
146{ 185{
147#define filesize ((off_t)-1)
148 int fd_data; 186 int fd_data;
149 int fd_local = -1; 187 int fd_local = -1;
150 off_t beg_range = 0; 188 off_t beg_range = 0;
151 189
152/*
153TODO: PASV command will not work for IPv6. RFC2428 describes
154IPv6-capable "extended PASV" - EPSV.
155
156"EPSV [protocol]" asks server to bind to and listen on a data port
157in specified protocol. Protocol is 1 for IPv4, 2 for IPv6.
158If not specified, defaults to "same as used for control connection".
159If server understood you, it should answer "229 <some text>(|||port|)"
160where "|" are literal pipe chars and "port" is ASCII decimal port#.
161
162There is also an IPv6-capable replacement for PORT (EPRT),
163but we don't need that.
164
165TODO: fold in sending of PASV/EPSV and parsing of response into
166xconnect_ftpdata(). (Also, need to stop ignoring IP address in PASV
167response).
168*/
169
170 /* connect to the data socket */ 190 /* connect to the data socket */
171 if (ftpcmd("PASV", NULL, control_stream) != 227) {
172 ftp_die("PASV");
173 }
174 fd_data = xconnect_ftpdata(); 191 fd_data = xconnect_ftpdata();
175 192
176 if (ftpcmd("SIZE", server_path, control_stream) != 213) { 193 if (ftpcmd("SIZE", server_path) != 213) {
177 do_continue = 0; 194 do_continue = 0;
178 } 195 }
179 196
@@ -197,16 +214,16 @@ response).
197 214
198 if (do_continue) { 215 if (do_continue) {
199 sprintf(buf, "REST %"OFF_FMT"d", beg_range); 216 sprintf(buf, "REST %"OFF_FMT"d", beg_range);
200 if (ftpcmd(buf, NULL, control_stream) != 350) { 217 if (ftpcmd(buf, NULL) != 350) {
201 do_continue = 0; 218 do_continue = 0;
202 } 219 }
203 } 220 }
204 221
205 if (ftpcmd("RETR", server_path, control_stream) > 150) { 222 if (ftpcmd("RETR", server_path) > 150) {
206 ftp_die("RETR"); 223 ftp_die("RETR");
207 } 224 }
208 225
209 /* make local _after_ we know that remote file exists */ 226 /* create local file _after_ we know that remote file exists */
210 if (fd_local == -1) { 227 if (fd_local == -1) {
211 fd_local = xopen(local_path, 228 fd_local = xopen(local_path,
212 do_continue ? (O_APPEND | O_WRONLY) 229 do_continue ? (O_APPEND | O_WRONLY)
@@ -214,41 +231,21 @@ response).
214 ); 231 );
215 } 232 }
216 233
217// TODO: merge tail of ftp_receive and ftp_send starting from here 234 return pump_data_and_QUIT(fd_data, fd_local);
218
219 /* copy the file */
220 if (bb_copyfd_eof(fd_data, fd_local) == -1) {
221 /* error msg is already printed by bb_copyfd_eof */
222 return EXIT_FAILURE;
223 }
224
225 /* close it all down */
226 close(fd_data);
227 if (ftpcmd(NULL, NULL, control_stream) != 226) {
228 ftp_die(NULL);
229 }
230 ftpcmd("QUIT", NULL, control_stream);
231
232 return EXIT_SUCCESS;
233} 235}
234#endif 236#endif
235 237
236#if !ENABLE_FTPPUT 238#if !ENABLE_FTPPUT
237int ftp_send(FILE *control_stream, 239int ftp_send(const char *server_path, char *local_path);
238 const char *server_path, char *local_path);
239#else 240#else
240static 241static
241int ftp_send(FILE *control_stream, 242int ftp_send(const char *server_path, char *local_path)
242 const char *server_path, char *local_path)
243{ 243{
244 int fd_data; 244 int fd_data;
245 int fd_local; 245 int fd_local;
246 int response; 246 int response;
247 247
248 /* connect to the data socket */ 248 /* connect to the data socket */
249 if (ftpcmd("PASV", NULL, control_stream) != 227) {
250 ftp_die("PASV");
251 }
252 fd_data = xconnect_ftpdata(); 249 fd_data = xconnect_ftpdata();
253 250
254 /* get the local file */ 251 /* get the local file */
@@ -256,7 +253,7 @@ int ftp_send(FILE *control_stream,
256 if (NOT_LONE_DASH(local_path)) 253 if (NOT_LONE_DASH(local_path))
257 fd_local = xopen(local_path, O_RDONLY); 254 fd_local = xopen(local_path, O_RDONLY);
258 255
259 response = ftpcmd("STOR", server_path, control_stream); 256 response = ftpcmd("STOR", server_path);
260 switch (response) { 257 switch (response) {
261 case 125: 258 case 125:
262 case 150: 259 case 150:
@@ -265,29 +262,10 @@ int ftp_send(FILE *control_stream,
265 ftp_die("STOR"); 262 ftp_die("STOR");
266 } 263 }
267 264
268 /* transfer the file */ 265 return pump_data_and_QUIT(fd_local, fd_data);
269 if (bb_copyfd_eof(fd_local, fd_data) == -1) {
270 /* error msg is already printed by bb_copyfd_eof */
271 return EXIT_FAILURE;
272 }
273
274 /* close it all down */
275 close(fd_data);
276 if (ftpcmd(NULL, NULL, control_stream) != 226) {
277 ftp_die("close");
278 }
279 ftpcmd("QUIT", NULL, control_stream);
280
281 return EXIT_SUCCESS;
282} 266}
283#endif 267#endif
284 268
285#define FTPGETPUT_OPT_CONTINUE 1
286#define FTPGETPUT_OPT_VERBOSE 2
287#define FTPGETPUT_OPT_USER 4
288#define FTPGETPUT_OPT_PASSWORD 8
289#define FTPGETPUT_OPT_PORT 16
290
291#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS 269#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
292static const char ftpgetput_longopts[] ALIGN1 = 270static const char ftpgetput_longopts[] ALIGN1 =
293 "continue\0" Required_argument "c" 271 "continue\0" Required_argument "c"
@@ -304,14 +282,13 @@ int ftpgetput_main(int argc ATTRIBUTE_UNUSED, char **argv)
304 unsigned opt; 282 unsigned opt;
305 const char *port = "ftp"; 283 const char *port = "ftp";
306 /* socket to ftp server */ 284 /* socket to ftp server */
307 FILE *control_stream;
308 285
309#if ENABLE_FTPPUT && !ENABLE_FTPGET 286#if ENABLE_FTPPUT && !ENABLE_FTPGET
310# define ftp_action ftp_send 287# define ftp_action ftp_send
311#elif ENABLE_FTPGET && !ENABLE_FTPPUT 288#elif ENABLE_FTPGET && !ENABLE_FTPPUT
312# define ftp_action ftp_receive 289# define ftp_action ftp_receive
313#else 290#else
314 int (*ftp_action)(FILE *, const char *, char *) = ftp_send; 291 int (*ftp_action)(const char *, char *) = ftp_send;
315 292
316 /* Check to see if the command is ftpget or ftput */ 293 /* Check to see if the command is ftpget or ftput */
317 if (applet_name[3] == 'g') { 294 if (applet_name[3] == 'g') {
@@ -344,8 +321,6 @@ int ftpgetput_main(int argc ATTRIBUTE_UNUSED, char **argv)
344 xmalloc_sockaddr2dotted(&lsa->u.sa)); 321 xmalloc_sockaddr2dotted(&lsa->u.sa));
345 } 322 }
346 323
347 /* Connect/Setup/Configure the FTP session */ 324 ftp_login();
348 control_stream = ftp_login(); 325 return ftp_action(argv[1], argv[2]);
349
350 return ftp_action(control_stream, argv[1], argv[2]);
351} 326}