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