Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / uip / ftpsbr.c
1 /* ftpsbr.c - simple FTP client library (why doesn't BSD have one?!?) */
2
3 #ifndef lint
4 static char ident[] = "@(#)$Id: ftpsbr.c,v 1.13 1993/08/25 17:25:27 jromine Exp $";
5 #endif
6
7 #include "../h/mh.h"
8 #include "../h/mhn.h"
9 #ifdef  FTP
10 #include <ctype.h>
11 #ifdef SVR4
12 #undef NULLVP          /* stdio.h */
13 #endif
14 #include <stdio.h>
15 #include <arpa/ftp.h>
16 #ifdef __STDC__
17 #include <stdarg.h>
18 #else
19 #include <varargs.h>
20 #endif
21
22 #ifdef __STDC__
23 static int      command(int arg1, ...);
24 #else
25 static int      command();
26 #endif
27
28 static int  ftp_quit(), ftp_read(), initconn(),
29             dataconn(), _command(), getreply();
30
31 /* \f   DATA */
32
33 #define v_debug         debugsw
34 #define v_verbose       verbosw
35
36
37 static  int     ftp_fd = NOTOK;
38 static  int     data_fd = NOTOK;
39
40 static  int     v_noise;
41
42 extern  int     v_debug;
43 extern  int     v_verbose;
44
45 /* \f */
46
47 #if     defined(SYS5) && defined(AUX)
48 #define u_short ushort
49 #define u_long  ulong
50 #endif
51
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <netdb.h>
56
57 #if     defined(BIND) && !defined(h_addr)
58 #define h_addr  h_addr_list[0]
59 #endif
60
61 #define inaddr_copy(hp,sin) \
62     bcopy ((hp) -> h_addr, (char *) &((sin) -> sin_addr), (hp) -> h_length)
63
64
65 struct hostent *gethostbystring ();
66
67 /* \f */
68
69 extern  int     errno;
70 #ifndef BSD44
71 extern  int     sys_nerr;
72 extern  char   *sys_errlist[];
73 #endif
74
75
76 #define start_tcp_client(sock,priv) \
77         socket (AF_INET, SOCK_STREAM, 0)
78
79 #define join_tcp_server(fd, sock) \
80         connect ((fd), (struct sockaddr *) (sock), sizeof *(sock))
81
82
83 /* ARGSUSED */
84
85 static int  start_tcp_server (sock, backlog, opt1, opt2)
86 struct sockaddr_in *sock;
87 int     backlog,
88         opt1,
89         opt2;
90 {
91     int     eindex,
92             sd;
93
94     if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == NOTOK)
95         return NOTOK;
96
97     if (bind (sd, (struct sockaddr *) sock, sizeof *sock) == NOTOK) {
98         eindex = errno;
99         (void) close (sd);
100         errno = eindex;
101     }
102     else
103         (void) listen (sd, backlog);
104
105     return sd;
106 }
107
108 static  int     __len__;
109 #define join_tcp_client(fd,sock) \
110         accept ((fd), (struct sockaddr *) (sock), \
111                 (__len__ = sizeof *(sock), &__len__))
112
113
114 #define read_tcp_socket         read
115 #define write_tcp_socket        write
116 #define close_tcp_socket        close
117
118 /* \f */
119
120 static void  _asprintf (bp, what, ap)   /* fmt, args, ... */
121 register char *bp;
122 char   *what;
123 va_list ap;
124 {
125     register int    eindex;
126     char   *fmt;
127
128     eindex = errno;
129
130     *bp = '\0';
131     fmt = va_arg (ap, char *);
132
133     if (fmt) {
134 #ifndef VSPRINTF
135         struct _iobuf iob;
136 #endif
137
138 #ifndef VSPRINTF
139 #ifdef  pyr
140         bzero ((char *) &iob, sizeof iob);
141         iob._file = _NFILE;
142 #endif
143         iob._flag = _IOWRT | _IOSTRG;
144 #if     !defined(vax) && !defined(pyr) && !defined(sequent)
145         iob._ptr = (unsigned char *) bp;
146 #else
147         iob._ptr = bp;
148 #endif
149         iob._cnt = BUFSIZ;
150         _doprnt (fmt, ap, &iob);
151         (void) putc ('\0', &iob);
152 #else
153         (void) vsprintf (bp, fmt, ap);
154 #endif
155         bp += strlen (bp);
156
157     }
158
159     if (what) {
160         if (*what) {
161             (void) sprintf (bp, " %s: ", what);
162             bp += strlen (bp);
163         }
164         if (0 < eindex && eindex < sys_nerr)
165             (void) strcpy (bp, sys_errlist[eindex]);
166         else
167             (void) sprintf (bp, "Error %d", eindex);
168         bp += strlen (bp);
169     }
170
171     errno = eindex;
172 }
173
174 /* \f */
175
176 int     ftp_get (host, user, password, cwd, remote, local, ascii, stayopen)
177 char   *host,
178        *user,
179        *password,
180        *cwd,
181        *remote,
182        *local;
183 int     ascii,
184         stayopen;
185 {
186     return ftp_trans (host, user, password, cwd, remote, local, "RETR", ascii,
187                       stayopen);
188 }
189
190 /* \f */
191
192 int     ftp_trans (host, user, password, cwd, remote, local, cmd, ascii,
193                    stayopen)
194 char   *host,
195        *user,
196        *password,
197        *cwd,
198        *remote,
199        *local,
200        *cmd;
201 int     ascii,
202         stayopen;
203 {
204     int     result;
205
206     if (stayopen <= 0) {
207         result = ftp_quit ();
208         if (host == NULL)
209             return result;
210     }
211
212     if (ftp_fd == NOTOK) {
213         struct sockaddr_in in_socket;
214         register struct hostent *hp;
215         register struct servent *sp;
216
217         if ((sp = getservbyname ("ftp", "tcp")) == NULL) {
218             fprintf (stderr, "tcp/ftp: unknown service");
219             return NOTOK;
220         }
221         if ((hp = gethostbystring (host)) == NULL) {
222             fprintf (stderr, "%s: unknown host\n", host);
223             return NOTOK;
224         }
225         in_socket.sin_family = hp -> h_addrtype;
226         inaddr_copy (hp, &in_socket);
227         in_socket.sin_port = sp -> s_port;
228
229         if ((ftp_fd = start_tcp_client ((struct sockaddr_in *) NULL, 0))
230                 == NOTOK) {
231             perror (host);
232             return NOTOK;
233         }
234         if (join_tcp_server (ftp_fd, &in_socket) == NOTOK) {
235             perror (host);
236             (void) close_tcp_socket (ftp_fd), ftp_fd = NOTOK;
237             return NOTOK;
238         }
239         (void) getreply (1, 0);
240
241         if (v_verbose) {
242             fprintf (stdout, "Connected to %s\n", host);
243             (void) fflush (stdout);
244         }
245
246         if (user) {
247             if ((result = command (0, "USER %s", user)) == CONTINUE)
248                 result = command (1, "PASS %s", password);
249             if (result != COMPLETE) {
250                 result = NOTOK;
251                 goto out;
252             }
253         }
254
255         if (remote == NULL)
256             return OK;
257     }
258
259     if (cwd && ((result = command (0, "CWD %s", cwd)) != COMPLETE
260                     && result != CONTINUE)) {
261         result = NOTOK;
262         goto out;
263     }
264
265     if (command (1, ascii ? "TYPE A" : "TYPE I") != COMPLETE) {
266         result = NOTOK;
267         goto out;
268     }
269
270     result = ftp_read (remote, local, cmd, ascii);
271
272 out: ;
273     if (result != OK || !stayopen)
274         (void) ftp_quit ();
275
276     return result;
277 }
278
279 /* \f */
280
281 static int  ftp_quit ()
282 {
283     int     n;
284
285     if (ftp_fd == NOTOK)
286         return OK;
287
288     n = command (1, "QUIT");
289
290     (void) close_tcp_socket (ftp_fd), ftp_fd = NOTOK;
291
292     return (n == 0 || n == COMPLETE ? OK : NOTOK);
293 }
294
295 /* \f */
296
297 static int  ftp_read (remote, local, cmd, ascii)
298 char    *remote,
299         *local,
300         *cmd;
301 int     ascii;
302 {
303     int     istdio = 0,
304             istore;
305     register int    cc;
306     int     expectingreply = 0;
307     char    buffer[BUFSIZ];
308     FILE   *fp = NULL;
309
310     if (initconn () == NOTOK)
311         goto bad;
312
313     v_noise = v_verbose;
314     if (command (-1, *remote ? "%s %s" : "%s", cmd, remote) != PRELIM)
315         goto bad;
316
317     expectingreply++;
318     if (dataconn () == NOTOK) {
319 bad: ;
320         if (fp && !istdio)
321             (void) fclose (fp);
322         if (data_fd != NOTOK)
323             (void) close_tcp_socket (data_fd), data_fd = NOTOK;
324         if (expectingreply)
325             (void) getreply (-2, 0);
326
327         return NOTOK;
328     }
329
330     istore = !strcmp (cmd, "STOR");
331
332     if (istdio = !strcmp (local, "-"))
333         fp = istore ? stdin : stdout;
334     else
335         if ((fp = fopen (local, istore ? "r" : "w")) == NULL) {
336             perror (local);
337             goto bad;
338         }
339
340     if (istore) {
341         if (ascii) {
342             int     c;
343             FILE   *out;
344
345             if (!(out = fdopen (data_fd, "w"))) {
346                 perror ("fdopen");
347                 goto bad;
348             }
349
350             while ((c = getc (fp)) != EOF) {
351                 if (c == '\n')
352                     (void) putc ('\r', out);
353                 if (putc (c, out) == EOF) {
354                     perror ("putc");
355                     (void) fclose (out);
356                     data_fd = NOTOK;
357                     goto bad;
358                 }
359             }
360
361             (void) fclose (out);
362             data_fd = NOTOK;
363         }
364         else {
365             while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, fp)) >0)
366                 if (write_tcp_socket (data_fd, buffer, cc) != cc) {
367                     perror ("write_tcp_socket");
368                     goto bad;
369                 }
370
371             (void) close_tcp_socket (data_fd), data_fd = NOTOK;
372         }
373     }
374     else {
375         if (ascii) {
376             int     c;
377             FILE   *in;
378
379             if (!(in = fdopen (data_fd, "r"))) {
380                 perror ("fdopen");
381                 goto bad;
382             }
383
384             while ((c = getc (in)) != EOF) {
385                 if (c == '\r')
386                     switch (c = getc (in)) {
387                         case EOF:
388                         case '\0':
389                             c = '\r';
390                             break;
391
392                         case '\n':
393                             break;
394
395                         default:
396                             (void) putc ('\r', fp);
397                             break;
398                         }
399
400                 if (putc (c, fp) == EOF) {
401                     perror ("putc");
402                     (void) fclose (in);
403                     data_fd = NOTOK;
404                     goto bad;
405                 }
406             }
407
408             (void) fclose (in);
409             data_fd = NOTOK;
410         }
411         else {
412             while ((cc = read_tcp_socket (data_fd, buffer, sizeof buffer)) > 0)
413                 if (fwrite (buffer, sizeof *buffer, cc, fp) == 0) {
414                     perror ("fwrite");
415                     goto bad;
416                 }
417             if (cc < 0) {
418                 perror ("read_tcp_socket");
419                 goto bad;
420             }
421
422             (void) close_tcp_socket (data_fd), data_fd = NOTOK;
423         }
424     }
425
426     if (!istdio)
427         (void) fclose (fp);
428
429     v_noise = v_verbose;
430     return (getreply (1, 0) == COMPLETE ? OK : NOTOK);
431 }
432
433 /* \f */
434
435 static int  initconn ()
436 {
437     int     len;
438     register char  *a,
439                    *p;
440     struct sockaddr_in in_socket;
441
442     if (getsockname (ftp_fd, (struct sockaddr *) &in_socket,
443                      (len = sizeof in_socket, &len)) == NOTOK) {
444         perror ("getsockname");
445         return NOTOK;
446     }
447     in_socket.sin_port = 0;
448     if ((data_fd = start_tcp_server (&in_socket, 1, 0, 0)) == NOTOK) {
449         perror ("start_tcp_server");
450         return NOTOK;
451     }
452
453     if (getsockname (data_fd, (struct sockaddr *) &in_socket,
454                      (len = sizeof in_socket, &len)) == NOTOK) {
455         perror ("getsockname");
456         return NOTOK;
457     }
458
459     a = (char *) &in_socket.sin_addr;
460     p = (char *) &in_socket.sin_port;
461
462 #define UC(b)   (((int) b) & 0xff)
463     if (command (1, "PORT %d,%d,%d,%d,%d,%d",
464                       UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
465                       UC(p[0]), UC(p[1])) == COMPLETE)
466         return OK;
467
468     return NOTOK;
469 }
470
471 /* \f */
472
473 static int  dataconn ()
474 {
475     int     fd;
476     struct sockaddr_in in_socket;
477     
478     if ((fd = join_tcp_client (data_fd, &in_socket)) == NOTOK) {
479         perror ("join_tcp_client");
480         return NOTOK;
481     }
482     (void) close_tcp_socket (data_fd);
483     data_fd = fd;
484
485     return OK;
486 }
487
488 /* \f */
489
490 #ifndef lint
491 #ifdef __STDC__
492 static int  command (int arg1, ...)
493 {
494     int     val;
495     va_list ap;
496
497     va_start (ap, arg1);
498
499     val = _command (arg1, ap);
500
501     va_end (ap);
502
503     return val;
504 }
505 #else
506 static int  command (va_alist)
507 va_dcl
508 {
509     int     val;
510     va_list ap;
511
512     va_start (ap);
513
514     val = va_arg (ap, int);
515     val = _command (val, ap);
516
517     va_end (ap);
518
519     return val;
520 }
521 #endif
522
523 static int  _command (complete, ap)
524 int complete;
525 va_list ap;
526 {
527     int     len;
528     char    buffer[BUFSIZ];
529
530     if (ftp_fd == NOTOK)
531         return NOTOK;
532
533     _asprintf (buffer, NULLCP, ap);
534     if (v_debug)
535         fprintf (stderr, "<--- %s\n", buffer);
536
537     (void) strcat (buffer, "\r\n");
538     len = strlen (buffer);
539
540     if (write_tcp_socket (ftp_fd, buffer, len) != len) {
541         perror ("write_tcp_socket");
542         return NOTOK;
543     }
544
545     return (getreply (complete, !strcmp (buffer, "QUIT")));
546 }
547 #else
548 /* VARARGS2 */
549
550 static int  command (complete, fmt)
551 int     complete;
552 char   *fmt;
553 {
554     return command (complete, fmt);
555 }
556 #endif
557
558 /* \f */
559
560 static int  getreply (complete, expecteof)
561 int     complete,
562         expecteof;
563 {
564     for (;;) {
565         register int code,
566                      dig,
567                      n;
568         int     continuation;
569         register char *bp;
570         char    buffer[BUFSIZ];
571
572         code = dig = n = continuation = 0;
573         bp = buffer;
574
575         for (;;) {
576             char    c;
577
578             if (read_tcp_socket (ftp_fd, &c, 1) < 1) {
579                 if (expecteof)
580                     return OK;
581
582                 perror ("read_tcp_socket");
583                 return DONE;
584             }
585             if (c == '\n')
586                 break;
587             *bp++ = c != '\r' ? c : '\0';
588
589             dig++;
590             if (dig < 4) {
591                 if (isdigit(c))
592                     code = code * 10 + (c - '0');
593                 else                            /* XXX: naughty FTP... */
594                     if (isspace (c))
595                         continuation++;
596             }
597             else
598                 if (dig == 4 && c == '-')
599                     continuation++;
600             if (n == 0)
601                 n = c;
602         }
603
604         if (v_debug)
605             fprintf (stderr, "---> %s\n", buffer);
606         if (continuation)
607             continue;
608
609         n -= '0';
610
611         if (v_noise) {
612             fprintf (stdout, "%s\n", buffer);
613             (void) fflush (stdout);
614             v_noise = 0;
615         }
616         else
617             if ((complete == -1 && n != PRELIM)
618                     || (complete == 0 && n != CONTINUE && n != COMPLETE)
619                     || (complete == 1 && n != COMPLETE))
620                 fprintf (stderr, "%s\n", buffer);
621
622         return n;
623     }
624 }
625 #endif