add a note about Irix make
[mmh] / uip / ftpsbr.c
1
2 /*
3  * ftpsbr.c -- simple FTP client library
4  *
5  * $Id$
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 extern int errno;
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 }