Magellan Linux

Annotation of /tags/mkinitrd-6_2_1/busybox/networking/ftpgetput.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (hide annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 10 months ago) by niro
Original Path: trunk/mkinitrd-magellan/busybox/networking/ftpgetput.c
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 niro 532 /* 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     }