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