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