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