Contents of /trunk/mkinitrd-magellan/busybox/networking/ftpgetput.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
File MIME type: text/plain
File size: 8412 byte(s)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
File MIME type: text/plain
File size: 8412 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * ftpget |
4 | * |
5 | * Mini implementation of FTP to retrieve a remote file. |
6 | * |
7 | * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com> |
8 | * Copyright (C) 2002 Glenn McGrath <bug1@iinet.net.au> |
9 | * |
10 | * Based on wget.c by Chip Rosenthal Covad Communications |
11 | * <chip@laserlink.net> |
12 | * |
13 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
14 | */ |
15 | |
16 | #include "busybox.h" |
17 | #include <getopt.h> |
18 | |
19 | typedef struct ftp_host_info_s { |
20 | char *user; |
21 | char *password; |
22 | struct len_and_sockaddr *lsa; |
23 | } ftp_host_info_t; |
24 | |
25 | static smallint verbose_flag; |
26 | static smallint do_continue; |
27 | |
28 | static void ftp_die(const char *msg, const char *remote) ATTRIBUTE_NORETURN; |
29 | static void ftp_die(const char *msg, const char *remote) |
30 | { |
31 | /* Guard against garbage from remote server */ |
32 | const char *cp = remote; |
33 | while (*cp >= ' ' && *cp < '\x7f') cp++; |
34 | bb_error_msg_and_die("unexpected server response%s%s: %.*s", |
35 | msg ? " to " : "", msg ? msg : "", |
36 | (int)(cp - remote), remote); |
37 | } |
38 | |
39 | |
40 | static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf) |
41 | { |
42 | unsigned n; |
43 | if (verbose_flag) { |
44 | bb_error_msg("cmd %s %s", s1, s2); |
45 | } |
46 | |
47 | if (s1) { |
48 | if (s2) { |
49 | fprintf(stream, "%s %s\r\n", s1, s2); |
50 | } else { |
51 | fprintf(stream, "%s\r\n", s1); |
52 | } |
53 | } |
54 | do { |
55 | char *buf_ptr; |
56 | |
57 | if (fgets(buf, 510, stream) == NULL) { |
58 | bb_perror_msg_and_die("fgets"); |
59 | } |
60 | buf_ptr = strstr(buf, "\r\n"); |
61 | if (buf_ptr) { |
62 | *buf_ptr = '\0'; |
63 | } |
64 | } while (!isdigit(buf[0]) || buf[3] != ' '); |
65 | |
66 | buf[3] = '\0'; |
67 | n = xatou(buf); |
68 | buf[3] = ' '; |
69 | return n; |
70 | } |
71 | |
72 | static int xconnect_ftpdata(ftp_host_info_t *server, char *buf) |
73 | { |
74 | char *buf_ptr; |
75 | unsigned short port_num; |
76 | |
77 | /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage] |
78 | * Server's IP is N1.N2.N3.N4 (we ignore it) |
79 | * Server's port for data connection is P1*256+P2 */ |
80 | buf_ptr = strrchr(buf, ')'); |
81 | if (buf_ptr) *buf_ptr = '\0'; |
82 | |
83 | buf_ptr = strrchr(buf, ','); |
84 | *buf_ptr = '\0'; |
85 | port_num = xatoul_range(buf_ptr + 1, 0, 255); |
86 | |
87 | buf_ptr = strrchr(buf, ','); |
88 | *buf_ptr = '\0'; |
89 | port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256; |
90 | |
91 | set_nport(server->lsa, htons(port_num)); |
92 | return xconnect_stream(server->lsa); |
93 | } |
94 | |
95 | static FILE *ftp_login(ftp_host_info_t *server) |
96 | { |
97 | FILE *control_stream; |
98 | char buf[512]; |
99 | |
100 | /* Connect to the command socket */ |
101 | control_stream = fdopen(xconnect_stream(server->lsa), "r+"); |
102 | if (control_stream == NULL) { |
103 | /* fdopen failed - extremely unlikely */ |
104 | bb_perror_nomsg_and_die(); |
105 | } |
106 | |
107 | if (ftpcmd(NULL, NULL, control_stream, buf) != 220) { |
108 | ftp_die(NULL, buf); |
109 | } |
110 | |
111 | /* Login to the server */ |
112 | switch (ftpcmd("USER", server->user, control_stream, buf)) { |
113 | case 230: |
114 | break; |
115 | case 331: |
116 | if (ftpcmd("PASS", server->password, control_stream, buf) != 230) { |
117 | ftp_die("PASS", buf); |
118 | } |
119 | break; |
120 | default: |
121 | ftp_die("USER", buf); |
122 | } |
123 | |
124 | ftpcmd("TYPE I", NULL, control_stream, buf); |
125 | |
126 | return control_stream; |
127 | } |
128 | |
129 | #if !ENABLE_FTPGET |
130 | int ftp_receive(ftp_host_info_t *server, FILE *control_stream, |
131 | const char *local_path, char *server_path); |
132 | #else |
133 | static |
134 | int ftp_receive(ftp_host_info_t *server, FILE *control_stream, |
135 | const char *local_path, char *server_path) |
136 | { |
137 | char buf[512]; |
138 | /* I think 'filesize' usage here is bogus. Let's see... */ |
139 | //off_t filesize = -1; |
140 | #define filesize ((off_t)-1) |
141 | int fd_data; |
142 | int fd_local = -1; |
143 | off_t beg_range = 0; |
144 | |
145 | /* Connect to the data socket */ |
146 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { |
147 | ftp_die("PASV", buf); |
148 | } |
149 | fd_data = xconnect_ftpdata(server, buf); |
150 | |
151 | if (ftpcmd("SIZE", server_path, control_stream, buf) == 213) { |
152 | //filesize = BB_STRTOOFF(buf + 4, NULL, 10); |
153 | //if (errno || filesize < 0) |
154 | // ftp_die("SIZE", buf); |
155 | } else { |
156 | do_continue = 0; |
157 | } |
158 | |
159 | if (LONE_DASH(local_path)) { |
160 | fd_local = STDOUT_FILENO; |
161 | do_continue = 0; |
162 | } |
163 | |
164 | if (do_continue) { |
165 | struct stat sbuf; |
166 | if (lstat(local_path, &sbuf) < 0) { |
167 | bb_perror_msg_and_die("lstat"); |
168 | } |
169 | if (sbuf.st_size > 0) { |
170 | beg_range = sbuf.st_size; |
171 | } else { |
172 | do_continue = 0; |
173 | } |
174 | } |
175 | |
176 | if (do_continue) { |
177 | sprintf(buf, "REST %"OFF_FMT"d", beg_range); |
178 | if (ftpcmd(buf, NULL, control_stream, buf) != 350) { |
179 | do_continue = 0; |
180 | } else { |
181 | //if (filesize != -1) |
182 | // filesize -= beg_range; |
183 | } |
184 | } |
185 | |
186 | if (ftpcmd("RETR", server_path, control_stream, buf) > 150) { |
187 | ftp_die("RETR", buf); |
188 | } |
189 | |
190 | /* only make a local file if we know that one exists on the remote server */ |
191 | if (fd_local == -1) { |
192 | if (do_continue) { |
193 | fd_local = xopen(local_path, O_APPEND | O_WRONLY); |
194 | } else { |
195 | fd_local = xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY); |
196 | } |
197 | } |
198 | |
199 | /* Copy the file */ |
200 | if (filesize != -1) { |
201 | if (bb_copyfd_size(fd_data, fd_local, filesize) == -1) |
202 | return EXIT_FAILURE; |
203 | } else { |
204 | if (bb_copyfd_eof(fd_data, fd_local) == -1) |
205 | return EXIT_FAILURE; |
206 | } |
207 | |
208 | /* close it all down */ |
209 | close(fd_data); |
210 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { |
211 | ftp_die(NULL, buf); |
212 | } |
213 | ftpcmd("QUIT", NULL, control_stream, buf); |
214 | |
215 | return EXIT_SUCCESS; |
216 | } |
217 | #endif |
218 | |
219 | #if !ENABLE_FTPPUT |
220 | int ftp_send(ftp_host_info_t *server, FILE *control_stream, |
221 | const char *server_path, char *local_path); |
222 | #else |
223 | static |
224 | int ftp_send(ftp_host_info_t *server, FILE *control_stream, |
225 | const char *server_path, char *local_path) |
226 | { |
227 | struct stat sbuf; |
228 | char buf[512]; |
229 | int fd_data; |
230 | int fd_local; |
231 | int response; |
232 | |
233 | /* Connect to the data socket */ |
234 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { |
235 | ftp_die("PASV", buf); |
236 | } |
237 | fd_data = xconnect_ftpdata(server, buf); |
238 | |
239 | /* get the local file */ |
240 | fd_local = STDIN_FILENO; |
241 | if (NOT_LONE_DASH(local_path)) { |
242 | fd_local = xopen(local_path, O_RDONLY); |
243 | fstat(fd_local, &sbuf); |
244 | |
245 | sprintf(buf, "ALLO %"OFF_FMT"u", sbuf.st_size); |
246 | response = ftpcmd(buf, NULL, control_stream, buf); |
247 | switch (response) { |
248 | case 200: |
249 | case 202: |
250 | break; |
251 | default: |
252 | close(fd_local); |
253 | ftp_die("ALLO", buf); |
254 | break; |
255 | } |
256 | } |
257 | response = ftpcmd("STOR", server_path, control_stream, buf); |
258 | switch (response) { |
259 | case 125: |
260 | case 150: |
261 | break; |
262 | default: |
263 | close(fd_local); |
264 | ftp_die("STOR", buf); |
265 | } |
266 | |
267 | /* transfer the file */ |
268 | if (bb_copyfd_eof(fd_local, fd_data) == -1) { |
269 | exit(EXIT_FAILURE); |
270 | } |
271 | |
272 | /* close it all down */ |
273 | close(fd_data); |
274 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { |
275 | ftp_die("close", buf); |
276 | } |
277 | ftpcmd("QUIT", NULL, control_stream, buf); |
278 | |
279 | return EXIT_SUCCESS; |
280 | } |
281 | #endif |
282 | |
283 | #define FTPGETPUT_OPT_CONTINUE 1 |
284 | #define FTPGETPUT_OPT_VERBOSE 2 |
285 | #define FTPGETPUT_OPT_USER 4 |
286 | #define FTPGETPUT_OPT_PASSWORD 8 |
287 | #define FTPGETPUT_OPT_PORT 16 |
288 | |
289 | #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS |
290 | static const struct option ftpgetput_long_options[] = { |
291 | { "continue", 1, NULL, 'c' }, |
292 | { "verbose", 0, NULL, 'v' }, |
293 | { "username", 1, NULL, 'u' }, |
294 | { "password", 1, NULL, 'p' }, |
295 | { "port", 1, NULL, 'P' }, |
296 | { 0, 0, 0, 0 } |
297 | }; |
298 | #endif |
299 | |
300 | int ftpgetput_main(int argc, char **argv) |
301 | { |
302 | /* content-length of the file */ |
303 | unsigned opt; |
304 | const char *port = "ftp"; |
305 | /* socket to ftp server */ |
306 | FILE *control_stream; |
307 | /* continue previous transfer (-c) */ |
308 | ftp_host_info_t *server; |
309 | |
310 | #if ENABLE_FTPPUT && !ENABLE_FTPGET |
311 | # define ftp_action ftp_send |
312 | #elif ENABLE_FTPGET && !ENABLE_FTPPUT |
313 | # define ftp_action ftp_receive |
314 | #else |
315 | int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = ftp_send; |
316 | /* Check to see if the command is ftpget or ftput */ |
317 | if (applet_name[3] == 'g') { |
318 | ftp_action = ftp_receive; |
319 | } |
320 | #endif |
321 | |
322 | /* Set default values */ |
323 | server = xmalloc(sizeof(*server)); |
324 | server->user = "anonymous"; |
325 | server->password = "busybox@"; |
326 | |
327 | /* |
328 | * Decipher the command line |
329 | */ |
330 | #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS |
331 | applet_long_options = ftpgetput_long_options; |
332 | #endif |
333 | opt_complementary = "=3"; /* must have 3 params */ |
334 | opt = getopt32(argc, argv, "cvu:p:P:", &server->user, &server->password, &port); |
335 | argv += optind; |
336 | |
337 | /* Process the non-option command line arguments */ |
338 | if (opt & FTPGETPUT_OPT_CONTINUE) { |
339 | do_continue = 1; |
340 | } |
341 | if (opt & FTPGETPUT_OPT_VERBOSE) { |
342 | verbose_flag = 1; |
343 | } |
344 | |
345 | /* We want to do exactly _one_ DNS lookup, since some |
346 | * sites (i.e. ftp.us.debian.org) use round-robin DNS |
347 | * and we want to connect to only one IP... */ |
348 | server->lsa = host2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21)); |
349 | if (verbose_flag) { |
350 | printf("Connecting to %s [%s]\n", argv[0], |
351 | xmalloc_sockaddr2dotted(&server->lsa->sa, server->lsa->len)); |
352 | } |
353 | |
354 | /* Connect/Setup/Configure the FTP session */ |
355 | control_stream = ftp_login(server); |
356 | |
357 | return ftp_action(server, control_stream, argv[1], argv[2]); |
358 | } |