home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume35 / ncftp / part02 / ftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-25  |  29.2 KB  |  1,380 lines

  1. /* ftp.c */
  2.  
  3. #include "sys.h"
  4. #include <sys/types.h>
  5. #include <sys/param.h>
  6. #include <setjmp.h>
  7. #include <sys/stat.h>
  8. #include <sys/socket.h>
  9. #include <sys/time.h>
  10. #include <sys/file.h>
  11.  
  12. #ifdef SYSLOG
  13. #    include <syslog.h>
  14. #endif
  15.  
  16. /* You may need this for declarations of fd_set, etc. */
  17. #ifdef SYSSELECTH
  18. #   include <sys/select.h>
  19. #else
  20. extern int select (int, void *, void *, void *, struct timeval *);
  21. #endif
  22.  
  23. #ifndef NO_UNISTDH        /* for prototypes only. */
  24. #    include <unistd.h>
  25. #endif
  26.  
  27. #include <netinet/in.h>
  28. #include <arpa/ftp.h>
  29. #include <arpa/inet.h>
  30. #include <arpa/telnet.h>
  31. #include <string.h>
  32. #include <signal.h>
  33. #include <errno.h>
  34. #include <netdb.h>
  35. #include <fcntl.h>
  36. #include <pwd.h>
  37. #include <ctype.h>
  38. #include "ftpdefs.h"
  39. #include "defaults.h"
  40. #include "ftp.h"
  41. #include "cmds.h"
  42. #include "main.h"
  43. #include "ftprc.h"
  44. #include "getpass.h"
  45. #include "copyright.h"
  46.  
  47. /* ftp.c globals */
  48. struct                sockaddr_in hisctladdr;
  49. struct                sockaddr_in data_addr;
  50. int                    data = -1;
  51. int                    abrtflag = 0;
  52. int                    connected;            /* connected to server */
  53. struct sockaddr_in    myctladdr;
  54. FILE                *cin = NULL, *cout = NULL;
  55. char                *reply_string = NULL;
  56. jmp_buf                sendabort, recvabort;
  57. int                    progress_meter = 1;
  58. int                    cur_progress_meter;
  59. int                    sendport = -1;        /* use PORT cmd for each data connection */
  60. int                    code;                /* return/reply code for ftp command */
  61. string                hostname;            /* name of host connected to */
  62. int                 cpend;                /* flag: if != 0, then pending server reply */
  63. char                *xferbuf;            /* buffer for local and remote I/O */
  64. size_t                xferbufsize;        /* size in bytes, of the transfer buffer. */
  65. long                next_report;
  66. long                bytes;
  67. long                now_sec;
  68. long                file_size;
  69. struct timeval        start, stop;
  70.  
  71. /* ftp.c externs */
  72. extern FILE                    *logf;
  73. extern string                hostname, cwd, anon_password;
  74. extern int                    verbose, debug, macnum, margc;
  75. extern int                    curtype, creating;
  76. extern int                    options, activemcmd, paging;
  77. extern int                    ansi_escapes;
  78. extern char                    *line, *margv[];
  79. extern char                    *tcap_normal, *tcap_boldface;
  80. extern char                    *tcap_underline, *tcap_reverse;
  81. extern struct userinfo        uinfo;
  82. extern struct macel            macros[];
  83.  
  84. #ifdef REDIR
  85. extern struct lslist        *lshead, *lstail;
  86. extern int                    is_ls;
  87. #endif
  88.  
  89. #define UPDATE_100 2
  90.  
  91.  
  92. int hookup(char *host, int port)
  93. {
  94.     register struct hostent *hp = 0;
  95.     int s, len, hErr = -1;
  96.  
  97.     bzero((char *)&hisctladdr, sizeof (hisctladdr));
  98. #ifdef BAD_INETADDR
  99.     hisctladdr.sin_addr = inet_addr(host);
  100. #else
  101.      hisctladdr.sin_addr.s_addr = inet_addr(host);
  102. #endif
  103.     if (hisctladdr.sin_addr.s_addr != -1) {
  104.         hisctladdr.sin_family = AF_INET;
  105.         (void) Strncpy(hostname, host);
  106.     } else {
  107.         hp = gethostbyname(host);
  108.         if (hp == NULL) {
  109. #ifdef HERROR
  110.             extern int h_errno;
  111.             if (h_errno == HOST_NOT_FOUND)
  112.                 (void) printf("%s: unknown host\n", host);
  113.             else (void) fprintf(stderr, "%s: gethostbyname herror (%d):  ",
  114.                 host, h_errno);
  115.             herror(NULL);
  116. #else
  117.             (void) printf("%s: unknown host\n", host);
  118. #endif
  119.             goto done;
  120.         }
  121.         hisctladdr.sin_family = hp->h_addrtype;
  122.         bcopy(hp->h_addr_list[0],
  123.             (caddr_t)&hisctladdr.sin_addr, hp->h_length);
  124.         (void) Strncpy(hostname, hp->h_name);
  125.     }
  126.     s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
  127.     if (s < 0) {
  128.         Perror("socket");
  129.         goto done;
  130.     }
  131.     hisctladdr.sin_port = port;
  132.     while (connect(s, (struct sockaddr *) &hisctladdr, (int) sizeof (hisctladdr)) < 0) {
  133.         if (hp && hp->h_addr_list[1]) {
  134.             int oerrno = errno;
  135.  
  136.             (void) fprintf(stderr, "NcFTP: connect error (%d) to address %s: ",
  137.                 errno, inet_ntoa(hisctladdr.sin_addr));
  138.             errno = oerrno;
  139.             Perror((char *) 0);
  140.             hp->h_addr_list++;
  141.             bcopy(hp->h_addr_list[0],
  142.                  (caddr_t)&hisctladdr.sin_addr, hp->h_length);
  143.             (void) fprintf(stdout, "Trying %s...\n",
  144.                 inet_ntoa(hisctladdr.sin_addr));
  145.             (void) close(s);
  146.             s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
  147.             if (s < 0) {
  148.                 Perror("socket");
  149.                 goto done;
  150.             }
  151.             continue;
  152.         }
  153.         Perror("connect");
  154.         switch (errno) {
  155.             case ENETDOWN:
  156.             case ENETUNREACH:
  157.             case ECONNABORTED:
  158.             case ETIMEDOUT:
  159.             case ECONNREFUSED:
  160.             case EHOSTDOWN:
  161.                 hErr = -2;    /* we can re-try later. */
  162.         }
  163.         goto bad;
  164.     }
  165.     len = sizeof (myctladdr);
  166.     if (getsockname(s, (char *)&myctladdr, &len) < 0) {
  167.         Perror("getsockname");
  168.         goto bad;
  169.     }
  170.     cin = fdopen(s, "r");
  171.     cout = fdopen(s, "w");
  172.     if (cin == NULL || cout == NULL) {
  173.         (void) fprintf(stderr, "ftp: fdopen failed.\n");
  174.         close_streams(0);
  175.         goto bad;
  176.     }
  177.     if (IS_VVERBOSE)
  178.         (void) printf("Connected to %s.\n", hostname);
  179.     if (getreply(0) > 2) {     /* read startup message from server */
  180.         close_streams(0);
  181.         goto bad;
  182.     }
  183. #ifdef SO_OOBINLINE
  184.     {
  185.     int on = 1;
  186.  
  187.     if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on))
  188.         < 0 && debug) {
  189.             Perror("setsockopt");
  190.         }
  191.     }
  192. #endif /* SO_OOBINLINE */
  193.  
  194.     hErr = 0;
  195.     goto done;
  196.  
  197. bad:
  198.     (void) close(s);
  199. done:
  200.     code = hErr;
  201.     return (hErr);
  202. }    /* hookup */
  203.  
  204.  
  205.  
  206.  
  207. int login(char *host, int openmode, int ignore_rc)
  208. {
  209.     string                tmp, str;
  210.     char                *username, *pass, *acct;
  211.     int                    n, aflag = 0, prompted_login;
  212.     int                    user_in_rc, tmpverbose;
  213.  
  214.     username = pass = acct = NULL;
  215.     if (!ignore_rc) {
  216.         if ((ruserpass2(host, &username, &pass, &acct)) < 0) {
  217.             /*NOTREACHED */
  218.             code = -1;
  219.             return(0);
  220.         }
  221.     }
  222.  
  223.     user_in_rc = 1;
  224.     prompted_login = 0;
  225.     if (username == NULL) {
  226.         user_in_rc = 0;
  227.         prompted_login = 1;
  228.     } 
  229.  
  230.     if (!user_in_rc) {
  231.         /* There was no username in the rc. */
  232.         if (openmode == OPEN_A) {
  233.             username = "anonymous";
  234.             prompted_login = 0;
  235.         } else { /* openmode == OPEN_U */
  236.             /* Prompt for a username. */
  237.             (void) printf("Name (%s:%s): ", host, uinfo.username);
  238.             (void) FGets(tmp, stdin);
  239.             tmp[strlen(tmp) - 1] = '\0';
  240.             /*
  241.              *    User can hit return if he wants to enter his username
  242.              *    automatically.
  243.              */
  244.             if (*tmp == '\0')
  245.                 username = uinfo.username;
  246.             else
  247.                 username = tmp;
  248.         }
  249.     }
  250.     (void) sprintf(str, "USER %s", username);
  251.     n = command(str);
  252.     if (n == CONTINUE) {
  253.         if (pass == NULL) {
  254.             if (strcmp("anonymous", username) == 0)
  255.                 pass = anon_password;
  256.             else
  257.                 pass = Getpass("Password:");
  258.         }
  259.         (void) sprintf(str, "PASS %s", pass);
  260.         n = command(str);
  261.     }
  262.     if (n == CONTINUE) {
  263.         aflag++;
  264.         acct = Getpass("Account:");
  265.         (void) sprintf(str, "ACCT %s", acct);
  266.         n = command(str);
  267.     }
  268.     if (n != COMPLETE) {
  269. noLogin:
  270.         (void) fprintf(stderr, "Login failed.\n");
  271.         return (0);
  272.     }
  273. #ifdef SYSLOG
  274.     syslog (LOG_INFO, "%s connected to %s as %s.",
  275.         uinfo.username, host, username);
  276. #endif
  277.  
  278.     if (!aflag && acct != NULL) {
  279.         (void) sprintf(str, "ACCT %s", acct);
  280.         (void) command(str);
  281.     }
  282.  
  283.     /* See if remote host dropped connection. */
  284.     tmpverbose = verbose;
  285.     verbose = V_QUIET;
  286.     n = command("NOOP");
  287.     verbose = tmpverbose;
  288.     if (n == 4)
  289.         goto noLogin;
  290.  
  291.     if (NOT_VQUIET && !prompted_login)
  292.         (void) printf("Logged into %s.\n", host);
  293.     if (!ignore_rc)
  294.         for (n = 0; n < macnum; ++n) {
  295.             if (!strcmp("init", macros[n].mac_name)) {
  296.                 (void) strcpy(line, "$init");
  297.                 makeargv();
  298.                 domacro(margc, margv);
  299.                 break;
  300.             }
  301.         }
  302.     return (1);
  303. }    /* login */
  304.  
  305.  
  306.  
  307. /*ARGSUSED*/
  308. void cmdabort(int unused)
  309. {
  310.     (void) printf("\n");
  311.     (void) fflush(stdout);
  312.     abrtflag++;
  313. }    /* cmdabort */
  314.  
  315.  
  316.  
  317.  
  318. command(char *cmd)
  319. {
  320.     int r;
  321.     void (*oldintr)(int);
  322.     string str;
  323.  
  324.     abrtflag = 0;
  325.     if (debug) {
  326.         (void) printf("---> \"%s\" (length %lu)\n", cmd, strlen(cmd));
  327.     }
  328.     if (cout == NULL) {
  329.         (void) sprintf(str, "%s: No control connection for command", cmd);
  330.         Perror(str);
  331.         code = -1;
  332.         return (0);
  333.     }
  334.     oldintr = signal(SIGINT, /* cmdabort */ SIG_IGN);
  335. #ifndef SCO324
  336.     if (cout != NULL)
  337.         (void) fprintf(cout, "%s\r\n", cmd);
  338. #else
  339.     {
  340.         /*
  341.          * The fprintf() above gives me a core-dump in memcpy()...
  342.          * This does the trick though...
  343.          */
  344.  
  345.         char *p = cmd;
  346.         while (*p)
  347.             fputc(*p++, cout);
  348.         fputc('\r', cout);
  349.         fputc('\n', cout);
  350.     }
  351. #endif /* !SCO324 */
  352.     (void) fflush(cout);
  353.     cpend = 1;
  354.     r = getreply(strcmp(cmd, "QUIT") == 0);
  355.     if (abrtflag && oldintr != SIG_IGN && oldintr != NULL)
  356.         (*oldintr)(0);
  357.     (void) signal(SIGINT, oldintr);
  358.     return(r);
  359. }    /* command */
  360.  
  361.  
  362.  
  363.  
  364. getreply(int expecteof)
  365. {
  366.     register int c, n;
  367.     int dig;
  368.     char *cp, *end, *dp;
  369.     int thiscode, originalcode = 0, continuation = 0;
  370.     void (*oldintr)(int);
  371.  
  372.     if (cin == NULL)
  373.         return (-1);
  374.     oldintr = signal(SIGINT, /* cmdabort */ SIG_IGN);
  375.     end = reply_string + RECEIVEDLINELEN - 2;
  376.     for (;;) {
  377.         dig = n = code = 0;
  378.         cp = reply_string;
  379.         for (;;) {
  380.             c = getc(cin);
  381.             if (c == IAC) {     /* handle telnet commands */
  382.                 switch (c = getc(cin)) {
  383.                 case WILL:
  384.                 case WONT:
  385.                     c = getc(cin);
  386.                     (void) fprintf(cout, "%c%c%c",IAC,DONT,c);
  387.                     (void) fflush(cout);
  388.                     break;
  389.                 case DO:
  390.                 case DONT:
  391.                     c = getc(cin);
  392.                     (void) fprintf(cout, "%c%c%c",IAC,WONT,c);
  393.                     (void) fflush(cout);
  394.                     break;
  395.                 default:
  396.                     break;
  397.                 }
  398.                 continue;
  399.             }
  400.             dig++;
  401.             if (c == EOF) {
  402.                 if (expecteof) {
  403.                     (void) signal(SIGINT,oldintr);
  404.                     code = 221;
  405.                     return (0);
  406.                 }
  407.                 lostpeer(0);
  408.                 if (NOT_VQUIET) {
  409.                     (void) printf("421 Service not available, remote server has closed connection\n");
  410.                     (void) fflush(stdout);
  411.                 }
  412.                 code = 421;
  413.                 return(4);
  414.             }
  415.             if (cp < end && c != '\r')
  416.                 *cp++ = c;
  417.  
  418.             if (c == '\n')
  419.                 break;
  420.             if (dig < 4 && isdigit(c))
  421.                 code = thiscode = code * 10 + (c - '0');
  422.             else if (dig == 4 && c == '-') {
  423.                 if (continuation)
  424.                     code = 0;
  425.                 continuation++;
  426.             }
  427.             if (n == 0)
  428.                 n = c;
  429.         }    /* end for(;;) #2 */
  430.         
  431.         *cp = '\0';
  432.         switch (verbose) {
  433.             case V_QUIET:
  434.                 /* Don't print anything. */
  435.                 break;
  436.             case V_ERRS:
  437.                 if (n == '5') {
  438.                     dp = reply_string;
  439.                     goto stripCode;
  440.                 }
  441.                 break;    
  442.             case V_IMPLICITCD:
  443.             case V_TERSE:
  444.                 dp = NULL;
  445.                 if (n == '5' && verbose == V_TERSE)
  446.                     dp = reply_string;
  447.                 else {
  448.                     switch (thiscode) {
  449.                         case 230:
  450.                         case 214:
  451.                         case 332:
  452.                             dp = reply_string;
  453.                             break;
  454.                         case 220:
  455.                             /*
  456.                              * Skip the foo FTP server ready line.
  457.                              */
  458.                             if (strstr(reply_string, "ready.") == NULL)
  459.                                 dp = reply_string;
  460.                             break;
  461.                         case 250:
  462.                             /*
  463.                              * Print 250 lines if they aren't
  464.                              * "250 CWD command successful."
  465.                              */
  466.                             if (strncmp(reply_string + 4, "CWD ", (size_t) 4))
  467.                                 dp = reply_string;
  468.                     }
  469.                 }
  470.                 if (dp == NULL) break;            
  471. stripCode:
  472.                 /* Try to strip out the code numbers, etc. */
  473.                 if (isdigit(*dp++) && isdigit(*dp++) && isdigit(*dp++)) {
  474.                     if (*dp == ' ' || *dp == '-') {
  475.                         dp++;
  476.                         if (*dp == ' ') dp++;
  477.                     } else dp = reply_string;            
  478.                 } else {
  479.                     int spaces;
  480.                     dp = reply_string;
  481.                     for (spaces = 0; spaces < 4; ++spaces)
  482.                         if (dp[spaces] != ' ')
  483.                             break;
  484.                     if (spaces == 4)
  485.                         dp += spaces;
  486.                 }                    
  487.                 goto printLine;
  488.             case V_VERBOSE:
  489.                 dp = reply_string;
  490. printLine:        (void) fputs(dp, stdout);
  491.         }    /* end switch */
  492.  
  493.         if (continuation && code != originalcode) {
  494.             if (originalcode == 0)
  495.                 originalcode = code;
  496.             continue;
  497.         }
  498.         if (n != '1')
  499.             cpend = 0;
  500.         (void) signal(SIGINT,oldintr);
  501.         if (code == 421 || originalcode == 421)
  502.             lostpeer(0);
  503.         if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN && oldintr)
  504.             (*oldintr)(0);
  505.         return (n - '0');
  506.     }    /* end for(;;) #1 */
  507. }    /* getreply */
  508.  
  509.  
  510.  
  511.  
  512. static int empty(struct fd_set *mask, int sec)
  513. {
  514.     struct timeval t;
  515.  
  516.     t.tv_sec = (long) sec;
  517.     t.tv_usec = 0;
  518.  
  519.     return(select(32, mask, NULL, NULL, &t));
  520. }    /* empty */
  521.  
  522.  
  523.  
  524.  
  525. static void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0)
  526. {
  527.     tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
  528.     tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
  529.     if (tdiff->tv_usec < 0)
  530.         tdiff->tv_sec--, tdiff->tv_usec += 1000000;
  531. }    /* tvsub */
  532.  
  533.  
  534.  
  535. static int barlen;
  536.  
  537. int start_progress(int sending, char *local)
  538. {
  539.     long s;
  540.     str32 spec;
  541.  
  542.     cur_progress_meter = progress_meter;
  543.     if ((cur_progress_meter > pr_kbytes) || (cur_progress_meter < 0))
  544.         cur_progress_meter = pr_percent;
  545.     if ((file_size <= 0) && ((cur_progress_meter == pr_percent) || (cur_progress_meter == pr_philbar)))
  546.         cur_progress_meter = pr_kbytes;
  547.     if (!ansi_escapes && (cur_progress_meter == pr_philbar))
  548.         cur_progress_meter = pr_none;
  549.  
  550.     (void) gettimeofday(&start, (struct timezone *)0);
  551.     now_sec = start.tv_sec;
  552.  
  553.     switch (cur_progress_meter) {
  554.         case pr_none:
  555.             break;
  556.         case pr_percent:
  557.             (void) printf("%s:     ", local);
  558.             goto zz;
  559.         case pr_kbytes:
  560.             (void) printf("%s:       ", local);
  561.             goto zz;
  562.         case pr_philbar:
  563.             printf("%s%s file: %s %s\n", 
  564.                 tcap_boldface,
  565.                 sending ? "Sending" : "Receiving",
  566.                 local,
  567.                 tcap_normal
  568.             );
  569.             barlen = 64;
  570.             for (s = file_size; s > 0; s /= 10L) barlen--;
  571.             (void) sprintf(spec, "      0 %%%ds %%ld bytes.\r", barlen);
  572.             printf(spec, " ", file_size);
  573.         zz:
  574.             (void) fflush(stdout);
  575.             echo(stdin, 0);
  576.     }    /* end switch */
  577.     return (cur_progress_meter);
  578. }    /* start_progress */
  579.  
  580.  
  581.  
  582.  
  583. void progress_report(int finish_up)
  584. {
  585.     int i;
  586.     int size;
  587.     str32 spec;
  588.  
  589.     next_report += xferbufsize;
  590.     (void) gettimeofday(&stop, (struct timezone *)0);
  591.     if ((stop.tv_sec > now_sec) || finish_up) {
  592.         switch (cur_progress_meter) {
  593.             case pr_none:
  594.                 break;
  595.             case pr_percent:
  596.                 (void) printf("\b\b\b\b%3ld%%", 100L * bytes / file_size);
  597.                 (void) fflush(stdout);
  598.                 break;
  599.             case pr_philbar:
  600.                 size = (int) ((float)barlen * ((float) bytes/file_size));
  601.                 (void) sprintf(spec, "%%3ld%%%%  0 %%s%%%ds%%s\r", size);
  602.                 (void) printf(
  603.                     spec,
  604.                     100L * bytes / file_size,
  605.                     tcap_reverse,
  606.                     " ",
  607.                     tcap_normal
  608.                 );
  609.                 (void) fflush(stdout);
  610.                 break;
  611.             case pr_kbytes:
  612.                 if ((bytes / 1024) > 0) {
  613.                     (void) printf("\b\b\b\b\b\b%5ldK", bytes / 1024);
  614.                     (void) fflush(stdout);
  615.                 }
  616.         }    /* end switch */
  617.         now_sec = stop.tv_sec;
  618.     }    /* end if we updated */
  619. }    /* progress_report */
  620.  
  621.  
  622.  
  623.  
  624. void end_progress(char *direction, char *local, char *remote)
  625. {
  626.     struct timeval            td;
  627.     float                    s, bs;
  628.     char                    *cp, *bsstr;
  629.     string                    str;
  630.  
  631.     if (bytes <= 0)
  632.         return;
  633.     progress_report(1);        /* tell progress proc to cleanup. */
  634.  
  635.     tvsub(&td, &stop, &start);
  636.     s = td.tv_sec + (td.tv_usec / 1000000.0);
  637.     if (s != 0.0)
  638.         bs = bytes / s;
  639.     if (bs > 1024.0) {
  640.         bs /= 1024.0;
  641.         bsstr = "K/s.\n";
  642.     } else
  643.         bsstr = "Bytes/s.\n";
  644.  
  645.     if (NOT_VQUIET) switch(cur_progress_meter) {
  646.         case pr_none:
  647.             (void) printf("%s: %ld bytes %s in %.2f seconds, %.2f %s", local, bytes, direction, s, bs, bsstr);
  648.             break;
  649.         case pr_kbytes:
  650.         case pr_percent:
  651.             (void) printf("%s%ld bytes %s in %.2f seconds, %.2f %s",
  652.             cur_progress_meter == pr_kbytes ? "\b\b\b\b\b\b" : "\b\b\b\b",
  653.             bytes, direction, s, bs, bsstr);
  654.             echo(stdin, 1);
  655.             break;
  656.         case pr_philbar:
  657.             printf("\n");
  658.             echo(stdin, 1);
  659.             break;
  660.     }
  661.     
  662.     /* Save transfers to the logfile. */
  663.     if (logf != NULL) {
  664.         /* if a simple path is given, try to log the full path */
  665.         if (rindex(remote, '/') == NULL && cwd != NULL) {
  666.             (void) sprintf(str, "%s/%s", cwd, remote);
  667.              cp = str;
  668.         } else
  669.             cp = remote;
  670.         (void) fprintf(logf, "\t-> \"%s\" %s, %.2f %s", cp, direction, bs, bsstr);
  671.     } 
  672. #ifdef SYSLOG
  673.     if (direction[0] == 'r')
  674.         syslog (LOG_INFO, "%s %s %s as %s from %s (%ld bytes).",
  675.             uinfo.username, direction, remote, local, hostname, bytes);
  676.     else
  677.         syslog (LOG_INFO, "%s %s %s as %s to %s (%ld bytes).",
  678.             uinfo.username, direction, local, remote, hostname, bytes);
  679. #endif
  680. }   /* end_progress */
  681.  
  682.  
  683.  
  684. void close_file(FILE **fin, int filetype)
  685. {
  686.     if (*fin != NULL) {
  687.         if (filetype == IS_FILE) {
  688.             (void) fclose(*fin);
  689.             *fin = NULL;
  690.         } else if (filetype == IS_PIPE) {
  691.             (void) pclose(*fin);
  692.             *fin = NULL;
  693.         }
  694.     }
  695. }    /* close_file */
  696.  
  697.  
  698.  
  699.  
  700. /*ARGSUSED*/
  701. void abortsend(int unused)
  702. {
  703.     activemcmd = 0;
  704.     abrtflag = 0;
  705.     (void) printf("\nSend aborted.\n");
  706.     (void) fflush(stdout);
  707.     longjmp(sendabort, 1);
  708. }    /* abortsend */
  709.  
  710.  
  711.  
  712. void sendrequest(char *cmd, char *local, char *remote)
  713. {
  714.     FILE                    *fin, *dout = NULL;
  715.     void                    (*oldintr)(int), (*oldintp)(int);
  716.     string                    str;
  717.     register int            c, d;
  718.     struct stat                st;
  719.     int                        filetype;
  720.     int                        do_reports = 0;
  721.     char                    *mode;
  722.     register char            *bufp;
  723.  
  724.     oldintr = NULL;
  725.     oldintp = NULL;
  726.     mode = "w";
  727.     bytes = file_size = 0L;
  728.     if (setjmp(sendabort)) {
  729.         while (cpend) {
  730.             (void) getreply(0);
  731.         }
  732.         if (data >= 0) {
  733.             (void) close(data);
  734.             data = -1;
  735.         }
  736.         if (oldintr)
  737.             (void) signal(SIGINT,oldintr);
  738.         if (oldintp)
  739.             (void) signal(SIGPIPE,oldintp);
  740.         code = -1;
  741.         echo(stdin, 1);
  742.         return;
  743.     }
  744.     oldintr = signal(SIGINT, abortsend);
  745.     file_size = -1;
  746.     if (strcmp(local, "-") == 0)  {
  747.         fin = stdin;
  748.         filetype = IS_STREAM;
  749.     } else if (*local == '|') {
  750.         filetype = IS_PIPE;
  751.         oldintp = signal(SIGPIPE,SIG_IGN);
  752.         fin = popen(local + 1, "r");
  753.         if (fin == NULL) {
  754.             Perror(local + 1);
  755.             (void) signal(SIGINT, oldintr);
  756.             (void) signal(SIGPIPE, oldintp);
  757.             code = -1;
  758.             return;
  759.         }
  760.     } else {
  761.         filetype = IS_FILE;
  762.         fin = fopen(local, "r");
  763.         if (fin == NULL) {
  764.             Perror(local);
  765.             (void) signal(SIGINT, oldintr);
  766.             code = -1;
  767.             return;
  768.         }
  769.         if (fstat(fileno(fin), &st) < 0 ||
  770.             (st.st_mode&S_IFMT) != S_IFREG) {
  771.             (void) fprintf(stdout, "%s: not a plain file.\n", local);
  772.             (void) signal(SIGINT, oldintr);
  773.             (void) fclose(fin);
  774.             code = -1;
  775.             return;
  776.         }
  777.         file_size = st.st_size;
  778.     }
  779.     if (initconn()) {
  780.         (void) signal(SIGINT, oldintr);
  781.         if (oldintp)
  782.             (void) signal(SIGPIPE, oldintp);
  783.         code = -1;
  784.         close_file(&fin, filetype);
  785.         return;
  786.     }
  787.     if (setjmp(sendabort))
  788.         goto Abort;
  789.  
  790.     if (remote) {
  791.         (void) sprintf(str, "%s %s", cmd, remote);
  792.         if (command(str) != PRELIM) {
  793.             (void) signal(SIGINT, oldintr);
  794.             if (oldintp)
  795.                 (void) signal(SIGPIPE, oldintp);
  796.             close_file(&fin, filetype);
  797.             return;
  798.         }
  799.     } else
  800.         if (command(cmd) != PRELIM) {
  801.             (void) signal(SIGINT, oldintr);
  802.             if (oldintp)
  803.                 (void) signal(SIGPIPE, oldintp);
  804.             close_file(&fin, filetype);
  805.             return;
  806.         }
  807.     dout = dataconn(mode);
  808.     if (dout == NULL)
  809.         goto Abort;
  810.     (void) gettimeofday(&start, (struct timezone *)0);
  811.     oldintp = signal(SIGPIPE, SIG_IGN);
  812.     if (do_reports = (filetype == IS_FILE && NOT_VQUIET))
  813.         do_reports = start_progress(1, local);
  814.  
  815.     switch (curtype) {
  816.  
  817.     case TYPE_I:
  818.     case TYPE_L:
  819.         errno = d = 0;
  820.         while ((c = read(fileno(fin), xferbuf, (int)xferbufsize)) > 0) {
  821.             bytes += c;
  822.             for (bufp = xferbuf; c > 0; c -= d, bufp += d)
  823.                 if ((d = write(fileno(dout), bufp, c)) <= 0)
  824.                     break;
  825.             /* Print progress indicator. */
  826.             if (do_reports)
  827.                 progress_report(0);
  828.         }
  829.         if (c < 0)
  830.             Perror(local);
  831.         if (d <= 0) {
  832.             if (d == 0 && !creating)
  833.                 (void) fprintf(stderr, "netout: write returned 0?\n");
  834.             else if (errno != EPIPE) 
  835.                 Perror("netout");
  836.             bytes = -1;
  837.         }
  838.         break;
  839.  
  840.     case TYPE_A:
  841.         next_report = xferbufsize;
  842.         while ((c = getc(fin)) != EOF) {
  843.             if (c == '\n') {
  844.                 if (ferror(dout))
  845.                     break;
  846.                 (void) putc('\r', dout);
  847.                 bytes++;
  848.             }
  849.             (void) putc(c, dout);
  850.             bytes++;
  851.  
  852.             /* Print progress indicator. */
  853.             if (do_reports && bytes > next_report)
  854.                 progress_report(0);
  855.         }
  856.         if (ferror(fin))
  857.             Perror(local);
  858.         if (ferror(dout)) {
  859.             if (errno != EPIPE)
  860.                 Perror("netout");
  861.             bytes = -1;
  862.         }
  863.         break;
  864.     }
  865. Done:
  866.     close_file(&fin, filetype);
  867.     if (dout)
  868.         (void) fclose(dout);
  869.     (void) getreply(0);
  870.     (void) signal(SIGINT, oldintr);
  871.     if (oldintp)
  872.         (void) signal(SIGPIPE, oldintp);
  873.     end_progress("sent", local, remote);
  874.     return;
  875. Abort:
  876.     code = -1;
  877.     echo(stdin, 1);
  878.     if (!cpend)
  879.         return;
  880.     if (data >= 0) {
  881.         (void) close(data);
  882.         data = -1;
  883.     }
  884.     goto Done;
  885. }    /* sendrequest */
  886.  
  887.  
  888.  
  889. long get_remote_size(char *remote, int filetype)
  890. {
  891.     int oldverbose;
  892.     long rmt_size;
  893.     string str;
  894.  
  895.     rmt_size = -1;                /*
  896.                                  * Return -1 if we could't get it. 
  897.                                  * Not all sites support SIZE.
  898.                                  */
  899.     
  900.     if (filetype == IS_FILE) {
  901.         /* Won't make sense for a pipe or stream. */
  902.         (void) sprintf(str, "SIZE %s", remote);
  903.         *reply_string = 0;
  904.         oldverbose = verbose;
  905.         verbose = V_QUIET;
  906.         (void) command(str);
  907.         verbose = oldverbose;
  908.         if (*reply_string != 5)        /* 5xx is an error. */
  909.             (void) sscanf(reply_string, "%*d %ld", &rmt_size);    
  910.     }
  911.     return rmt_size;
  912. }    /* get_remote_size */
  913.  
  914.  
  915.  
  916. /*ARGSUSED*/
  917. void abortrecv(int unused)
  918. {
  919.     activemcmd = 0;
  920.     abrtflag = 0;
  921.     (void) printf("(abort)\n");
  922.     (void) fflush(stdout);
  923.     longjmp(recvabort, 1);
  924. }    /* abortrecv */
  925.  
  926.  
  927.  
  928. void recvrequest(char *cmd, char *local, char *remote, char *mode)
  929. {
  930.     FILE                        *fout, *din;
  931.     void                        (*oldintr)(int), (*oldintp)(int); 
  932.     int                            oldverbose, oldtype = 0, is_retr;
  933.     int                            tcrflag, nfnd;
  934.     char                        msg;
  935.     string                        str;
  936.     struct fd_set                mask;
  937.     int                            c, d;
  938.     int                            filetype, do_reports = 0;
  939.     string                        remote_dir;
  940.     char                        *cp;
  941. #ifdef REDIR
  942.     char                        *linePtr;
  943.     int                            nchars;
  944.     string                        str2;
  945. #endif
  946.  
  947.     bytes = 0;
  948.     is_retr = strcmp(cmd, "RETR") == 0;
  949.     oldintr = NULL;
  950.     oldintp = NULL;
  951.     tcrflag = /* !crflag && */ is_retr;
  952.  
  953.     /*
  954.      * The ls() function can specify a directory to list along with ls flags,
  955.      * if it sends the flags first followed by the directory name.
  956.      *
  957.      * So far, we don't care about the remote directory being listed.  I put
  958.      * it now so I won't forget in case I need to do something with it later.
  959.      */
  960.     remote_dir[0] = 0;
  961.     if (remote != NULL) {
  962.         cp = index(remote, LS_FLAGS_AND_FILE);
  963.         if (cp == NULL)
  964.             (void) Strncpy(remote_dir, remote);
  965.         else {
  966.             *cp++ = ' ';
  967.             (void) Strncpy(remote_dir, cp);
  968.         }
  969.     }
  970.  
  971.     if (setjmp(recvabort)) {
  972.         echo(stdin, 1);
  973.         while (cpend) {
  974.             (void) getreply(0);
  975.         }
  976.         if (data >= 0) {
  977.             (void) close(data);
  978.             data = -1;
  979.         }
  980.         if (oldintr)
  981.             (void) signal(SIGINT, oldintr);
  982.         code = -1;
  983.         return;
  984.     }
  985.     oldintr = signal(SIGINT, abortrecv);
  986.  
  987.     if (strcmp(local, "-") == 0)
  988.         filetype = IS_STREAM;
  989.     else if (*local == '|')
  990.         filetype = IS_PIPE;
  991.     else {
  992.         filetype = IS_FILE;  /* is_retr ? IS_FILE : IS_STREAM; */
  993.         if (access(local, 2) < 0) {
  994.             char *dir = rindex(local, '/');
  995.  
  996.             if (errno != ENOENT && errno != EACCES) {
  997.                 Perror(local);
  998.                 (void) signal(SIGINT, oldintr);
  999.                 code = -1;
  1000.                 return;
  1001.             }
  1002.             /* See if we have write permission on this directory. */
  1003.             if (dir != NULL) {
  1004.                 /* Special case: /filename. */
  1005.                 if (dir != local)
  1006.                     *dir = 0;
  1007.                 if (access(dir == local ? "/" : local, 2) < 0) {
  1008.                     /*
  1009.                      *    We have a big long pathname, like /a/b/c/d,
  1010.                      *    but see if we can write into the current
  1011.                      *    directory and call the file ./d.
  1012.                      */
  1013.                     if (access(".", 2) < 0) {
  1014.                         (void) strcpy(local, " and .");
  1015.                         goto noaccess;
  1016.                     }
  1017.                     (void) strcpy(local, dir + 1);    /* use simple filename. */
  1018.                 } else
  1019.                     *dir = '/';
  1020.             } else {
  1021.                 /* We have a simple path name (file name only). */
  1022.                 if (access(".", 2) < 0) {
  1023. noaccess:            Perror(local);
  1024.                     (void) signal(SIGINT, oldintr);
  1025.                     code = -1;
  1026.                     return;
  1027.                 }
  1028.             }
  1029.         }
  1030.     }
  1031.     if (initconn()) {
  1032.         (void) signal(SIGINT, oldintr);
  1033.         code = -1;
  1034.         return;
  1035.     }
  1036.     if (!is_retr) {
  1037.         if (curtype != TYPE_A) {
  1038.             oldtype = curtype;
  1039.             oldverbose = verbose;
  1040.             if (!debug)
  1041.                 verbose = V_QUIET;
  1042.             setascii(0, NULL);
  1043.             verbose = oldverbose;
  1044.         }
  1045.     }
  1046.  
  1047.     file_size = -1;
  1048.     if (remote) {
  1049.         file_size = get_remote_size(remote, filetype);
  1050.         (void) sprintf(str, "%s %s", cmd, remote);
  1051.         if (command(str) != PRELIM) 
  1052.             goto nevrmind;
  1053.     } else {
  1054.         if (command(cmd) != PRELIM) {
  1055. nevrmind:    (void) signal(SIGINT, oldintr);
  1056.             if (oldtype) {
  1057.                 if (!debug)
  1058.                     verbose = V_QUIET;
  1059.                 if (oldtype == TYPE_I)
  1060.                     setbinary(0, NULL);
  1061.                 verbose = oldverbose;
  1062.             }
  1063.             return;
  1064.         }
  1065.     }
  1066.     din = dataconn("r");
  1067.     if (din == NULL)
  1068.         goto Abort;
  1069.     if (filetype == IS_STREAM) {
  1070.         fout = stdout;
  1071.     } else if (filetype == IS_PIPE) {
  1072.         oldintp = signal(SIGPIPE, SIG_IGN);
  1073.         fout = popen(local + 1, "w");
  1074.         if (fout == NULL) {
  1075.             Perror(local+1);
  1076.             goto Abort;
  1077.         }
  1078.     } else {
  1079.         fout = fopen(local, mode);
  1080.         if (fout == NULL) {
  1081.             Perror(local);
  1082.             goto Abort;
  1083.         }
  1084.     }
  1085.     do_reports = NOT_VQUIET && is_retr && filetype == IS_FILE;
  1086.     if (do_reports)
  1087.         do_reports = start_progress(0, local);
  1088.  
  1089.     if (setjmp(recvabort))
  1090.         goto Abort;
  1091.  
  1092.     switch (curtype) {
  1093.  
  1094.     case TYPE_I:
  1095.     case TYPE_L:
  1096.         errno = d = 0;
  1097.         while ((c = read(fileno(din), xferbuf, (int)xferbufsize)) > 0) {
  1098.             if ((d = write(fileno(fout), xferbuf, c)) != c)
  1099.                 break;
  1100.             bytes += c;
  1101.  
  1102.             /* Print progress indicator. */
  1103.             if (do_reports)
  1104.                 progress_report(0);
  1105.         }
  1106.         if (c < 0) {
  1107.             if (errno != EPIPE)
  1108.                 Perror("netin");
  1109.             bytes = -1;
  1110.         }
  1111.         if (d < c) {
  1112.             if (d < 0) {
  1113.                 if (errno != EPIPE)
  1114.                     Perror(local);
  1115.             } else
  1116.                 (void) fprintf(stderr, "%s: short write\n", local);
  1117.         }
  1118.         break;
  1119.  
  1120.     case TYPE_A:
  1121. #ifdef REDIR
  1122.         nchars = 0;
  1123.         linePtr = str2;
  1124. #endif
  1125.         next_report = xferbufsize;
  1126.         while ((c = getc(din)) != EOF) {
  1127.             while (c == '\r') {
  1128.                 bytes++;
  1129.                 if ((c = getc(din)) != '\n' || tcrflag) {
  1130.                     if (ferror(fout))
  1131.                         goto break2;
  1132.                     (void) putc('\r', fout);
  1133.                     if (c == '\0') {
  1134.                         bytes++;
  1135.                         goto contin2;
  1136.                     }
  1137.                     if (c == EOF)
  1138.                         goto contin2;
  1139.                 }
  1140.             }
  1141.             (void) putc(c, fout);
  1142.             bytes++;
  1143.             
  1144.             /* Print progress indicator. */
  1145.             if (do_reports && bytes > next_report)
  1146.                 progress_report(0);
  1147. #ifdef REDIR
  1148.             if (nchars < sizeof(str2) - 1) {        /* No seg violations, please */
  1149.                  *linePtr++ = c;  /* build redir string */
  1150.                  nchars++;
  1151.              }
  1152. #endif
  1153.    contin2:
  1154. #ifdef REDIR
  1155.             /* Save the input line in the buffer for recall later. */
  1156.              if (c=='\n' && is_ls) {
  1157.                 register struct lslist *new;
  1158.     
  1159.                 *--linePtr = '\0';
  1160.                 nchars--;
  1161.                 new = (struct lslist *) malloc((size_t) sizeof(struct lslist));
  1162.                 if (new != NULL) {
  1163.                     if (new->string = malloc(strlen(str2) + 1))
  1164.                         (void) strcpy(new->string, str2);
  1165.                        new->next = NULL;
  1166.                        if (lshead == NULL)
  1167.                            lshead = lstail = new;
  1168.                       else {
  1169.                           lstail->next = new;
  1170.                           lstail = new;
  1171.                       }
  1172.                   }
  1173.                   /* reset line buffer */
  1174.                 linePtr = str2;
  1175.                 nchars = 0;
  1176.             }    /* ls mode & last char is a newline */
  1177. #else
  1178.             ;
  1179. #endif
  1180.         }    /* while ((c = getc(din)) != EOF) */
  1181. break2:
  1182.         if (ferror(din)) {
  1183.             if (errno != EPIPE)
  1184.                 Perror("netin");
  1185.             bytes = -1;
  1186.         }
  1187.         if (ferror(fout)) {
  1188.             if (errno != EPIPE)
  1189.                 Perror(local);
  1190.         }
  1191.         break;
  1192.     }    /* end switch (curtype) */
  1193.     
  1194.     close_file(&fout, filetype);
  1195.     (void) signal(SIGINT, oldintr);
  1196.     if (oldintp)
  1197.         (void) signal(SIGPIPE, oldintp);
  1198.     if (din)
  1199.         (void) fclose(din);
  1200.     (void) getreply(0);
  1201.     if (bytes > 0 && is_retr && filetype == IS_FILE)
  1202.         end_progress("received", local, remote);
  1203.     if (oldtype) {
  1204.         if (!debug)
  1205.             verbose = V_QUIET;
  1206.             if (oldtype == TYPE_I)
  1207.                 setbinary(0, NULL);
  1208.         verbose = oldverbose;
  1209.     }
  1210.     return;
  1211. Abort:
  1212.     echo(stdin, 1);
  1213.  
  1214. /* Abort using RFC959 recommended IP,SYNC sequence  */
  1215.  
  1216.     if (oldintp)
  1217.         (void) signal(SIGPIPE, oldintr);
  1218.     (void) signal(SIGINT,SIG_IGN);
  1219.     if (oldtype) {
  1220.         if (!debug)
  1221.             verbose = V_QUIET;
  1222.             if (oldtype == TYPE_I)
  1223.                 setbinary(0, NULL);
  1224.         verbose = oldverbose;
  1225.     }
  1226.     if (!cpend) {
  1227.         code = -1;
  1228.         (void) signal(SIGINT,oldintr);
  1229.         return;
  1230.     }
  1231.  
  1232.     if (!cout) return;
  1233.     (void) fprintf(cout,"%c%c",IAC,IP);
  1234.     (void) fflush(cout); 
  1235.     msg = IAC;
  1236. /* send IAC in urgent mode instead of DM because UNIX places oob mark */
  1237. /* after urgent byte rather than before as now is protocol            */
  1238.     if (send(fileno(cout),&msg,1,MSG_OOB) != 1) {
  1239.         Perror("abort");
  1240.     }
  1241.     (void) fprintf(cout,"%cABOR\r\n",DM);
  1242.     (void) fflush(cout);
  1243.     FD_ZERO(&mask);
  1244.     FD_SET(fileno(cin), &mask);
  1245.     if (din) { 
  1246.         FD_SET(fileno(din), &mask);
  1247.     }
  1248.     if ((nfnd = empty(&mask,10)) <= 0) {
  1249.         if (nfnd < 0) {
  1250.             Perror("abort");
  1251.         }
  1252.         code = -1;
  1253.         lostpeer(0);
  1254.     }
  1255.     if (din && FD_ISSET(fileno(din), &mask)) {
  1256.         while ((c = read(fileno(din), xferbuf, xferbufsize)) > 0)
  1257.             ;
  1258.     }
  1259.     if ((c = getreply(0)) == ERROR && code == 552) { /* needed for nic style abort */
  1260.         if (data >= 0) {
  1261.             (void) close(data);
  1262.             data = -1;
  1263.         }
  1264.         (void) getreply(0);
  1265.     }
  1266.     (void) getreply(0);
  1267.     code = -1;
  1268.     if (data >= 0) {
  1269.         (void) close(data);
  1270.         data = -1;
  1271.     }
  1272.     close_file(&fout, filetype);
  1273.     if (din)
  1274.         (void) fclose(din);
  1275.     end_progress("received", local, remote);
  1276.     (void) signal(SIGINT,oldintr);
  1277. }    /* recvrequest */
  1278.  
  1279.  
  1280.  
  1281.  
  1282. /*
  1283.  * Need to start a listen on the data channel
  1284.  * before we send the command, otherwise the
  1285.  * server's connect may fail.
  1286.  */
  1287.  
  1288. initconn(void)
  1289. {
  1290.     register char        *p, *a;
  1291.     int                    result, len, tmpno = 0;
  1292.     int                    on = 1, rval;
  1293.     string                str;
  1294.     void                (*oldintr)(int);
  1295.  
  1296.     oldintr = signal(SIGINT, SIG_IGN);
  1297. noport:
  1298.     data_addr = myctladdr;
  1299.     if (sendport)
  1300.         data_addr.sin_port = 0;    /* let system pick one */ 
  1301.     if (data != -1)
  1302.         (void) close (data);
  1303.     data = socket(AF_INET, SOCK_STREAM, 0);
  1304.     if (data < 0) {
  1305.         Perror("socket");
  1306.         if (tmpno)
  1307.             sendport = 1;
  1308.         rval = 1;  goto Return;
  1309.     }
  1310.     if (!sendport)
  1311.         if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) {
  1312.             Perror("setsockopt (reuse address)");
  1313.             goto bad;
  1314.         }
  1315.     if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
  1316.         Perror("bind");
  1317.         goto bad;
  1318.     }
  1319.     if (options & SO_DEBUG &&
  1320.         setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
  1321.         Perror("setsockopt (ignored)");
  1322.     len = sizeof (data_addr);
  1323.     if (getsockname(data, (char *)&data_addr, &len) < 0) {
  1324.         Perror("getsockname");
  1325.         goto bad;
  1326.     }
  1327.     if (listen(data, 1) < 0)
  1328.         Perror("listen");
  1329.     if (sendport) {
  1330.         a = (char *)&data_addr.sin_addr;
  1331.         p = (char *)&data_addr.sin_port;
  1332. #define UC(x) (int) (((int) x) & 0xff)
  1333.         (void) sprintf(str, "PORT %d,%d,%d,%d,%d,%d",
  1334.             UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
  1335.         result = command(str);
  1336.         if (result == ERROR && sendport == -1) {
  1337.             sendport = 0;
  1338.             tmpno = 1;
  1339.             goto noport;
  1340.         }
  1341.         rval = (result != COMPLETE);  goto Return;
  1342.     }
  1343.     if (tmpno)
  1344.         sendport = 1;
  1345.     rval = 0;  goto Return;
  1346. bad:
  1347.     (void) close(data), data = -1;
  1348.     if (tmpno)
  1349.         sendport = 1;
  1350.     rval = 1;
  1351. Return:
  1352.     (void) signal(SIGINT, oldintr);
  1353.     return (rval);
  1354. }    /* initconn */
  1355.  
  1356.  
  1357.  
  1358.  
  1359. FILE *
  1360. dataconn(char *mode)
  1361. {
  1362.     struct sockaddr_in from;
  1363.     FILE *fp;
  1364.     int s, fromlen = sizeof (from);
  1365.  
  1366.     s = accept(data, (struct sockaddr *) &from, &fromlen);
  1367.     if (s < 0) {
  1368.         Perror("accept");
  1369.         (void) close(data), data = -1;
  1370.         fp = NULL;
  1371.     } else {
  1372.         (void) close(data);
  1373.         data = s;
  1374.         fp = fdopen(data, mode);
  1375.     }
  1376.     return (fp);
  1377. }    /* dataconn */
  1378.  
  1379. /* eof ftp.c */
  1380.