1 | |
---|
2 | /* |
---|
3 | * ftpsbr.c -- simple FTP client library |
---|
4 | * |
---|
5 | * $Id: ftpsbr.c,v 1.2 2003-03-20 17:19:30 ghudson Exp $ |
---|
6 | */ |
---|
7 | |
---|
8 | #include <h/mh.h> |
---|
9 | #include <h/mime.h> |
---|
10 | |
---|
11 | #ifdef HAVE_ARPA_FTP_H |
---|
12 | # include <arpa/ftp.h> |
---|
13 | #endif |
---|
14 | |
---|
15 | #define v_debug debugsw |
---|
16 | #define v_verbose verbosw |
---|
17 | |
---|
18 | static int ftp_fd = NOTOK; |
---|
19 | static int data_fd = NOTOK; |
---|
20 | |
---|
21 | static int v_noise; |
---|
22 | |
---|
23 | extern int v_debug; |
---|
24 | extern int v_verbose; |
---|
25 | |
---|
26 | #include <sys/types.h> |
---|
27 | #include <sys/socket.h> |
---|
28 | #include <netinet/in.h> |
---|
29 | #include <netdb.h> |
---|
30 | |
---|
31 | #if defined(BIND) && !defined(h_addr) |
---|
32 | # define h_addr h_addr_list[0] |
---|
33 | #endif |
---|
34 | |
---|
35 | #define inaddr_copy(hp,sin) \ |
---|
36 | memcpy((char *) &((sin)->sin_addr), (hp)->h_addr, (hp)->h_length) |
---|
37 | |
---|
38 | #include <errno.h> |
---|
39 | |
---|
40 | #define start_tcp_client(sock,priv) \ |
---|
41 | socket (AF_INET, SOCK_STREAM, 0) |
---|
42 | |
---|
43 | #define join_tcp_server(fd, sock) \ |
---|
44 | connect ((fd), (struct sockaddr *) (sock), sizeof *(sock)) |
---|
45 | |
---|
46 | /* |
---|
47 | * prototypes |
---|
48 | */ |
---|
49 | struct hostent *gethostbystring (); |
---|
50 | int ftp_get (char *, char *, char *, char *, char *, char *, int, int); |
---|
51 | int ftp_trans (char *, char *, char *, char *, char *, char *, char *, int, int); |
---|
52 | |
---|
53 | /* |
---|
54 | * static prototypes |
---|
55 | */ |
---|
56 | static int start_tcp_server (struct sockaddr_in *, int, int, int); |
---|
57 | static void _asnprintf (char *, int, char *, va_list); |
---|
58 | static int ftp_quit (void); |
---|
59 | static int ftp_read (char *, char *, char *, int); |
---|
60 | static int initconn (void); |
---|
61 | static int dataconn (void); |
---|
62 | static int command (int arg1, ...); |
---|
63 | static int vcommand (int, va_list); |
---|
64 | static int getreply (int, int); |
---|
65 | |
---|
66 | |
---|
67 | static int |
---|
68 | start_tcp_server (struct sockaddr_in *sock, int backlog, int opt1, int opt2) |
---|
69 | { |
---|
70 | int eindex, sd; |
---|
71 | |
---|
72 | if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == NOTOK) |
---|
73 | return NOTOK; |
---|
74 | |
---|
75 | if (bind (sd, (struct sockaddr *) sock, sizeof *sock) == NOTOK) { |
---|
76 | eindex = errno; |
---|
77 | close (sd); |
---|
78 | errno = eindex; |
---|
79 | } else { |
---|
80 | listen (sd, backlog); |
---|
81 | } |
---|
82 | |
---|
83 | return sd; |
---|
84 | } |
---|
85 | |
---|
86 | |
---|
87 | static int __len__; |
---|
88 | |
---|
89 | #define join_tcp_client(fd,sock) \ |
---|
90 | accept ((fd), (struct sockaddr *) (sock), \ |
---|
91 | (__len__ = sizeof *(sock), &__len__)) |
---|
92 | |
---|
93 | #define read_tcp_socket read |
---|
94 | #define write_tcp_socket write |
---|
95 | #define close_tcp_socket close |
---|
96 | |
---|
97 | static void |
---|
98 | _asnprintf (char *bp, int len_bp, char *what, va_list ap) |
---|
99 | { |
---|
100 | int eindex, len; |
---|
101 | char *fmt; |
---|
102 | |
---|
103 | eindex = errno; |
---|
104 | |
---|
105 | *bp = '\0'; |
---|
106 | fmt = va_arg (ap, char *); |
---|
107 | |
---|
108 | if (fmt) { |
---|
109 | vsnprintf(bp, len_bp, fmt, ap); |
---|
110 | len = strlen(bp); |
---|
111 | bp += len; |
---|
112 | len_bp -= len; |
---|
113 | } |
---|
114 | |
---|
115 | if (what) { |
---|
116 | char *s; |
---|
117 | |
---|
118 | if (*what) { |
---|
119 | snprintf (bp, len_bp, " %s: ", what); |
---|
120 | len = strlen (bp); |
---|
121 | bp += len; |
---|
122 | len_bp -= len; |
---|
123 | } |
---|
124 | if ((s = strerror(eindex))) |
---|
125 | strncpy (bp, s, len_bp); |
---|
126 | else |
---|
127 | snprintf (bp, len_bp, "Error %d", eindex); |
---|
128 | bp += strlen (bp); |
---|
129 | } |
---|
130 | |
---|
131 | errno = eindex; |
---|
132 | } |
---|
133 | |
---|
134 | |
---|
135 | int |
---|
136 | ftp_get (char *host, char *user, char *password, char *cwd, |
---|
137 | char *remote, char *local, int ascii, int stayopen) |
---|
138 | { |
---|
139 | return ftp_trans (host, user, password, cwd, remote, local, |
---|
140 | "RETR", ascii, stayopen); |
---|
141 | } |
---|
142 | |
---|
143 | |
---|
144 | int |
---|
145 | ftp_trans (char *host, char *user, char *password, char *cwd, char *remote, |
---|
146 | char *local, char *cmd, int ascii, int stayopen) |
---|
147 | { |
---|
148 | int result; |
---|
149 | |
---|
150 | if (stayopen <= 0) { |
---|
151 | result = ftp_quit (); |
---|
152 | if (host == NULL) |
---|
153 | return result; |
---|
154 | } |
---|
155 | |
---|
156 | if (ftp_fd == NOTOK) { |
---|
157 | struct sockaddr_in in_socket; |
---|
158 | register struct hostent *hp; |
---|
159 | register struct servent *sp; |
---|
160 | |
---|
161 | if ((sp = getservbyname ("ftp", "tcp")) == NULL) { |
---|
162 | fprintf (stderr, "tcp/ftp: unknown service"); |
---|
163 | return NOTOK; |
---|
164 | } |
---|
165 | if ((hp = gethostbystring (host)) == NULL) { |
---|
166 | fprintf (stderr, "%s: unknown host\n", host); |
---|
167 | return NOTOK; |
---|
168 | } |
---|
169 | in_socket.sin_family = hp->h_addrtype; |
---|
170 | inaddr_copy (hp, &in_socket); |
---|
171 | in_socket.sin_port = sp->s_port; |
---|
172 | |
---|
173 | if ((ftp_fd = start_tcp_client ((struct sockaddr_in *) NULL, 0)) |
---|
174 | == NOTOK) { |
---|
175 | perror (host); |
---|
176 | return NOTOK; |
---|
177 | } |
---|
178 | if (join_tcp_server (ftp_fd, &in_socket) == NOTOK) { |
---|
179 | perror (host); |
---|
180 | close_tcp_socket (ftp_fd), ftp_fd = NOTOK; |
---|
181 | return NOTOK; |
---|
182 | } |
---|
183 | getreply (1, 0); |
---|
184 | |
---|
185 | if (v_verbose) { |
---|
186 | fprintf (stdout, "Connected to %s\n", host); |
---|
187 | fflush (stdout); |
---|
188 | } |
---|
189 | |
---|
190 | if (user) { |
---|
191 | if ((result = command (0, "USER %s", user)) == CONTINUE) |
---|
192 | result = command (1, "PASS %s", password); |
---|
193 | if (result != COMPLETE) { |
---|
194 | result = NOTOK; |
---|
195 | goto out; |
---|
196 | } |
---|
197 | } |
---|
198 | |
---|
199 | if (remote == NULL) |
---|
200 | return OK; |
---|
201 | } |
---|
202 | |
---|
203 | if (cwd && ((result = command (0, "CWD %s", cwd)) != COMPLETE |
---|
204 | && result != CONTINUE)) { |
---|
205 | result = NOTOK; |
---|
206 | goto out; |
---|
207 | } |
---|
208 | |
---|
209 | if (command (1, ascii ? "TYPE A" : "TYPE I") != COMPLETE) { |
---|
210 | result = NOTOK; |
---|
211 | goto out; |
---|
212 | } |
---|
213 | |
---|
214 | result = ftp_read (remote, local, cmd, ascii); |
---|
215 | |
---|
216 | out: ; |
---|
217 | if (result != OK || !stayopen) |
---|
218 | ftp_quit (); |
---|
219 | |
---|
220 | return result; |
---|
221 | } |
---|
222 | |
---|
223 | |
---|
224 | static int |
---|
225 | ftp_quit (void) |
---|
226 | { |
---|
227 | int n; |
---|
228 | |
---|
229 | if (ftp_fd == NOTOK) |
---|
230 | return OK; |
---|
231 | |
---|
232 | n = command (1, "QUIT"); |
---|
233 | close_tcp_socket (ftp_fd), ftp_fd = NOTOK; |
---|
234 | return (n == 0 || n == COMPLETE ? OK : NOTOK); |
---|
235 | } |
---|
236 | |
---|
237 | static int |
---|
238 | ftp_read (char *remote, char *local, char *cmd, int ascii) |
---|
239 | { |
---|
240 | int istdio = 0, istore; |
---|
241 | register int cc; |
---|
242 | int expectingreply = 0; |
---|
243 | char buffer[BUFSIZ]; |
---|
244 | FILE *fp = NULL; |
---|
245 | |
---|
246 | if (initconn () == NOTOK) |
---|
247 | goto bad; |
---|
248 | |
---|
249 | v_noise = v_verbose; |
---|
250 | if (command (-1, *remote ? "%s %s" : "%s", cmd, remote) != PRELIM) |
---|
251 | goto bad; |
---|
252 | |
---|
253 | expectingreply++; |
---|
254 | if (dataconn () == NOTOK) { |
---|
255 | bad: ; |
---|
256 | if (fp && !istdio) |
---|
257 | fclose (fp); |
---|
258 | if (data_fd != NOTOK) |
---|
259 | close_tcp_socket (data_fd), data_fd = NOTOK; |
---|
260 | if (expectingreply) |
---|
261 | getreply (-2, 0); |
---|
262 | |
---|
263 | return NOTOK; |
---|
264 | } |
---|
265 | |
---|
266 | istore = !strcmp (cmd, "STOR"); |
---|
267 | |
---|
268 | if ((istdio = !strcmp (local, "-"))) |
---|
269 | fp = istore ? stdin : stdout; |
---|
270 | else |
---|
271 | if ((fp = fopen (local, istore ? "r" : "w")) == NULL) { |
---|
272 | perror (local); |
---|
273 | goto bad; |
---|
274 | } |
---|
275 | |
---|
276 | if (istore) { |
---|
277 | if (ascii) { |
---|
278 | int c; |
---|
279 | FILE *out; |
---|
280 | |
---|
281 | if (!(out = fdopen (data_fd, "w"))) { |
---|
282 | perror ("fdopen"); |
---|
283 | goto bad; |
---|
284 | } |
---|
285 | |
---|
286 | while ((c = getc (fp)) != EOF) { |
---|
287 | if (c == '\n') |
---|
288 | putc ('\r', out); |
---|
289 | if (putc (c, out) == EOF) { |
---|
290 | perror ("putc"); |
---|
291 | fclose (out); |
---|
292 | data_fd = NOTOK; |
---|
293 | goto bad; |
---|
294 | } |
---|
295 | } |
---|
296 | |
---|
297 | fclose (out); |
---|
298 | data_fd = NOTOK; |
---|
299 | } |
---|
300 | else { |
---|
301 | while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, fp)) > 0) |
---|
302 | if (write_tcp_socket (data_fd, buffer, cc) != cc) { |
---|
303 | perror ("write_tcp_socket"); |
---|
304 | goto bad; |
---|
305 | } |
---|
306 | |
---|
307 | close_tcp_socket (data_fd), data_fd = NOTOK; |
---|
308 | } |
---|
309 | } |
---|
310 | else { |
---|
311 | if (ascii) { |
---|
312 | int c; |
---|
313 | FILE *in; |
---|
314 | |
---|
315 | if (!(in = fdopen (data_fd, "r"))) { |
---|
316 | perror ("fdopen"); |
---|
317 | goto bad; |
---|
318 | } |
---|
319 | |
---|
320 | while ((c = getc (in)) != EOF) { |
---|
321 | if (c == '\r') |
---|
322 | switch (c = getc (in)) { |
---|
323 | case EOF: |
---|
324 | case '\0': |
---|
325 | c = '\r'; |
---|
326 | break; |
---|
327 | |
---|
328 | case '\n': |
---|
329 | break; |
---|
330 | |
---|
331 | default: |
---|
332 | putc ('\r', fp); |
---|
333 | break; |
---|
334 | } |
---|
335 | |
---|
336 | if (putc (c, fp) == EOF) { |
---|
337 | perror ("putc"); |
---|
338 | fclose (in); |
---|
339 | data_fd = NOTOK; |
---|
340 | goto bad; |
---|
341 | } |
---|
342 | } |
---|
343 | |
---|
344 | fclose (in); |
---|
345 | data_fd = NOTOK; |
---|
346 | } |
---|
347 | else { |
---|
348 | while ((cc = read_tcp_socket (data_fd, buffer, sizeof buffer)) > 0) |
---|
349 | if (fwrite (buffer, sizeof *buffer, cc, fp) == 0) { |
---|
350 | perror ("fwrite"); |
---|
351 | goto bad; |
---|
352 | } |
---|
353 | if (cc < 0) { |
---|
354 | perror ("read_tcp_socket"); |
---|
355 | goto bad; |
---|
356 | } |
---|
357 | |
---|
358 | close_tcp_socket (data_fd), data_fd = NOTOK; |
---|
359 | } |
---|
360 | } |
---|
361 | |
---|
362 | if (!istdio) |
---|
363 | fclose (fp); |
---|
364 | |
---|
365 | v_noise = v_verbose; |
---|
366 | return (getreply (1, 0) == COMPLETE ? OK : NOTOK); |
---|
367 | } |
---|
368 | |
---|
369 | |
---|
370 | #define UC(b) (((int) b) & 0xff) |
---|
371 | |
---|
372 | static int |
---|
373 | initconn (void) |
---|
374 | { |
---|
375 | int len; |
---|
376 | register char *a, *p; |
---|
377 | struct sockaddr_in in_socket; |
---|
378 | |
---|
379 | if (getsockname (ftp_fd, (struct sockaddr *) &in_socket, |
---|
380 | (len = sizeof(in_socket), &len)) == NOTOK) { |
---|
381 | perror ("getsockname"); |
---|
382 | return NOTOK; |
---|
383 | } |
---|
384 | in_socket.sin_port = 0; |
---|
385 | if ((data_fd = start_tcp_server (&in_socket, 1, 0, 0)) == NOTOK) { |
---|
386 | perror ("start_tcp_server"); |
---|
387 | return NOTOK; |
---|
388 | } |
---|
389 | |
---|
390 | if (getsockname (data_fd, (struct sockaddr *) &in_socket, |
---|
391 | (len = sizeof in_socket, &len)) == NOTOK) { |
---|
392 | perror ("getsockname"); |
---|
393 | return NOTOK; |
---|
394 | } |
---|
395 | |
---|
396 | a = (char *) &in_socket.sin_addr; |
---|
397 | p = (char *) &in_socket.sin_port; |
---|
398 | |
---|
399 | if (command (1, "PORT %d,%d,%d,%d,%d,%d", |
---|
400 | UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), |
---|
401 | UC(p[0]), UC(p[1])) == COMPLETE) |
---|
402 | return OK; |
---|
403 | |
---|
404 | return NOTOK; |
---|
405 | } |
---|
406 | |
---|
407 | static int |
---|
408 | dataconn (void) |
---|
409 | { |
---|
410 | int fd; |
---|
411 | struct sockaddr_in in_socket; |
---|
412 | |
---|
413 | if ((fd = join_tcp_client (data_fd, &in_socket)) == NOTOK) { |
---|
414 | perror ("join_tcp_client"); |
---|
415 | return NOTOK; |
---|
416 | } |
---|
417 | close_tcp_socket (data_fd); |
---|
418 | data_fd = fd; |
---|
419 | |
---|
420 | return OK; |
---|
421 | } |
---|
422 | |
---|
423 | |
---|
424 | static int |
---|
425 | command (int arg1, ...) |
---|
426 | { |
---|
427 | int val; |
---|
428 | va_list ap; |
---|
429 | |
---|
430 | va_start (ap, arg1); |
---|
431 | val = vcommand (arg1, ap); |
---|
432 | va_end (ap); |
---|
433 | |
---|
434 | return val; |
---|
435 | } |
---|
436 | |
---|
437 | static int |
---|
438 | vcommand (int complete, va_list ap) |
---|
439 | { |
---|
440 | int len; |
---|
441 | char buffer[BUFSIZ]; |
---|
442 | |
---|
443 | if (ftp_fd == NOTOK) |
---|
444 | return NOTOK; |
---|
445 | |
---|
446 | _asnprintf (buffer, sizeof(buffer), NULL, ap); |
---|
447 | if (v_debug) |
---|
448 | fprintf (stderr, "<--- %s\n", buffer); |
---|
449 | |
---|
450 | strcat (buffer, "\r\n"); |
---|
451 | len = strlen (buffer); |
---|
452 | |
---|
453 | if (write_tcp_socket (ftp_fd, buffer, len) != len) { |
---|
454 | perror ("write_tcp_socket"); |
---|
455 | return NOTOK; |
---|
456 | } |
---|
457 | |
---|
458 | return (getreply (complete, !strcmp (buffer, "QUIT"))); |
---|
459 | } |
---|
460 | |
---|
461 | |
---|
462 | static int |
---|
463 | getreply (int complete, int expecteof) |
---|
464 | { |
---|
465 | for (;;) { |
---|
466 | register int code, dig, n; |
---|
467 | int continuation; |
---|
468 | register char *bp; |
---|
469 | char buffer[BUFSIZ]; |
---|
470 | |
---|
471 | code = dig = n = continuation = 0; |
---|
472 | bp = buffer; |
---|
473 | |
---|
474 | for (;;) { |
---|
475 | char c; |
---|
476 | |
---|
477 | if (read_tcp_socket (ftp_fd, &c, 1) < 1) { |
---|
478 | if (expecteof) |
---|
479 | return OK; |
---|
480 | |
---|
481 | perror ("read_tcp_socket"); |
---|
482 | return DONE; |
---|
483 | } |
---|
484 | if (c == '\n') |
---|
485 | break; |
---|
486 | *bp++ = c != '\r' ? c : '\0'; |
---|
487 | |
---|
488 | dig++; |
---|
489 | if (dig < 4) { |
---|
490 | if (isdigit(c)) |
---|
491 | code = code * 10 + (c - '0'); |
---|
492 | else /* XXX: naughty FTP... */ |
---|
493 | if (isspace (c)) |
---|
494 | continuation++; |
---|
495 | } |
---|
496 | else |
---|
497 | if (dig == 4 && c == '-') |
---|
498 | continuation++; |
---|
499 | if (n == 0) |
---|
500 | n = c; |
---|
501 | } |
---|
502 | |
---|
503 | if (v_debug) |
---|
504 | fprintf (stderr, "---> %s\n", buffer); |
---|
505 | if (continuation) |
---|
506 | continue; |
---|
507 | |
---|
508 | n -= '0'; |
---|
509 | |
---|
510 | if (v_noise) { |
---|
511 | fprintf (stdout, "%s\n", buffer); |
---|
512 | fflush (stdout); |
---|
513 | v_noise = 0; |
---|
514 | } |
---|
515 | else |
---|
516 | if ((complete == -1 && n != PRELIM) |
---|
517 | || (complete == 0 && n != CONTINUE && n != COMPLETE) |
---|
518 | || (complete == 1 && n != COMPLETE)) |
---|
519 | fprintf (stderr, "%s\n", buffer); |
---|
520 | |
---|
521 | return n; |
---|
522 | } |
---|
523 | } |
---|