source: trunk/third/nmh/uip/ftpsbr.c @ 19067

Revision 19067, 9.8 KB checked in by ghudson, 22 years ago (diff)
Use errno.h for errno.
Line 
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
18static int ftp_fd = NOTOK;
19static int data_fd = NOTOK;
20
21static int v_noise;
22
23extern int v_debug;
24extern 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 */
49struct hostent *gethostbystring ();
50int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
51int ftp_trans (char *, char *, char *, char *, char *, char *, char *, int, int);
52
53/*
54 * static prototypes
55 */
56static int start_tcp_server (struct sockaddr_in *, int, int, int);
57static void _asnprintf (char *, int, char *, va_list);
58static int ftp_quit (void);
59static int ftp_read (char *, char *, char *, int);
60static int initconn (void);
61static int dataconn (void);
62static int command (int arg1, ...);
63static int vcommand (int, va_list);
64static int getreply (int, int);
65
66
67static int
68start_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
87static 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
97static 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
135int
136ftp_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
144int
145ftp_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
216out: ;
217    if (result != OK || !stayopen)
218        ftp_quit ();
219
220    return result;
221}
222
223
224static int
225ftp_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
237static int
238ftp_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) {
255bad: ;
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
372static int
373initconn (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
407static int
408dataconn (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
424static int
425command (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
437static int
438vcommand (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
462static int
463getreply (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}
Note: See TracBrowser for help on using the repository browser.