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