Relayouted all switch statements: case aligns with switch.
[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",
163                                         host, gai_strerror(result));
164                         return NOTOK;
165                 }
166
167                 if ((ftp_fd = start_tcp_client(res)) == NOTOK) {
168                         perror(host);
169                         freeaddrinfo(res);
170                         return NOTOK;
171                 }
172                 if (join_tcp_server(ftp_fd, res->ai_addr, res->ai_addrlen)
173                                 == NOTOK) {
174                         perror(host);
175                         freeaddrinfo(res);
176                         close_tcp_socket(ftp_fd), ftp_fd = NOTOK;
177                         return NOTOK;
178                 }
179                 freeaddrinfo(res);
180                 getreply(1, 0);
181
182                 if (v_verbose) {
183                         fprintf(stdout, "Connected to %s\n", host);
184                         fflush(stdout);
185                 }
186
187                 if (user) {
188                         if ((result = command (0, "USER %s", user))
189                                         == CONTINUE)
190                                 result = command(1, "PASS %s", password);
191                         if (result != COMPLETE) {
192                                 result = NOTOK;
193                                 goto out;
194                         }
195                 }
196
197                 if (remote == NULL)
198                         return OK;
199         }
200
201         if (cwd && ((result = command (0, "CWD %s", cwd)) != COMPLETE
202                         && result != CONTINUE)) {
203                 result = NOTOK;
204                 goto out;
205         }
206
207         if (command(1, ascii ? "TYPE A" : "TYPE I") != COMPLETE) {
208                 result = NOTOK;
209                 goto out;
210         }
211
212         result = ftp_read(remote, local, cmd, ascii);
213
214 out: ;
215         if (result != OK || !stayopen)
216                 ftp_quit();
217
218         return result;
219 }
220
221
222 static int
223 ftp_quit(void)
224 {
225         int n;
226
227         if (ftp_fd == NOTOK)
228                 return OK;
229
230         n = command(1, "QUIT");
231         close_tcp_socket(ftp_fd), ftp_fd = NOTOK;
232         return (n == 0 || n == COMPLETE ? OK : NOTOK);
233 }
234
235 static int
236 ftp_read(char *remote, char *local, char *cmd, int ascii)
237 {
238         int istdio = 0, istore;
239         register int cc;
240         int expectingreply = 0;
241         char buffer[BUFSIZ];
242         FILE *fp = NULL;
243
244         if (initconn() == NOTOK)
245                 goto bad;
246
247         v_noise = v_verbose;
248         if (command(-1, *remote ? "%s %s" : "%s", cmd, remote) != PRELIM)
249                 goto bad;
250
251         expectingreply++;
252         if (dataconn() == NOTOK) {
253 bad: ;
254                 if (fp && !istdio)
255                         fclose(fp);
256                 if (data_fd != NOTOK)
257                         close_tcp_socket(data_fd), data_fd = NOTOK;
258                 if (expectingreply)
259                         getreply(-2, 0);
260
261                 return NOTOK;
262         }
263
264         istore = !strcmp(cmd, "STOR");
265
266         if ((istdio = !strcmp(local, "-")))
267                 fp = istore ? stdin : stdout;
268         else
269                 if ((fp = fopen(local, istore ? "r" : "w")) == NULL) {
270                         perror(local);
271                         goto bad;
272                 }
273
274         if (istore) {
275                 if (ascii) {
276                         int c;
277                         FILE *out;
278
279                         if (!(out = fdopen(data_fd, "w"))) {
280                                 perror("fdopen");
281                                 goto bad;
282                         }
283
284                         while ((c = getc(fp)) != EOF) {
285                                 if (c == '\n')
286                                         putc('\r', out);
287                                 if (putc(c, out) == EOF) {
288                                         perror("putc");
289                                         fclose(out);
290                                         data_fd = NOTOK;
291                                         goto bad;
292                                 }
293                         }
294
295                         fclose(out);
296                         data_fd = NOTOK;
297                 } else {
298                         while ((cc = fread(buffer, sizeof *buffer,
299                                         sizeof buffer, fp)) > 0)
300                                 if (write_tcp_socket(data_fd, buffer, cc)
301                                                 != cc) {
302                                         perror("write_tcp_socket");
303                                         goto bad;
304                                 }
305
306                         close_tcp_socket(data_fd), data_fd = NOTOK;
307                 }
308         } else if (ascii) {
309                 int c;
310                 FILE *in;
311
312                 if (!(in = fdopen(data_fd, "r"))) {
313                         perror("fdopen");
314                         goto bad;
315                 }
316
317                 while ((c = getc(in)) != EOF) {
318                         if (c == '\r')
319                                 switch (c = getc(in)) {
320                                 case EOF:
321                                 case '\0':
322                                         c = '\r';
323                                         break;
324
325                                 case '\n':
326                                         break;
327
328                                 default:
329                                         putc('\r', fp);
330                                         break;
331                                 }
332
333                         if (putc(c, fp) == EOF) {
334                                 perror("putc");
335                                 fclose(in);
336                                 data_fd = NOTOK;
337                                 goto bad;
338                         }
339                 }
340
341                 fclose(in);
342                 data_fd = NOTOK;
343         } else {
344                 while ((cc = read_tcp_socket(data_fd, buffer,
345                                 sizeof buffer)) > 0)
346                         if (fwrite(buffer, sizeof *buffer, cc, fp) == 0) {
347                                 perror("fwrite");
348                                 goto bad;
349                         }
350                 if (cc < 0) {
351                         perror("read_tcp_socket");
352                         goto bad;
353                 }
354
355                 close_tcp_socket(data_fd), data_fd = NOTOK;
356         }
357
358         if (!istdio)
359                 fclose(fp);
360
361         v_noise = v_verbose;
362         return (getreply(1, 0) == COMPLETE ? OK : NOTOK);
363 }
364
365
366 #define UC(b) (((int) b) & 0xff)
367
368 static int
369 initconn(void)
370 {
371         int len;
372         register char *a, *p;
373         struct sockaddr_in in_socket;
374
375         if (getsockname(ftp_fd, (struct sockaddr *) &in_socket,
376                 (len = sizeof(in_socket), &len)) == NOTOK) {
377                 perror("getsockname");
378                 return NOTOK;
379         }
380         in_socket.sin_port = 0;
381         if ((data_fd = start_tcp_server(&in_socket, 1, 0, 0)) == NOTOK) {
382                 perror("start_tcp_server");
383                 return NOTOK;
384         }
385
386         if (getsockname(data_fd, (struct sockaddr *) &in_socket,
387                 (len = sizeof in_socket, &len)) == NOTOK) {
388                 perror("getsockname");
389                 return NOTOK;
390         }
391
392         a = (char *) &in_socket.sin_addr;
393         p = (char *) &in_socket.sin_port;
394
395         if (command(1, "PORT %d,%d,%d,%d,%d,%d",
396                         UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
397                         UC(p[0]), UC(p[1])) == COMPLETE)
398                 return OK;
399
400         return NOTOK;
401 }
402
403 static int
404 dataconn(void)
405 {
406         int fd;
407         struct sockaddr_in in_socket;
408
409         if ((fd = join_tcp_client(data_fd, &in_socket)) == NOTOK) {
410                 perror("join_tcp_client");
411                 return NOTOK;
412         }
413         close_tcp_socket(data_fd);
414         data_fd = fd;
415
416         return OK;
417 }
418
419
420 static int
421 command(int arg1, ...)
422 {
423         int val;
424         va_list ap;
425
426         va_start(ap, arg1);
427         val = vcommand(arg1, ap);
428         va_end(ap);
429
430         return val;
431 }
432
433 static int
434 vcommand(int complete, va_list ap)
435 {
436         int len;
437         char buffer[BUFSIZ];
438
439         if (ftp_fd == NOTOK)
440                 return NOTOK;
441
442         _asnprintf(buffer, sizeof(buffer), NULL, ap);
443         if (v_debug)
444                 fprintf(stderr, "<--- %s\n", buffer);
445
446         strcat(buffer, "\r\n");
447         len = strlen(buffer);
448
449         if (write_tcp_socket(ftp_fd, buffer, len) != len) {
450                 perror("write_tcp_socket");
451                 return NOTOK;
452         }
453
454         return(getreply(complete, !strcmp(buffer, "QUIT")));
455 }
456
457
458 static int
459 getreply(int complete, int expecteof)
460 {
461         for (;;) {
462                 register int code, dig, n;
463                 int continuation;
464                 register char *bp;
465                 char buffer[BUFSIZ];
466
467                 code = dig = n = continuation = 0;
468                 bp = buffer;
469
470                 for (;;) {
471                         unsigned char c;
472
473                         if (read_tcp_socket(ftp_fd, &c, 1) < 1) {
474                                 if (expecteof)
475                                         return OK;
476
477                                 perror("read_tcp_socket");
478                                 return DONE;
479                         }
480                         if (c == '\n')
481                                 break;
482                         *bp++ = c != '\r' ? c : '\0';
483
484                         dig++;
485                         if (dig < 4) {
486                                 if (isdigit(c))
487                                         code = code * 10 + (c - '0');
488                                 else if (isspace(c))
489                                         /* XXX: naughty FTP... */
490                                         continuation++;
491                         } else
492                                 if (dig == 4 && c == '-')
493                                         continuation++;
494                         if (n == 0)
495                                 n = c;
496                 }
497
498                 if (v_debug)
499                         fprintf(stderr, "---> %s\n", buffer);
500                 if (continuation)
501                         continue;
502
503                 n -= '0';
504
505                 if (v_noise) {
506                         fprintf(stdout, "%s\n", buffer);
507                         fflush(stdout);
508                         v_noise = 0;
509                 } else
510                         if ((complete == -1 && n != PRELIM)
511                                 || (complete == 0 && n != CONTINUE && n != COMPLETE)
512                                 || (complete == 1 && n != COMPLETE))
513                                 fprintf(stderr, "%s\n", buffer);
514
515                 return n;
516         }
517 }