diff options
author | Glenn L McGrath <bug1@ihug.co.nz> | 2002-12-13 02:43:50 +0000 |
---|---|---|
committer | Glenn L McGrath <bug1@ihug.co.nz> | 2002-12-13 02:43:50 +0000 |
commit | 02d7cbfe92a94d51d2c9b11bab3dc6fe2cec7e89 (patch) | |
tree | 0f360f59ce41caaef1c123a69b3a88aa14c3e6e2 | |
parent | f03c933e473760cb3776aee8283ecb3e4bddf097 (diff) | |
download | busybox-w32-02d7cbfe92a94d51d2c9b11bab3dc6fe2cec7e89.tar.gz busybox-w32-02d7cbfe92a94d51d2c9b11bab3dc6fe2cec7e89.tar.bz2 busybox-w32-02d7cbfe92a94d51d2c9b11bab3dc6fe2cec7e89.zip |
New applets, ftpget and ftpput
-rw-r--r-- | include/applets.h | 6 | ||||
-rw-r--r-- | include/usage.h | 21 | ||||
-rw-r--r-- | networking/Config.in | 12 | ||||
-rw-r--r-- | networking/Makefile.in | 2 | ||||
-rw-r--r-- | networking/ftpgetput.c | 405 |
5 files changed, 446 insertions, 0 deletions
diff --git a/include/applets.h b/include/applets.h index bd2053039..5d8e7bb68 100644 --- a/include/applets.h +++ b/include/applets.h | |||
@@ -200,6 +200,12 @@ | |||
200 | #ifdef CONFIG_FSCK_MINIX | 200 | #ifdef CONFIG_FSCK_MINIX |
201 | APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix) | 201 | APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix) |
202 | #endif | 202 | #endif |
203 | #ifdef CONFIG_FTPGET | ||
204 | APPLET(ftpget, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) | ||
205 | #endif | ||
206 | #ifdef CONFIG_FTPPUT | ||
207 | APPLET(ftpput, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) | ||
208 | #endif | ||
203 | #ifdef CONFIG_GETOPT | 209 | #ifdef CONFIG_GETOPT |
204 | APPLET(getopt, getopt_main, _BB_DIR_BIN, _BB_SUID_NEVER) | 210 | APPLET(getopt, getopt_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
205 | #endif | 211 | #endif |
diff --git a/include/usage.h b/include/usage.h index d3c1e5072..3b519e65d 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -602,6 +602,27 @@ | |||
602 | "\t-m\tActivates MINIX-like \"mode not cleared\" warnings\n" \ | 602 | "\t-m\tActivates MINIX-like \"mode not cleared\" warnings\n" \ |
603 | "\t-f\tForce file system check." | 603 | "\t-f\tForce file system check." |
604 | 604 | ||
605 | #define ftpget_trivial_usage \ | ||
606 | "[options] remote-host local-directory remote-file" | ||
607 | #define ftpget_full_usage \ | ||
608 | "Retrieve a remote file via FTP.\n\n" \ | ||
609 | "Options:\n" \ | ||
610 | "\t-c, --continue Continue a previous transfer\n" \ | ||
611 | "\t-v, --verbose Verbose\n" \ | ||
612 | "\t-u, --username Username to be used\n" \ | ||
613 | "\t-p, --password Password to be used\n" \ | ||
614 | "\t-P, --port Port number to be used\n" | ||
615 | |||
616 | #define ftpput_trivial_usage \ | ||
617 | "[options] remote-host remote-directory local-file" | ||
618 | #define ftpput_full_usage \ | ||
619 | "Store a local file on a remote machine via FTP.\n\n" \ | ||
620 | "Options:\n" \ | ||
621 | "\t-v, --verbose Verbose\n" \ | ||
622 | "\t-u, --username Username to be used\n" \ | ||
623 | "\t-p, --password Password to be used\n" \ | ||
624 | "\t-P, --port Port number to be used\n" | ||
625 | |||
605 | #define getopt_trivial_usage \ | 626 | #define getopt_trivial_usage \ |
606 | "[OPTIONS]..." | 627 | "[OPTIONS]..." |
607 | #define getopt_full_usage \ | 628 | #define getopt_full_usage \ |
diff --git a/networking/Config.in b/networking/Config.in index b4b9462c3..b622b65f3 100644 --- a/networking/Config.in +++ b/networking/Config.in | |||
@@ -11,6 +11,18 @@ config CONFIG_FEATURE_IPV6 | |||
11 | help | 11 | help |
12 | Please submit a patch to add help text for this item. | 12 | Please submit a patch to add help text for this item. |
13 | 13 | ||
14 | config CONFIG_FTPGET | ||
15 | bool "ftpget" | ||
16 | default n | ||
17 | help | ||
18 | Retrieve a remote file via FTP. | ||
19 | |||
20 | config CONFIG_FTPPUT | ||
21 | bool "ftpput" | ||
22 | default n | ||
23 | help | ||
24 | Store a remote file via FTP. | ||
25 | |||
14 | config CONFIG_HOSTNAME | 26 | config CONFIG_HOSTNAME |
15 | bool "hostname" | 27 | bool "hostname" |
16 | default n | 28 | default n |
diff --git a/networking/Makefile.in b/networking/Makefile.in index e72b00194..c2ae451a4 100644 --- a/networking/Makefile.in +++ b/networking/Makefile.in | |||
@@ -23,6 +23,8 @@ NETWORKING_DIR:=$(TOPDIR)networking/ | |||
23 | endif | 23 | endif |
24 | 24 | ||
25 | NETWORKING-y:= | 25 | NETWORKING-y:= |
26 | NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o | ||
27 | NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o | ||
26 | NETWORKING-$(CONFIG_HOSTNAME) += hostname.o | 28 | NETWORKING-$(CONFIG_HOSTNAME) += hostname.o |
27 | NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o | 29 | NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o |
28 | NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o | 30 | NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o |
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c new file mode 100644 index 000000000..4cebbc71c --- /dev/null +++ b/networking/ftpgetput.c | |||
@@ -0,0 +1,405 @@ | |||
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@optushome.com.au> | ||
9 | * | ||
10 | * Based on wget.c by Chip Rosenthal Covad Communications | ||
11 | * <chip@laserlink.net> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
21 | * General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <sys/types.h> | ||
30 | #include <sys/ioctl.h> | ||
31 | #include <sys/time.h> | ||
32 | #include <sys/stat.h> | ||
33 | |||
34 | #include <ctype.h> | ||
35 | #include <errno.h> | ||
36 | #include <fcntl.h> | ||
37 | #include <getopt.h> | ||
38 | #include <stdio.h> | ||
39 | #include <stdlib.h> | ||
40 | #include <signal.h> | ||
41 | #include <string.h> | ||
42 | #include <unistd.h> | ||
43 | |||
44 | #include <netinet/in.h> | ||
45 | #include <netdb.h> | ||
46 | |||
47 | #include "busybox.h" | ||
48 | |||
49 | typedef struct ftp_host_info_s { | ||
50 | char *host; | ||
51 | char *port; | ||
52 | char *user; | ||
53 | char *password; | ||
54 | } ftp_host_info_t; | ||
55 | |||
56 | static char verbose_flag; | ||
57 | static char do_continue = 0; | ||
58 | |||
59 | /* If chunksize == 0 read till end of file */ | ||
60 | static int copyfd_chunk(int fd1, int fd2, off_t chunksize) | ||
61 | { | ||
62 | size_t nread; | ||
63 | size_t nwritten; | ||
64 | size_t size; | ||
65 | char buffer[BUFSIZ]; | ||
66 | |||
67 | do { | ||
68 | if ((chunksize > BUFSIZ) || (chunksize == 0)) { | ||
69 | size = BUFSIZ; | ||
70 | } else { | ||
71 | size = chunksize; | ||
72 | } | ||
73 | |||
74 | nread = safe_read(fd1, buffer, size); | ||
75 | |||
76 | if (nread < 0) { | ||
77 | if (chunksize) { | ||
78 | perror_msg_and_die("read error"); | ||
79 | } else { | ||
80 | return(0); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | nwritten = full_write(fd2, buffer, nread); | ||
85 | |||
86 | if (nwritten != nread) { | ||
87 | error_msg_and_die("Unable to write all data"); | ||
88 | } | ||
89 | |||
90 | if (chunksize) { | ||
91 | chunksize -= nwritten; | ||
92 | } | ||
93 | |||
94 | } while (chunksize); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static ftp_host_info_t *ftp_init(void) | ||
100 | { | ||
101 | ftp_host_info_t *host; | ||
102 | |||
103 | host = xcalloc(1, sizeof(ftp_host_info_t)); | ||
104 | |||
105 | /* Set the default port */ | ||
106 | if (getservbyname("ftp", "tcp")) { | ||
107 | host->port = "ftp"; | ||
108 | } else { | ||
109 | host->port = "21"; | ||
110 | } | ||
111 | host->user = "anonymous"; | ||
112 | host->password = "busybox@"; | ||
113 | |||
114 | return(host); | ||
115 | } | ||
116 | |||
117 | static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf) | ||
118 | { | ||
119 | if (verbose_flag) { | ||
120 | error_msg("cmd %s%s", s1, s2); | ||
121 | } | ||
122 | |||
123 | if (s1) { | ||
124 | if (s2) { | ||
125 | fprintf(stream, "%s%s\n", s1, s2); | ||
126 | } else { | ||
127 | fprintf(stream, "%s\n", s1); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | do { | ||
132 | if (fgets(buf, 510, stream) == NULL) { | ||
133 | perror_msg_and_die("fgets()"); | ||
134 | } | ||
135 | } while (! isdigit(buf[0]) || buf[3] != ' '); | ||
136 | |||
137 | return atoi(buf); | ||
138 | } | ||
139 | |||
140 | static int xconnect_ftpdata(const char *target_host, const char *buf) | ||
141 | { | ||
142 | char *buf_ptr; | ||
143 | char data_port[6]; | ||
144 | unsigned short port_num; | ||
145 | |||
146 | buf_ptr = strrchr(buf, ','); | ||
147 | *buf_ptr = '\0'; | ||
148 | port_num = atoi(buf_ptr + 1); | ||
149 | |||
150 | buf_ptr = strrchr(buf, ','); | ||
151 | *buf_ptr = '\0'; | ||
152 | port_num += atoi(buf_ptr + 1) * 256; | ||
153 | |||
154 | sprintf(data_port, "%d", port_num); | ||
155 | return(xconnect(target_host, data_port)); | ||
156 | } | ||
157 | |||
158 | static FILE *ftp_login(ftp_host_info_t *server) | ||
159 | { | ||
160 | FILE *control_stream; | ||
161 | char buf[512]; | ||
162 | int control_fd; | ||
163 | |||
164 | /* Connect to the command socket */ | ||
165 | control_fd = xconnect(server->host, server->port); | ||
166 | control_stream = fdopen(control_fd, "r+"); | ||
167 | if (control_stream == NULL) { | ||
168 | perror_msg_and_die("Couldnt open control stream"); | ||
169 | } | ||
170 | |||
171 | if (ftpcmd(NULL, NULL, control_stream, buf) != 220) { | ||
172 | error_msg_and_die("%s", buf + 4); | ||
173 | } | ||
174 | |||
175 | /* Login to the server */ | ||
176 | switch (ftpcmd("USER ", server->user, control_stream, buf)) { | ||
177 | case 230: | ||
178 | break; | ||
179 | case 331: | ||
180 | if (ftpcmd("PASS ", server->password, control_stream, buf) != 230) { | ||
181 | error_msg_and_die("PASS error: %s", buf + 4); | ||
182 | } | ||
183 | break; | ||
184 | default: | ||
185 | error_msg_and_die("USER error: %s", buf + 4); | ||
186 | } | ||
187 | |||
188 | ftpcmd("TYPE I", NULL, control_stream, buf); | ||
189 | |||
190 | return(control_stream); | ||
191 | } | ||
192 | |||
193 | #ifdef CONFIG_FTPGET | ||
194 | static int ftp_recieve(FILE *control_stream, const char *host, const char *local_path, char *server_path) | ||
195 | { | ||
196 | char *filename; | ||
197 | char *local_file; | ||
198 | char buf[512]; | ||
199 | off_t filesize = 0; | ||
200 | int fd_data; | ||
201 | int fd_local; | ||
202 | off_t beg_range = 0; | ||
203 | |||
204 | filename = get_last_path_component(server_path); | ||
205 | local_file = concat_path_file(local_path, filename); | ||
206 | |||
207 | /* Connect to the data socket */ | ||
208 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { | ||
209 | error_msg_and_die("PASV error: %s", buf + 4); | ||
210 | } | ||
211 | fd_data = xconnect_ftpdata(host, buf); | ||
212 | |||
213 | if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) { | ||
214 | filesize = atol(buf + 4); | ||
215 | } | ||
216 | |||
217 | /* only make a local file if we know that one exists on the remote server */ | ||
218 | if (do_continue) { | ||
219 | fd_local = xopen(local_file, O_APPEND | O_WRONLY); | ||
220 | } else { | ||
221 | fd_local = xopen(local_file, O_CREAT | O_TRUNC | O_WRONLY); | ||
222 | } | ||
223 | |||
224 | if (do_continue) { | ||
225 | struct stat sbuf; | ||
226 | if (fstat(fd_local, &sbuf) < 0) { | ||
227 | perror_msg_and_die("fstat()"); | ||
228 | } | ||
229 | if (sbuf.st_size > 0) { | ||
230 | beg_range = sbuf.st_size; | ||
231 | } else { | ||
232 | do_continue = 0; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | if (do_continue) { | ||
237 | sprintf(buf, "REST %ld", beg_range); | ||
238 | if (ftpcmd(buf, NULL, control_stream, buf) != 350) { | ||
239 | do_continue = 0; | ||
240 | } else { | ||
241 | filesize -= beg_range; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | if (ftpcmd("RETR ", server_path, control_stream, buf) > 150) { | ||
246 | error_msg_and_die("RETR error: %s", buf + 4); | ||
247 | } | ||
248 | |||
249 | /* Copy the file */ | ||
250 | copyfd_chunk(fd_data, fd_local, filesize); | ||
251 | |||
252 | /* close it all down */ | ||
253 | close(fd_data); | ||
254 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { | ||
255 | error_msg_and_die("ftp error: %s", buf + 4); | ||
256 | } | ||
257 | ftpcmd("QUIT", NULL, control_stream, buf); | ||
258 | |||
259 | return(EXIT_SUCCESS); | ||
260 | } | ||
261 | #endif | ||
262 | |||
263 | #ifdef CONFIG_FTPPUT | ||
264 | static int ftp_send(FILE *control_stream, const char *host, const char *local_path, char *server_path) | ||
265 | { | ||
266 | struct stat sbuf; | ||
267 | char buf[512]; | ||
268 | int fd_data; | ||
269 | int fd_local; | ||
270 | int response; | ||
271 | |||
272 | /* Connect to the data socket */ | ||
273 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { | ||
274 | error_msg_and_die("PASV error: %s", buf + 4); | ||
275 | } | ||
276 | fd_data = xconnect_ftpdata(host, buf); | ||
277 | |||
278 | if (ftpcmd("CWD ", server_path, control_stream, buf) != 250) { | ||
279 | error_msg_and_die("CWD error: %s", buf + 4); | ||
280 | } | ||
281 | |||
282 | /* get the local file */ | ||
283 | fd_local = xopen(local_path, O_RDONLY); | ||
284 | fstat(fd_local, &sbuf); | ||
285 | |||
286 | sprintf(buf, "ALLO %lu", sbuf.st_size); | ||
287 | response = ftpcmd(buf, NULL, control_stream, buf); | ||
288 | switch (response) { | ||
289 | case 200: | ||
290 | case 202: | ||
291 | break; | ||
292 | default: | ||
293 | close(fd_local); | ||
294 | error_msg_and_die("ALLO error: %s", buf + 4); | ||
295 | break; | ||
296 | } | ||
297 | |||
298 | response = ftpcmd("STOR ", local_path, control_stream, buf); | ||
299 | switch (response) { | ||
300 | case 125: | ||
301 | case 150: | ||
302 | break; | ||
303 | default: | ||
304 | close(fd_local); | ||
305 | error_msg_and_die("STOR error: %s", buf + 4); | ||
306 | } | ||
307 | |||
308 | /* transfer the file */ | ||
309 | copyfd_chunk(fd_local, fd_data, 0); | ||
310 | |||
311 | /* close it all down */ | ||
312 | close(fd_data); | ||
313 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { | ||
314 | error_msg_and_die("error: %s", buf + 4); | ||
315 | } | ||
316 | ftpcmd("QUIT", NULL, control_stream, buf); | ||
317 | |||
318 | return(EXIT_SUCCESS); | ||
319 | } | ||
320 | #endif | ||
321 | |||
322 | int ftpgetput_main(int argc, char **argv) | ||
323 | { | ||
324 | /* content-length of the file */ | ||
325 | int option_index = -1; | ||
326 | int opt; | ||
327 | |||
328 | /* socket to ftp server */ | ||
329 | FILE *control_stream; | ||
330 | |||
331 | /* continue a prev transfer (-c) */ | ||
332 | ftp_host_info_t *server; | ||
333 | |||
334 | int (*ftp_action)(FILE *, const char *, const char *, char *) = NULL; | ||
335 | |||
336 | struct option long_options[] = { | ||
337 | {"username", 1, NULL, 'u'}, | ||
338 | {"password", 1, NULL, 'p'}, | ||
339 | {"port", 1, NULL, 'P'}, | ||
340 | {"continue", 1, NULL, 'c'}, | ||
341 | {"verbose", 0, NULL, 'v'}, | ||
342 | {0, 0, 0, 0} | ||
343 | }; | ||
344 | |||
345 | #ifdef CONFIG_FTPPUT | ||
346 | if (applet_name[3] == 'p') { | ||
347 | ftp_action = ftp_send; | ||
348 | } | ||
349 | #endif | ||
350 | #ifdef CONFIG_FTPGET | ||
351 | if (applet_name[3] == 'g') { | ||
352 | ftp_action = ftp_recieve; | ||
353 | } | ||
354 | #endif | ||
355 | |||
356 | /* Set default values */ | ||
357 | server = ftp_init(); | ||
358 | verbose_flag = 0; | ||
359 | |||
360 | /* | ||
361 | * Decipher the command line | ||
362 | */ | ||
363 | while ((opt = getopt_long(argc, argv, "u:p:P:cv", long_options, &option_index)) != EOF) { | ||
364 | switch(opt) { | ||
365 | case 'c': | ||
366 | do_continue = 1; | ||
367 | break; | ||
368 | case 'u': | ||
369 | server->user = optarg; | ||
370 | break; | ||
371 | case 'p': | ||
372 | server->password = optarg; | ||
373 | break; | ||
374 | case 'P': | ||
375 | server->port = optarg; | ||
376 | break; | ||
377 | case 'v': | ||
378 | verbose_flag = 1; | ||
379 | break; | ||
380 | default: | ||
381 | show_usage(); | ||
382 | } | ||
383 | } | ||
384 | |||
385 | /* | ||
386 | * Process the non-option command line arguments | ||
387 | */ | ||
388 | if (argc - optind != 3) { | ||
389 | show_usage(); | ||
390 | } | ||
391 | |||
392 | /* Connect/Setup/Configure the FTP session */ | ||
393 | server->host = argv[optind]; | ||
394 | control_stream = ftp_login(server); | ||
395 | |||
396 | return(ftp_action(control_stream, argv[optind], argv[optind + 1], argv[optind + 2])); | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | Local Variables: | ||
401 | c-file-style: "linux" | ||
402 | c-basic-offset: 4 | ||
403 | tab-width: 4 | ||
404 | End: | ||
405 | */ | ||