diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-29 07:40:35 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-29 07:40:35 +0000 |
commit | 7cb808e1c5d6184dc3846f65344c7879f60de4f3 (patch) | |
tree | 6481bc073acb40bc64629bdaa62f765e415b23a9 /networking/ftpgetput.c | |
parent | 0e7940ae906ea9f29050323fced2bc48f70008af (diff) | |
download | busybox-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.c | 195 |
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) |
27 | enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) }; | 28 | enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) }; |
28 | struct BUG_G_too_big { | 29 | struct 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 { | |||
41 | static void ftp_die(const char *msg) ATTRIBUTE_NORETURN; | 43 | static void ftp_die(const char *msg) ATTRIBUTE_NORETURN; |
42 | static void ftp_die(const char *msg) | 44 | static 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 | ||
54 | static int ftpcmd(const char *s1, const char *s2, FILE *stream) | 56 | static 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 | ||
83 | static FILE *ftp_login(void) | 83 | static 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 | ||
116 | static int xconnect_ftpdata(void) | 112 | static 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 | /* | ||
118 | TODO: PASV command will not work for IPv6. RFC2428 describes | ||
119 | IPv6-capable "extended PASV" - EPSV. | ||
120 | |||
121 | "EPSV [protocol]" asks server to bind to and listen on a data port | ||
122 | in specified protocol. Protocol is 1 for IPv4, 2 for IPv6. | ||
123 | If not specified, defaults to "same as used for control connection". | ||
124 | If server understood you, it should answer "229 <some text>(|||port|)" | ||
125 | where "|" are literal pipe chars and "port" is ASCII decimal port#. | ||
126 | |||
127 | There is also an IPv6-capable replacement for PORT (EPRT), | ||
128 | but we don't need that. | ||
129 | |||
130 | NB: PASV may still work for some servers even over IPv6. | ||
131 | For example, vsftp happily answers | ||
132 | "227 Entering Passive Mode (0,0,0,0,n,n)" and proceeds as usual. | ||
133 | |||
134 | TODO2: 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 | ||
159 | static 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 |
140 | int ftp_receive(FILE *control_stream, | 181 | int ftp_receive(const char *local_path, char *server_path); |
141 | const char *local_path, char *server_path); | ||
142 | #else | 182 | #else |
143 | static | 183 | static |
144 | int ftp_receive(FILE *control_stream, | 184 | int 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 | /* | ||
153 | TODO: PASV command will not work for IPv6. RFC2428 describes | ||
154 | IPv6-capable "extended PASV" - EPSV. | ||
155 | |||
156 | "EPSV [protocol]" asks server to bind to and listen on a data port | ||
157 | in specified protocol. Protocol is 1 for IPv4, 2 for IPv6. | ||
158 | If not specified, defaults to "same as used for control connection". | ||
159 | If server understood you, it should answer "229 <some text>(|||port|)" | ||
160 | where "|" are literal pipe chars and "port" is ASCII decimal port#. | ||
161 | |||
162 | There is also an IPv6-capable replacement for PORT (EPRT), | ||
163 | but we don't need that. | ||
164 | |||
165 | TODO: fold in sending of PASV/EPSV and parsing of response into | ||
166 | xconnect_ftpdata(). (Also, need to stop ignoring IP address in PASV | ||
167 | response). | ||
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 |
237 | int ftp_send(FILE *control_stream, | 239 | int ftp_send(const char *server_path, char *local_path); |
238 | const char *server_path, char *local_path); | ||
239 | #else | 240 | #else |
240 | static | 241 | static |
241 | int ftp_send(FILE *control_stream, | 242 | int 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 |
292 | static const char ftpgetput_longopts[] ALIGN1 = | 270 | static 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 | } |