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