]> diplodocus.org Git - nmh/blob - uip/ftpsbr.c
1d6727fa22e5859fe25891a9398830378d9f4e5f
[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 #ifdef AI_ADDRCONFIG
155 hints.ai_flags = AI_ADDRCONFIG;
156 #endif
157 hints.ai_family = PF_INET;
158 hints.ai_socktype = SOCK_STREAM;
159
160 result = getaddrinfo(host, "ftp", &hints, &res);
161
162 if (result) {
163 fprintf(stderr, "%s/ftp: %s\n", host, gai_strerror(result));
164 return NOTOK;
165 }
166
167 if ((ftp_fd = start_tcp_client (res)) == NOTOK) {
168 perror (host);
169 freeaddrinfo(res);
170 return NOTOK;
171 }
172 if (join_tcp_server (ftp_fd, res->ai_addr, res->ai_addrlen) == NOTOK) {
173 perror (host);
174 freeaddrinfo(res);
175 close_tcp_socket (ftp_fd), ftp_fd = NOTOK;
176 return NOTOK;
177 }
178 freeaddrinfo(res);
179 getreply (1, 0);
180
181 if (v_verbose) {
182 fprintf (stdout, "Connected to %s\n", host);
183 fflush (stdout);
184 }
185
186 if (user) {
187 if ((result = command (0, "USER %s", user)) == CONTINUE)
188 result = command (1, "PASS %s", password);
189 if (result != COMPLETE) {
190 result = NOTOK;
191 goto out;
192 }
193 }
194
195 if (remote == NULL)
196 return OK;
197 }
198
199 if (cwd && ((result = command (0, "CWD %s", cwd)) != COMPLETE
200 && result != CONTINUE)) {
201 result = NOTOK;
202 goto out;
203 }
204
205 if (command (1, ascii ? "TYPE A" : "TYPE I") != COMPLETE) {
206 result = NOTOK;
207 goto out;
208 }
209
210 result = ftp_read (remote, local, cmd, ascii);
211
212 out: ;
213 if (result != OK || !stayopen)
214 ftp_quit ();
215
216 return result;
217 }
218
219
220 static int
221 ftp_quit (void)
222 {
223 int n;
224
225 if (ftp_fd == NOTOK)
226 return OK;
227
228 n = command (1, "QUIT");
229 close_tcp_socket (ftp_fd), ftp_fd = NOTOK;
230 return (n == 0 || n == COMPLETE ? OK : NOTOK);
231 }
232
233 static int
234 ftp_read (char *remote, char *local, char *cmd, int ascii)
235 {
236 int istdio = 0, istore;
237 register int cc;
238 int expectingreply = 0;
239 char buffer[BUFSIZ];
240 FILE *fp = NULL;
241
242 if (initconn () == NOTOK)
243 goto bad;
244
245 v_noise = v_verbose;
246 if (command (-1, *remote ? "%s %s" : "%s", cmd, remote) != PRELIM)
247 goto bad;
248
249 expectingreply++;
250 if (dataconn () == NOTOK) {
251 bad: ;
252 if (fp && !istdio)
253 fclose (fp);
254 if (data_fd != NOTOK)
255 close_tcp_socket (data_fd), data_fd = NOTOK;
256 if (expectingreply)
257 getreply (-2, 0);
258
259 return NOTOK;
260 }
261
262 istore = !strcmp (cmd, "STOR");
263
264 if ((istdio = !strcmp (local, "-")))
265 fp = istore ? stdin : stdout;
266 else
267 if ((fp = fopen (local, istore ? "r" : "w")) == NULL) {
268 perror (local);
269 goto bad;
270 }
271
272 if (istore) {
273 if (ascii) {
274 int c;
275 FILE *out;
276
277 if (!(out = fdopen (data_fd, "w"))) {
278 perror ("fdopen");
279 goto bad;
280 }
281
282 while ((c = getc (fp)) != EOF) {
283 if (c == '\n')
284 putc ('\r', out);
285 if (putc (c, out) == EOF) {
286 perror ("putc");
287 fclose (out);
288 data_fd = NOTOK;
289 goto bad;
290 }
291 }
292
293 fclose (out);
294 data_fd = NOTOK;
295 }
296 else {
297 while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, fp)) > 0)
298 if (write_tcp_socket (data_fd, buffer, cc) != cc) {
299 perror ("write_tcp_socket");
300 goto bad;
301 }
302
303 close_tcp_socket (data_fd), data_fd = NOTOK;
304 }
305 }
306 else {
307 if (ascii) {
308 int c;
309 FILE *in;
310
311 if (!(in = fdopen (data_fd, "r"))) {
312 perror ("fdopen");
313 goto bad;
314 }
315
316 while ((c = getc (in)) != EOF) {
317 if (c == '\r')
318 switch (c = getc (in)) {
319 case EOF:
320 case '\0':
321 c = '\r';
322 break;
323
324 case '\n':
325 break;
326
327 default:
328 putc ('\r', fp);
329 break;
330 }
331
332 if (putc (c, fp) == EOF) {
333 perror ("putc");
334 fclose (in);
335 data_fd = NOTOK;
336 goto bad;
337 }
338 }
339
340 fclose (in);
341 data_fd = NOTOK;
342 }
343 else {
344 while ((cc = read_tcp_socket (data_fd, buffer, sizeof buffer)) > 0)
345 if (fwrite (buffer, sizeof *buffer, cc, fp) == 0) {
346 perror ("fwrite");
347 goto bad;
348 }
349 if (cc < 0) {
350 perror ("read_tcp_socket");
351 goto bad;
352 }
353
354 close_tcp_socket (data_fd), data_fd = NOTOK;
355 }
356 }
357
358 if (!istdio)
359 fclose (fp);
360
361 v_noise = v_verbose;
362 return (getreply (1, 0) == COMPLETE ? OK : NOTOK);
363 }
364
365
366 #define UC(b) (((int) b) & 0xff)
367
368 static int
369 initconn (void)
370 {
371 int len;
372 register char *a, *p;
373 struct sockaddr_in in_socket;
374
375 if (getsockname (ftp_fd, (struct sockaddr *) &in_socket,
376 (len = sizeof(in_socket), &len)) == NOTOK) {
377 perror ("getsockname");
378 return NOTOK;
379 }
380 in_socket.sin_port = 0;
381 if ((data_fd = start_tcp_server (&in_socket, 1, 0, 0)) == NOTOK) {
382 perror ("start_tcp_server");
383 return NOTOK;
384 }
385
386 if (getsockname (data_fd, (struct sockaddr *) &in_socket,
387 (len = sizeof in_socket, &len)) == NOTOK) {
388 perror ("getsockname");
389 return NOTOK;
390 }
391
392 a = (char *) &in_socket.sin_addr;
393 p = (char *) &in_socket.sin_port;
394
395 if (command (1, "PORT %d,%d,%d,%d,%d,%d",
396 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
397 UC(p[0]), UC(p[1])) == COMPLETE)
398 return OK;
399
400 return NOTOK;
401 }
402
403 static int
404 dataconn (void)
405 {
406 int fd;
407 struct sockaddr_in in_socket;
408
409 if ((fd = join_tcp_client (data_fd, &in_socket)) == NOTOK) {
410 perror ("join_tcp_client");
411 return NOTOK;
412 }
413 close_tcp_socket (data_fd);
414 data_fd = fd;
415
416 return OK;
417 }
418
419
420 static int
421 command (int arg1, ...)
422 {
423 int val;
424 va_list ap;
425
426 va_start (ap, arg1);
427 val = vcommand (arg1, ap);
428 va_end (ap);
429
430 return val;
431 }
432
433 static int
434 vcommand (int complete, va_list ap)
435 {
436 int len;
437 char buffer[BUFSIZ];
438
439 if (ftp_fd == NOTOK)
440 return NOTOK;
441
442 _asnprintf (buffer, sizeof(buffer), NULL, ap);
443 if (v_debug)
444 fprintf (stderr, "<--- %s\n", buffer);
445
446 strcat (buffer, "\r\n");
447 len = strlen (buffer);
448
449 if (write_tcp_socket (ftp_fd, buffer, len) != len) {
450 perror ("write_tcp_socket");
451 return NOTOK;
452 }
453
454 return (getreply (complete, !strcmp (buffer, "QUIT")));
455 }
456
457
458 static int
459 getreply (int complete, int expecteof)
460 {
461 for (;;) {
462 register int code, dig, n;
463 int continuation;
464 register char *bp;
465 char buffer[BUFSIZ];
466
467 code = dig = n = continuation = 0;
468 bp = buffer;
469
470 for (;;) {
471 unsigned char c;
472
473 if (read_tcp_socket (ftp_fd, &c, 1) < 1) {
474 if (expecteof)
475 return OK;
476
477 perror ("read_tcp_socket");
478 return DONE;
479 }
480 if (c == '\n')
481 break;
482 *bp++ = c != '\r' ? c : '\0';
483
484 dig++;
485 if (dig < 4) {
486 if (isdigit(c))
487 code = code * 10 + (c - '0');
488 else /* XXX: naughty FTP... */
489 if (isspace (c))
490 continuation++;
491 }
492 else
493 if (dig == 4 && c == '-')
494 continuation++;
495 if (n == 0)
496 n = c;
497 }
498
499 if (v_debug)
500 fprintf (stderr, "---> %s\n", buffer);
501 if (continuation)
502 continue;
503
504 n -= '0';
505
506 if (v_noise) {
507 fprintf (stdout, "%s\n", buffer);
508 fflush (stdout);
509 v_noise = 0;
510 }
511 else
512 if ((complete == -1 && n != PRELIM)
513 || (complete == 0 && n != CONTINUE && n != COMPLETE)
514 || (complete == 1 && n != COMPLETE))
515 fprintf (stderr, "%s\n", buffer);
516
517 return n;
518 }
519 }