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