diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
commit | 8d42f86b146871ae4c4cafd3801a85f381249a14 (patch) | |
tree | b963999fc54eddb65f1929b894f868e24851fc9c /networking/ftpgetput.c | |
download | busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.gz busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.bz2 busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.zip |
Correcting branch name to be like previous ones
Diffstat (limited to 'networking/ftpgetput.c')
-rw-r--r-- | networking/ftpgetput.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c new file mode 100644 index 000000000..223d2435c --- /dev/null +++ b/networking/ftpgetput.c | |||
@@ -0,0 +1,340 @@ | |||
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 sockaddr_in *s_in; | ||
23 | } ftp_host_info_t; | ||
24 | |||
25 | static char verbose_flag = 0; | ||
26 | static char do_continue = 0; | ||
27 | |||
28 | static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf) | ||
29 | { | ||
30 | if (verbose_flag) { | ||
31 | bb_error_msg("cmd %s%s", s1, s2); | ||
32 | } | ||
33 | |||
34 | if (s1) { | ||
35 | if (s2) { | ||
36 | fprintf(stream, "%s%s\r\n", s1, s2); | ||
37 | } else { | ||
38 | fprintf(stream, "%s\r\n", s1); | ||
39 | } | ||
40 | } | ||
41 | do { | ||
42 | char *buf_ptr; | ||
43 | |||
44 | if (fgets(buf, 510, stream) == NULL) { | ||
45 | bb_perror_msg_and_die("fgets"); | ||
46 | } | ||
47 | buf_ptr = strstr(buf, "\r\n"); | ||
48 | if (buf_ptr) { | ||
49 | *buf_ptr = '\0'; | ||
50 | } | ||
51 | } while (!isdigit(buf[0]) || buf[3] != ' '); | ||
52 | |||
53 | return xatou(buf); | ||
54 | } | ||
55 | |||
56 | static int xconnect_ftpdata(ftp_host_info_t *server, const char *buf) | ||
57 | { | ||
58 | char *buf_ptr; | ||
59 | unsigned short port_num; | ||
60 | |||
61 | buf_ptr = strrchr(buf, ','); | ||
62 | *buf_ptr = '\0'; | ||
63 | port_num = xatoul_range(buf_ptr + 1, 0, 255); | ||
64 | |||
65 | buf_ptr = strrchr(buf, ','); | ||
66 | *buf_ptr = '\0'; | ||
67 | port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256; | ||
68 | |||
69 | server->s_in->sin_port = htons(port_num); | ||
70 | return xconnect_tcp_v4(server->s_in); | ||
71 | } | ||
72 | |||
73 | static FILE *ftp_login(ftp_host_info_t *server) | ||
74 | { | ||
75 | FILE *control_stream; | ||
76 | char buf[512]; | ||
77 | |||
78 | /* Connect to the command socket */ | ||
79 | control_stream = fdopen(xconnect_tcp_v4(server->s_in), "r+"); | ||
80 | if (control_stream == NULL) { | ||
81 | bb_perror_msg_and_die("cannot open control stream"); | ||
82 | } | ||
83 | |||
84 | if (ftpcmd(NULL, NULL, control_stream, buf) != 220) { | ||
85 | bb_error_msg_and_die("%s", buf + 4); | ||
86 | } | ||
87 | |||
88 | /* Login to the server */ | ||
89 | switch (ftpcmd("USER ", server->user, control_stream, buf)) { | ||
90 | case 230: | ||
91 | break; | ||
92 | case 331: | ||
93 | if (ftpcmd("PASS ", server->password, control_stream, buf) != 230) { | ||
94 | bb_error_msg_and_die("PASS error: %s", buf + 4); | ||
95 | } | ||
96 | break; | ||
97 | default: | ||
98 | bb_error_msg_and_die("USER error: %s", buf + 4); | ||
99 | } | ||
100 | |||
101 | ftpcmd("TYPE I", NULL, control_stream, buf); | ||
102 | |||
103 | return control_stream; | ||
104 | } | ||
105 | |||
106 | #if !ENABLE_FTPGET | ||
107 | int ftp_receive(ftp_host_info_t *server, FILE *control_stream, | ||
108 | const char *local_path, char *server_path); | ||
109 | #else | ||
110 | static | ||
111 | int ftp_receive(ftp_host_info_t *server, FILE *control_stream, | ||
112 | const char *local_path, char *server_path) | ||
113 | { | ||
114 | char buf[512]; | ||
115 | off_t filesize = 0; | ||
116 | int fd_data; | ||
117 | int fd_local = -1; | ||
118 | off_t beg_range = 0; | ||
119 | |||
120 | /* Connect to the data socket */ | ||
121 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { | ||
122 | bb_error_msg_and_die("PASV error: %s", buf + 4); | ||
123 | } | ||
124 | fd_data = xconnect_ftpdata(server, buf); | ||
125 | |||
126 | if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) { | ||
127 | filesize = BB_STRTOOFF(buf + 4, NULL, 10); | ||
128 | if (errno || filesize < 0) | ||
129 | bb_error_msg_and_die("SIZE error: %s", buf + 4); | ||
130 | } else { | ||
131 | filesize = -1; | ||
132 | do_continue = 0; | ||
133 | } | ||
134 | |||
135 | if ((local_path[0] == '-') && (local_path[1] == '\0')) { | ||
136 | fd_local = STDOUT_FILENO; | ||
137 | do_continue = 0; | ||
138 | } | ||
139 | |||
140 | if (do_continue) { | ||
141 | struct stat sbuf; | ||
142 | if (lstat(local_path, &sbuf) < 0) { | ||
143 | bb_perror_msg_and_die("lstat"); | ||
144 | } | ||
145 | if (sbuf.st_size > 0) { | ||
146 | beg_range = sbuf.st_size; | ||
147 | } else { | ||
148 | do_continue = 0; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | if (do_continue) { | ||
153 | sprintf(buf, "REST %"OFF_FMT"d", beg_range); | ||
154 | if (ftpcmd(buf, NULL, control_stream, buf) != 350) { | ||
155 | do_continue = 0; | ||
156 | } else { | ||
157 | filesize -= beg_range; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | if (ftpcmd("RETR ", server_path, control_stream, buf) > 150) { | ||
162 | bb_error_msg_and_die("RETR error: %s", buf + 4); | ||
163 | } | ||
164 | |||
165 | /* only make a local file if we know that one exists on the remote server */ | ||
166 | if (fd_local == -1) { | ||
167 | if (do_continue) { | ||
168 | fd_local = xopen(local_path, O_APPEND | O_WRONLY); | ||
169 | } else { | ||
170 | fd_local = xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /* Copy the file */ | ||
175 | if (filesize != -1) { | ||
176 | if (-1 == bb_copyfd_size(fd_data, fd_local, filesize)) | ||
177 | exit(EXIT_FAILURE); | ||
178 | } else { | ||
179 | if (-1 == bb_copyfd_eof(fd_data, fd_local)) | ||
180 | exit(EXIT_FAILURE); | ||
181 | } | ||
182 | |||
183 | /* close it all down */ | ||
184 | close(fd_data); | ||
185 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { | ||
186 | bb_error_msg_and_die("ftp error: %s", buf + 4); | ||
187 | } | ||
188 | ftpcmd("QUIT", NULL, control_stream, buf); | ||
189 | |||
190 | return EXIT_SUCCESS; | ||
191 | } | ||
192 | #endif | ||
193 | |||
194 | #if !ENABLE_FTPPUT | ||
195 | int ftp_send(ftp_host_info_t *server, FILE *control_stream, | ||
196 | const char *server_path, char *local_path); | ||
197 | #else | ||
198 | static | ||
199 | int ftp_send(ftp_host_info_t *server, FILE *control_stream, | ||
200 | const char *server_path, char *local_path) | ||
201 | { | ||
202 | struct stat sbuf; | ||
203 | char buf[512]; | ||
204 | int fd_data; | ||
205 | int fd_local; | ||
206 | int response; | ||
207 | |||
208 | /* Connect to the data socket */ | ||
209 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { | ||
210 | bb_error_msg_and_die("PASV error: %s", buf + 4); | ||
211 | } | ||
212 | fd_data = xconnect_ftpdata(server, buf); | ||
213 | |||
214 | /* get the local file */ | ||
215 | if ((local_path[0] == '-') && (local_path[1] == '\0')) { | ||
216 | fd_local = STDIN_FILENO; | ||
217 | } else { | ||
218 | fd_local = xopen(local_path, O_RDONLY); | ||
219 | fstat(fd_local, &sbuf); | ||
220 | |||
221 | sprintf(buf, "ALLO %lu", (unsigned long)sbuf.st_size); | ||
222 | response = ftpcmd(buf, NULL, control_stream, buf); | ||
223 | switch (response) { | ||
224 | case 200: | ||
225 | case 202: | ||
226 | break; | ||
227 | default: | ||
228 | close(fd_local); | ||
229 | bb_error_msg_and_die("ALLO error: %s", buf + 4); | ||
230 | break; | ||
231 | } | ||
232 | } | ||
233 | response = ftpcmd("STOR ", server_path, control_stream, buf); | ||
234 | switch (response) { | ||
235 | case 125: | ||
236 | case 150: | ||
237 | break; | ||
238 | default: | ||
239 | close(fd_local); | ||
240 | bb_error_msg_and_die("STOR error: %s", buf + 4); | ||
241 | } | ||
242 | |||
243 | /* transfer the file */ | ||
244 | if (bb_copyfd_eof(fd_local, fd_data) == -1) { | ||
245 | exit(EXIT_FAILURE); | ||
246 | } | ||
247 | |||
248 | /* close it all down */ | ||
249 | close(fd_data); | ||
250 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { | ||
251 | bb_error_msg_and_die("error: %s", buf + 4); | ||
252 | } | ||
253 | ftpcmd("QUIT", NULL, control_stream, buf); | ||
254 | |||
255 | return EXIT_SUCCESS; | ||
256 | } | ||
257 | #endif | ||
258 | |||
259 | #define FTPGETPUT_OPT_CONTINUE 1 | ||
260 | #define FTPGETPUT_OPT_VERBOSE 2 | ||
261 | #define FTPGETPUT_OPT_USER 4 | ||
262 | #define FTPGETPUT_OPT_PASSWORD 8 | ||
263 | #define FTPGETPUT_OPT_PORT 16 | ||
264 | |||
265 | #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS | ||
266 | static const struct option ftpgetput_long_options[] = { | ||
267 | { "continue", 1, NULL, 'c' }, | ||
268 | { "verbose", 0, NULL, 'v' }, | ||
269 | { "username", 1, NULL, 'u' }, | ||
270 | { "password", 1, NULL, 'p' }, | ||
271 | { "port", 1, NULL, 'P' }, | ||
272 | { 0, 0, 0, 0 } | ||
273 | }; | ||
274 | #endif | ||
275 | |||
276 | int ftpgetput_main(int argc, char **argv) | ||
277 | { | ||
278 | /* content-length of the file */ | ||
279 | unsigned opt; | ||
280 | char *port = "ftp"; | ||
281 | |||
282 | /* socket to ftp server */ | ||
283 | FILE *control_stream; | ||
284 | struct sockaddr_in s_in; | ||
285 | |||
286 | /* continue a prev transfer (-c) */ | ||
287 | ftp_host_info_t *server; | ||
288 | |||
289 | int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = NULL; | ||
290 | |||
291 | /* Check to see if the command is ftpget or ftput */ | ||
292 | if (ENABLE_FTPPUT && (!ENABLE_FTPGET || applet_name[3] == 'p')) { | ||
293 | ftp_action = ftp_send; | ||
294 | } | ||
295 | if (ENABLE_FTPGET && (!ENABLE_FTPPUT || applet_name[3] == 'g')) { | ||
296 | ftp_action = ftp_receive; | ||
297 | } | ||
298 | |||
299 | /* Set default values */ | ||
300 | server = xmalloc(sizeof(ftp_host_info_t)); | ||
301 | server->user = "anonymous"; | ||
302 | server->password = "busybox@"; | ||
303 | verbose_flag = 0; | ||
304 | |||
305 | /* | ||
306 | * Decipher the command line | ||
307 | */ | ||
308 | #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS | ||
309 | applet_long_options = ftpgetput_long_options; | ||
310 | #endif | ||
311 | opt = getopt32(argc, argv, "cvu:p:P:", &server->user, &server->password, &port); | ||
312 | |||
313 | /* Process the non-option command line arguments */ | ||
314 | if (argc - optind != 3) { | ||
315 | bb_show_usage(); | ||
316 | } | ||
317 | |||
318 | if (opt & FTPGETPUT_OPT_CONTINUE) { | ||
319 | do_continue = 1; | ||
320 | } | ||
321 | if (opt & FTPGETPUT_OPT_VERBOSE) { | ||
322 | verbose_flag = 1; | ||
323 | } | ||
324 | |||
325 | /* We want to do exactly _one_ DNS lookup, since some | ||
326 | * sites (i.e. ftp.us.debian.org) use round-robin DNS | ||
327 | * and we want to connect to only one IP... */ | ||
328 | server->s_in = &s_in; | ||
329 | bb_lookup_host(&s_in, argv[optind]); | ||
330 | s_in.sin_port = bb_lookup_port(port, "tcp", 21); | ||
331 | if (verbose_flag) { | ||
332 | printf("Connecting to %s[%s]:%d\n", | ||
333 | argv[optind], inet_ntoa(s_in.sin_addr), ntohs(s_in.sin_port)); | ||
334 | } | ||
335 | |||
336 | /* Connect/Setup/Configure the FTP session */ | ||
337 | control_stream = ftp_login(server); | ||
338 | |||
339 | return ftp_action(server, control_stream, argv[optind + 1], argv[optind + 2]); | ||
340 | } | ||