1 |
/* Copy a file using the ftp protocol |
2 |
|
3 |
Copyright (C) 1997 Free Software Foundation, Inc. |
4 |
|
5 |
Written by Miles Bader <miles@gnu.ai.mit.edu> |
6 |
|
7 |
This program is free software; you can redistribute it and/or |
8 |
modify it under the terms of the GNU General Public License as |
9 |
published by the Free Software Foundation; either version 2, or (at |
10 |
your option) any later version. |
11 |
|
12 |
This program is distributed in the hope that it will be useful, but |
13 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
General Public License for more details. |
16 |
|
17 |
You should have received a copy of the GNU General Public License |
18 |
along with this program; if not, write to the Free Software |
19 |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
20 |
|
21 |
#include <unistd.h> |
22 |
#include <string.h> |
23 |
#include <errno.h> |
24 |
#include <error.h> |
25 |
#include <argp.h> |
26 |
#include <netdb.h> |
27 |
#include <fcntl.h> |
28 |
|
29 |
#include <version.h> |
30 |
|
31 |
#include <ftpconn.h> |
32 |
|
33 |
#define COPY_SZ 65536 |
34 |
|
35 |
const char *argp_program_version = STANDARD_HURD_VERSION (ftpcp); |
36 |
|
37 |
#define OPT_SRC_U -3 |
38 |
#define OPT_SRC_A -4 |
39 |
#define OPT_SRC_P -5 |
40 |
#define OPT_DST_U -6 |
41 |
#define OPT_DST_A -7 |
42 |
#define OPT_DST_P -8 |
43 |
|
44 |
|
45 |
static struct argp_option options[] = |
46 |
{ |
47 |
{"user", 'u', "USER",0, "User to login as on both ftp servers"}, |
48 |
{"password", 'p', "PWD", 0, "USER's password"}, |
49 |
{"account", 'a', "ACCT",0, "Account to login as"}, |
50 |
{"src-user", OPT_SRC_U, "USER",0, "User to login as on the src ftp server"}, |
51 |
{"src-password",OPT_SRC_P, "PWD", 0, "The src USER's password"}, |
52 |
{"src-account", OPT_SRC_A, "ACCT",0, "Account to login as on the source server"}, |
53 |
{"dst-user", OPT_DST_U, "USER",0, "User to login as on the dst ftp server"}, |
54 |
{"dst-password",OPT_DST_P, "PWD", 0, "The dst USER's password"}, |
55 |
{"dst-account", OPT_DST_A, "ACCT",0, "Account to login as on the source server"}, |
56 |
{"debug", 'D', 0, 0, "Turn on debugging output for ftp connections"}, |
57 |
{0, 0} |
58 |
}; |
59 |
static char *args_doc = "SRC [DST]"; |
60 |
static char *doc = "Copy file SRC over ftp to DST." |
61 |
"\vBoth SRC and DST may have the form HOST:FILE, FILE, or -, where - is" |
62 |
" standard input for SRC or standard output for DST, and FILE is a local" |
63 |
" file. DST may be a directory, in which case the basename of SRC is" |
64 |
" appended to make the actual destination filename."; |
65 |
|
66 |
/* customization hooks. */ |
67 |
static struct ftp_conn_hooks conn_hooks = { 0 }; |
68 |
|
69 |
static void |
70 |
cntl_debug (struct ftp_conn *conn, int type, const char *txt) |
71 |
{ |
72 |
char *type_str; |
73 |
|
74 |
switch (type) |
75 |
{ |
76 |
case FTP_CONN_CNTL_DEBUG_CMD: type_str = ">"; break; |
77 |
case FTP_CONN_CNTL_DEBUG_REPLY: type_str = "="; break; |
78 |
default: type_str = "?"; break; |
79 |
} |
80 |
|
81 |
fprintf (stderr, "%s%s%s\n", (char *)conn->hook ?: "", type_str, txt); |
82 |
} |
83 |
|
84 |
/* Return an ftp connection for the host NAME using PARAMS. If an error |
85 |
occurrs, a message is printed the program exits. If CNAME is non-zero, |
86 |
the host's canonical name, in mallocated storage, is returned in it. */ |
87 |
struct ftp_conn * |
88 |
get_host_conn (char *name, struct ftp_conn_params *params, char **cname) |
89 |
{ |
90 |
error_t err; |
91 |
struct hostent *he; |
92 |
struct ftp_conn *conn; |
93 |
|
94 |
he = gethostbyname (name); |
95 |
if (! he) |
96 |
error (10, 0, "%s: %s", name, hstrerror (h_errno)); |
97 |
|
98 |
params->addr = malloc (he->h_length); |
99 |
if (! params->addr) |
100 |
error (11, ENOMEM, "%s", name); |
101 |
|
102 |
bcopy (he->h_addr_list[0], params->addr, he->h_length); |
103 |
params->addr_len = he->h_length; |
104 |
params->addr_type = he->h_addrtype; |
105 |
|
106 |
err = ftp_conn_create (params, &conn_hooks, &conn); |
107 |
if (err) |
108 |
error (12, err, "%s", he->h_name); |
109 |
|
110 |
if (cname) |
111 |
*cname = strdup (he->h_name); |
112 |
|
113 |
return conn; |
114 |
} |
115 |
|
116 |
static void |
117 |
cp (int src, const char *src_name, int dst, const char *dst_name) |
118 |
{ |
119 |
ssize_t rd; |
120 |
static void *copy_buf = 0; |
121 |
|
122 |
if (! copy_buf) |
123 |
{ |
124 |
copy_buf = valloc (COPY_SZ); |
125 |
if (! copy_buf) |
126 |
error (13, ENOMEM, "Cannot allocate copy buffer"); |
127 |
} |
128 |
|
129 |
while ((rd = read (src, copy_buf, COPY_SZ)) > 0) |
130 |
do |
131 |
{ |
132 |
int wr = write (dst, copy_buf, rd); |
133 |
if (wr < 0) |
134 |
error (14, errno, "%s", dst_name); |
135 |
rd -= wr; |
136 |
} |
137 |
while (rd > 0); |
138 |
|
139 |
if (rd != 0) |
140 |
error (15, errno, "%s", src_name); |
141 |
} |
142 |
|
143 |
struct epoint |
144 |
{ |
145 |
char *name; /* Name, of the form HOST:FILE, FILE, or -. */ |
146 |
char *file; /* If remote, the FILE portion, or 0. */ |
147 |
int fd; /* A file descriptor to use. */ |
148 |
struct ftp_conn *conn; /* An ftp connection to use. */ |
149 |
struct ftp_conn_params params; |
150 |
}; |
151 |
|
152 |
static void |
153 |
econnect (struct epoint *e, struct ftp_conn_params *def_params, char *name) |
154 |
{ |
155 |
char *rmt; |
156 |
|
157 |
if (! e->name) |
158 |
e->name = "-"; |
159 |
|
160 |
rmt = strchr (e->name, ':'); |
161 |
if (rmt) |
162 |
{ |
163 |
error_t err; |
164 |
|
165 |
*rmt++ = 0; |
166 |
|
167 |
if (! e->params.user) |
168 |
e->params.user = def_params->user; |
169 |
if (! e->params.pass) |
170 |
e->params.pass = def_params->pass; |
171 |
if (! e->params.acct) |
172 |
e->params.acct = def_params->acct; |
173 |
|
174 |
e->conn = get_host_conn (e->name, &e->params, &e->name); |
175 |
e->name = realloc (e->name, strlen (e->name) + 1 + strlen (rmt) + 1); |
176 |
if (! e->name) |
177 |
error (22, ENOMEM, "Cannot allocate name storage"); |
178 |
|
179 |
e->conn->hook = name; |
180 |
|
181 |
err = ftp_conn_set_type (e->conn, "I"); |
182 |
if (err) |
183 |
error (23, err, "%s: Cannot set connection type to binary", |
184 |
e->name); |
185 |
|
186 |
strcat (e->name, ":"); |
187 |
strcat (e->name, rmt); |
188 |
|
189 |
e->file = rmt; |
190 |
} |
191 |
else if (e->params.user || e->params.pass || e->params.acct) |
192 |
error (20, 0, |
193 |
"%s: Ftp login parameter specified for a local endpoint (%s,%s,%s)", |
194 |
e->name, e->params.user, e->params.pass, e->params.acct); |
195 |
else |
196 |
e->file = strdup (e->name); |
197 |
} |
198 |
|
199 |
static error_t |
200 |
eopen_wr (struct epoint *e, int *fd) |
201 |
{ |
202 |
if (e->conn) |
203 |
return ftp_conn_start_store (e->conn, e->file, fd); |
204 |
else if (strcmp (e->name, "-") == 0) |
205 |
*fd = 1; |
206 |
else |
207 |
{ |
208 |
*fd = open (e->name, O_WRONLY | O_CREAT | O_TRUNC, 0666); |
209 |
if (*fd < 0) |
210 |
return errno; |
211 |
} |
212 |
return 0; |
213 |
} |
214 |
|
215 |
static error_t |
216 |
eopen_rd (struct epoint *e, int *fd) |
217 |
{ |
218 |
if (e->conn) |
219 |
return ftp_conn_start_retrieve (e->conn, e->file, fd); |
220 |
else if (strcmp (e->name, "-") == 0) |
221 |
*fd = 0; |
222 |
else |
223 |
{ |
224 |
*fd = open (e->name, O_RDONLY, 0666); |
225 |
if (*fd < 0) |
226 |
return errno; |
227 |
} |
228 |
return 0; |
229 |
} |
230 |
|
231 |
static void |
232 |
efinish (struct epoint *e) |
233 |
{ |
234 |
if (e->conn) |
235 |
{ |
236 |
error_t err = ftp_conn_finish_transfer (e->conn); |
237 |
if (err) |
238 |
error (31, err, "%s", e->name); |
239 |
} |
240 |
} |
241 |
|
242 |
/* Give a name which refers to a directory file, and a name in that |
243 |
directory, this should return in COMPOSITE the composite name refering to |
244 |
that name in that directory, in malloced storage. */ |
245 |
error_t |
246 |
eappend (struct epoint *e, |
247 |
const char *dir, const char *name, |
248 |
char **composite) |
249 |
{ |
250 |
if (e->conn) |
251 |
return ftp_conn_append_name (e->conn, dir, name, composite); |
252 |
else |
253 |
{ |
254 |
char *rval = malloc (strlen (dir) + 1 + strlen (name) + 1); |
255 |
|
256 |
if (! rval) |
257 |
return ENOMEM; |
258 |
|
259 |
if (dir[0] == '/' && dir[1] == '\0') |
260 |
stpcpy (stpcpy (rval, dir), name); |
261 |
else |
262 |
stpcpy (stpcpy (stpcpy (rval, dir), "/"), name); |
263 |
|
264 |
*composite = rval; |
265 |
|
266 |
return 0; |
267 |
} |
268 |
} |
269 |
|
270 |
/* If the name of a file COMPOSITE is a composite name (containing both a |
271 |
filename and a directory name), this function will return the name |
272 |
component only in BASE, in malloced storage, otherwise it simply returns a |
273 |
newly malloced copy of COMPOSITE in BASE. */ |
274 |
error_t |
275 |
ebasename (struct epoint *e, const char *composite, char **base) |
276 |
{ |
277 |
if (e->conn) |
278 |
return ftp_conn_basename (e->conn, composite, base); |
279 |
else |
280 |
{ |
281 |
*base = strdup (basename (composite)); |
282 |
return 0; |
283 |
} |
284 |
} |
285 |
|
286 |
static void |
287 |
append_basename (struct epoint *dst, struct epoint *src) |
288 |
{ |
289 |
char *bname; |
290 |
error_t err = ebasename (src, src->file, &bname); |
291 |
|
292 |
if (err) |
293 |
error (33, err, "%s: Cannot find basename", src->name); |
294 |
|
295 |
err = eappend (dst, dst->file, bname, &dst->file); |
296 |
if (err) |
297 |
error (34, err, "%s: Cannot append name component", dst->name); |
298 |
|
299 |
err = eappend (dst, dst->name, bname, &dst->name); |
300 |
if (err) |
301 |
error (35, err, "%s: Cannot append name component", dst->name); |
302 |
} |
303 |
|
304 |
int |
305 |
main (int argc, char **argv) |
306 |
{ |
307 |
error_t err; |
308 |
struct epoint rd = { 0 }, wr = { 0 }; |
309 |
struct ftp_conn_params def_params = { 0 }; /* default params */ |
310 |
|
311 |
/* Parse our options... */ |
312 |
error_t parse_opt (int key, char *arg, struct argp_state *state) |
313 |
{ |
314 |
switch (key) |
315 |
{ |
316 |
case ARGP_KEY_ARG: |
317 |
switch (state->arg_num) |
318 |
{ |
319 |
case 0: rd.name = arg; break; |
320 |
case 1: wr.name = arg; break; |
321 |
default: return ARGP_ERR_UNKNOWN; |
322 |
} |
323 |
break; |
324 |
case ARGP_KEY_NO_ARGS: |
325 |
argp_usage (state); |
326 |
|
327 |
case 'u': def_params.user = arg; break; |
328 |
case 'p': def_params.pass = arg; break; |
329 |
case 'a': def_params.acct = arg; break; |
330 |
|
331 |
case OPT_SRC_U: rd.params.user = arg; break; |
332 |
case OPT_SRC_P: rd.params.pass = arg; break; |
333 |
case OPT_SRC_A: rd.params.acct = arg; break; |
334 |
|
335 |
case OPT_DST_U: wr.params.user = arg; break; |
336 |
case OPT_DST_P: wr.params.pass = arg; break; |
337 |
case OPT_DST_A: wr.params.acct = arg; break; |
338 |
|
339 |
case 'D': conn_hooks.cntl_debug = cntl_debug; break; |
340 |
|
341 |
default: |
342 |
return ARGP_ERR_UNKNOWN; |
343 |
} |
344 |
return 0; |
345 |
} |
346 |
struct argp argp = {options, parse_opt, args_doc, doc}; |
347 |
|
348 |
argp_parse (&argp, argc, argv, 0, 0, 0); |
349 |
|
350 |
econnect (&rd, &def_params, "SRC"); |
351 |
econnect (&wr, &def_params, "DST"); |
352 |
|
353 |
if (rd.conn && wr.conn) |
354 |
/* Both endpoints are remote; directly copy between ftp servers. */ |
355 |
{ |
356 |
err = ftp_conn_rmt_copy (rd.conn, rd.file, wr.conn, wr.file); |
357 |
if (err == EISDIR) |
358 |
/* The destination name is a directory; try again with the source |
359 |
basename appended. */ |
360 |
{ |
361 |
append_basename (&wr, &rd); |
362 |
err = ftp_conn_rmt_copy (rd.conn, rd.file, wr.conn, wr.file); |
363 |
} |
364 |
if (err) |
365 |
error (30, err, "Remote copy"); |
366 |
} |
367 |
else |
368 |
/* One endpoint is local, so do the copying ourself. */ |
369 |
{ |
370 |
int rd_fd, wr_fd; |
371 |
|
372 |
err = eopen_rd (&rd, &rd_fd); |
373 |
if (err) |
374 |
error (31, err, "%s", rd.name); |
375 |
|
376 |
err = eopen_wr (&wr, &wr_fd); |
377 |
if (err == EISDIR) |
378 |
/* The destination name is a directory; try again with the source |
379 |
basename appended. */ |
380 |
{ |
381 |
append_basename (&wr, &rd); |
382 |
err = eopen_wr (&wr, &wr_fd); |
383 |
} |
384 |
if (err) |
385 |
error (32, err, "%s", wr.name); |
386 |
|
387 |
cp (rd_fd, rd.name, wr_fd, wr.name); |
388 |
|
389 |
close (rd_fd); |
390 |
close (wr_fd); |
391 |
|
392 |
efinish (&rd); |
393 |
efinish (&wr); |
394 |
} |
395 |
|
396 |
exit (0); |
397 |
} |