home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / archival / ftp / BFTP.312 / fts_code.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-29  |  28.3 KB  |  1,119 lines

  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <sys/time.h>
  4. #include <ctype.h>
  5. #include <strings.h>
  6.  
  7. #include <sys/types.h>
  8. #include <sys/param.h>
  9. #include <sys/socket.h>
  10. #include <sys/file.h>
  11.  
  12. #ifdef ISI4_2
  13. #include <netdb.h-bind>
  14. #else
  15. #include <netdb.h>
  16. #endif
  17.  
  18. #include <netinet/in.h>    
  19. #include <arpa/telnet.h>
  20.     
  21. #include "fts.h"
  22.     /* bftp.h is included in fts.h */
  23.  
  24. boolean    conference = FALSE;
  25. FILE    *tracefp = NULL,  /* File to save trace of Telnet history */
  26.     *logfp = NULL;    /* File to compose message */
  27.     
  28. extern int errno;
  29. extern char *sys_errlist[] ;
  30.  
  31. #define endof(x) (x + strlen(x))
  32. #define ToLower(ch) ( islower(ch)? ch:tolower(ch) )
  33. #define ToUpper(ch) ( isupper(ch)? ch:toupper(ch) )
  34.  
  35. char *disposition[] =
  36.     {"SUCCEEDED!",
  37.      "FAILED BUT CAN RETRY",
  38.      "FAILED PERMANENTLY",
  39.      "!!SYSTEM ERROR"
  40.      } ;    
  41.      
  42. /*
  43. *  substr -- return pointer to sub-string in string
  44. *    First check for an exact match; if one is not found,
  45. *    check for a case-independent match.
  46. */
  47. char *
  48. substr (sb, sbSub)
  49.    char  *sb;
  50.    char  *sbSub;
  51. {
  52.    char  *pch;
  53.    char  *pchSub;
  54.    char  *pchStart = sb;
  55.  
  56.    while( *(pch = pchStart++) )
  57.       {
  58.       for( pchSub = sbSub; (*pch && *pchSub); )
  59.          if( *pch++ != *pchSub++ )
  60.             goto mismatch;
  61.       if( !(*pchSub) )
  62.          return (pchStart-1);
  63.    mismatch: ;
  64.       }
  65.    pchStart = sb;
  66.    while( *(pch = pchStart++) )
  67.       {
  68.       for( pchSub = sbSub; (*pch && *pchSub); ) {
  69.          if( ToLower(*pch) != ToLower(*pchSub) )
  70.             goto mismatch2;
  71.      pch++;
  72.      pchSub++;
  73.      }
  74.       if( !(*pchSub) )
  75.          return (pchStart-1);
  76.    mismatch2: ;
  77.       }
  78.    return (NULL);
  79. } /* substr */
  80.  
  81. lookuphost(SH)
  82.    SHandle SH;
  83. {
  84.    if (SH) {
  85.       SH->hostcount = resolve_name(SH->h.host, SH->inetaddr, MAX_addrs);
  86.       if (!SH->hostcount ) {
  87.        fprintf(logfp, "Unknown host: %s\n", SH->h.host);
  88.        return(ERR_RETRY);
  89.            }
  90.       SH->addrcurr = 0;
  91.        }
  92.    return(OK);
  93. } /* lookuphost */
  94.         
  95. /* 
  96. *    return = xfer(Source-SH, Destination-SH)  
  97. */
  98. int     
  99. xfer(ssh, dsh, fptr, reqfile, listfile)
  100.    SHandle ssh, dsh;
  101.    struct fileinfo *fptr;
  102.    char *reqfile, *listfile;
  103. {
  104.     int retcode;
  105.     struct sockaddr_in myctladdr;
  106.     
  107. /*    tracefp = stdout; */
  108. /*    tracefp = logfp;  */
  109.     if (ssh) ssh->socket = -1;
  110.     if (dsh) dsh->socket = -1;
  111.     
  112.     if ( (OK != (retcode = lookuphost(ssh))) ||
  113.         (OK != (retcode = lookuphost(dsh))) ||
  114.             (OK != (retcode = openconn(ssh, &myctladdr))) ||
  115.         (OK != (retcode = openconn(dsh, &myctladdr))) ||
  116.         (OK != (retcode = login(ssh))) ||
  117.         (OK != (retcode = login(dsh))) ||
  118.         (OK != (retcode = TransferTo(ssh, dsh, fptr, 
  119.                         reqfile, listfile, 
  120.                         &myctladdr)))) {
  121.         
  122.         fprintf(logfp, "\nStatus: %s\n\n", 
  123.                 disposition[-1 * retcode]);
  124.         fflush(logfp);
  125.     }
  126.     closeconn(ssh);
  127.     closeconn(dsh);
  128.     return(retcode);
  129. }  /* xfer */
  130.            
  131. int
  132. data_listen(SH, myctladdr, data)
  133.    register SHandle SH;
  134.    struct  sockaddr_in *myctladdr;
  135.    int *data;
  136. {
  137.    register char *p, *a;
  138.    struct  sockaddr_in data_addr;
  139.    int  len = sizeof(data_addr),
  140.        code;            /* return from getreply */
  141.    char    replybuf[MAXreply+1];     /* Text of last FTP reply message */
  142.  
  143.    if (! SH) 
  144.       return(OK);
  145.     
  146.    /* Set up the socket address structure */
  147.    bcopy((char *)myctladdr, (char *)&data_addr, len);
  148.  
  149.    data_addr.sin_port = 0;
  150.    
  151.    /* Open TCP connection...  */
  152.    *data = socket(AF_INET, SOCK_STREAM, 0, 0);
  153.    if (*data < 0) {
  154.        serror(SH, "FTS: data socket");
  155.        return(ERR_SYSTEM);
  156.        }
  157.    if (bind(*data, (char *)&data_addr, sizeof (data_addr)) < 0) {
  158.        serror(SH, "FTS: data bind");    
  159.        close(*data);
  160.        return(ERR_SYSTEM);
  161.        }
  162.    if (getsockname(*data, (char *)&data_addr, &len) < 0) {
  163.        serror(SH, "FTS: data getsockname");
  164.        close(*data);
  165.        return(ERR_SYSTEM);
  166.        }
  167.    if (listen(*data, 1) < 0) {
  168.        serror(SH, "FTS: data listen");
  169.        close(*data);
  170.        return(ERR_SYSTEM);
  171.        }
  172.        
  173.    a = (char *)&data_addr.sin_addr;
  174.    p = (char *)&data_addr.sin_port;
  175. #define UC(b)   (((int)b)&0xff)
  176.  
  177.    if (REP_OK != (code = command(SH, replybuf, "PORT %d,%d,%d,%d,%d,%d",
  178.                     UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
  179.                  UC(p[0]), UC(p[1]))))
  180.       close(*data);
  181.       
  182.    return(msgifreq("PORT error", code, SH, replybuf));
  183. } /* data_listen */      
  184.  
  185. int
  186. data_accept(SH, data, d_fpin)
  187.    register SHandle SH;
  188.    int *data;
  189.    FILE **d_fpin;
  190. {
  191.    FILE *fdopen();
  192.    struct sockaddr_in from;
  193.    int s, fromlen = sizeof (from);
  194.  
  195.    s = accept(*data, &from, &fromlen, 0);
  196.    if (s < 0) {
  197.     serror(SH, "FTS: data accept");
  198.     close(*data);
  199.     return(ERR_SYSTEM);
  200.     }
  201.    *data = s;
  202.     
  203.    /* Open input stream... */
  204.    if ((*d_fpin = fdopen(*data, "r")) == NULL) {
  205.       serror(SH, "FTS: data fdopen");
  206.       close(*data);
  207.       return(ERR_SYSTEM);
  208.       }
  209.    return(OK);
  210. } /* data_accept */
  211.         
  212. data_close(data, d_fpin)
  213.    int data;
  214.    FILE *d_fpin;
  215. {
  216.    int c;
  217.  
  218.    if (d_fpin) {
  219.       /* Read to end of file */
  220.       while ((c = getc(d_fpin)) != EOF) {
  221.      if (tracefp)
  222.         fprintf(tracefp,"%c", (char) c);
  223.      }
  224.  
  225.      /* close connection */
  226.      fclose(d_fpin);
  227.      }
  228.    close(data);
  229. } /* data_close */
  230.  
  231. /* 
  232. *   result = openconn(SHandle)
  233. *
  234. *   Open a TELNET connection to host specified in server-structure.
  235. *   Returns OK if it succeeds (socket, fpin, and fpout will be set).
  236. */
  237.      
  238. int
  239. openconn(SH, myctladdr)
  240.    register SHandle SH;
  241.    struct  sockaddr_in *myctladdr;
  242. {
  243.    struct sockaddr_in remote;    
  244.    int sock, len = sizeof (struct sockaddr_in);
  245.  
  246.    FILE *fdopen();
  247.    
  248.    char    replybuf[MAXreply+1]; /* Text of last FTP reply message */
  249.    
  250.    if (! SH) 
  251.       return(OK);
  252.    else {
  253.     fprintf(logfp,"Connect to: %s, %d\n", SH->h.host, SH->h.port);
  254.     fflush(logfp);    
  255.     
  256.     /* Set up the socket address structure */
  257.     bzero((char *)&remote, sizeof(struct sockaddr_in));
  258.     remote.sin_family = AF_INET;
  259.     bcopy((char *) &SH->inetaddr[SH->addrcurr],
  260.           (char *) &remote.sin_addr, sizeof(struct in_addr)); 
  261.     remote.sin_port = htons(SH->h.port);
  262.     
  263.     SH->socket = sock = socket(AF_INET, SOCK_STREAM, 0, 0);
  264.     if (sock < 0) {
  265.        serror(SH, "FTS: socket");
  266.        return(ERR_SYSTEM);
  267.        }
  268.        
  269.     /* Open TCP connection...  */
  270.     if (connect(sock, (struct sockaddr *) &remote, sizeof(remote)) < 0) {
  271.         close(sock);
  272.         SH->socket = -1;
  273.         serror(SH, "FTS: TELNET connection failed");
  274.         return((errno == ETIMEDOUT) ? ERR_RETRY :
  275.                (errno == ECONNREFUSED) ? ERR_RETRY :
  276.            (errno == ENETUNREACH) ? ERR_RETRY :
  277.            ERR_SYSTEM);
  278.         }
  279.         
  280.     if (getsockname(sock, (char *)myctladdr, &len) < 0) {
  281.         serror(SH, "FTS: getsockname failed");
  282.         close(sock);
  283.         SH->socket = -1;
  284.         return(ERR_SYSTEM);
  285.         }
  286.         
  287.     /* Open input and output streams... */
  288.     SH->fpin =  NULL;
  289.     if ( ((SH->fpin = fdopen(sock, "r")) == NULL)  ||
  290.          ((SH->fpout = fdopen(sock, "w")) == NULL)) {
  291.         if (SH->fpin) 
  292.              fclose(SH->fpin);
  293.         serror(SH, "FTS: fdopen");
  294.         close(sock);
  295.         SH->socket = -1;
  296.         return(ERR_SYSTEM);
  297.         }
  298.         
  299.      /* Get connection greeting message */
  300.      if (REP_OK == getreply(SH, replybuf)) 
  301.         return(OK);
  302.      else 
  303.         return(ERR_RETRY);        
  304.      }
  305. } /* openconn */
  306.  
  307.  
  308. /*
  309. *   closeconn(SHandle)
  310. *
  311. *   Close TELNET connection to FTP Server (gently)
  312. */
  313. closeconn(SH)
  314.    SHandle SH;
  315. {
  316.    if (SH) {
  317.     if (SH->socket < 0) return;  /* not open */
  318.     if (SH->fpout) {
  319.        sendcmd(SH, "QUIT");
  320.        fclose(SH->fpout);
  321.        }
  322.     fclose(SH->fpin);
  323.     close(SH->socket);
  324.     SH->socket = -1;
  325.     }
  326. } /* closeconn */
  327.  
  328.  
  329. /*
  330.  *   result = login(SHandle)
  331.  *
  332.  *   Given open TELNET connection, do login to FTP Server.
  333.  *   Returns OK if it succeeds, or else one of:
  334.  *      ERR_PERMANENT, ERR_RETRY, or ERR_SYSTEM.
  335.  *
  336.  */
  337. int 
  338. login(SH)
  339.    SHandle SH;
  340. {
  341.    int    code;            /* return from getreply */
  342.    char    replybuf[MAXreply+1];     /* Text of last FTP reply message */
  343.  
  344.    if (! SH)
  345.       return(OK);
  346.       
  347.    if (SH->socket < 0)  {
  348.     tellerr(SH, "CONN NOT OPEN FOR LOGIN");
  349.     return(ERR_SYSTEM);
  350.         }
  351.      
  352.    fprintf(logfp,"Logging in: %s, on %s\n", SH->h.user, SH->h.host);
  353.    fflush(logfp);    
  354.     
  355.    if (REP_NEEDMORE == (code = command(SH, replybuf, "USER %s", SH->h.user)))
  356.       if (!strlen(SH->h.passwd)) {
  357.      tellerr(SH, "Need password, none supplied");
  358.      return(ERR_PERMANENT);
  359.      }
  360.       else if (REP_NEEDMORE == (code = command(SH, replybuf, "PASS %s", SH->h.passwd) ) )
  361.          if (!strlen(SH->h.acct)) {
  362.         tellerr(SH, "Need account, none supplied");
  363.         return(ERR_PERMANENT);
  364.             }
  365.      else        
  366.         code = command(SH, replybuf, "ACCT %s", SH->h.acct);
  367.         
  368.    return(msgifreq("Login failure", code, SH, replybuf));
  369. } /* login */
  370.  
  371. char
  372. scan_system(cp)
  373.    char *cp;
  374. {
  375.    if (! strncmp(cp, "UNIX", 4))
  376.       return('/');
  377.    else if (! strncmp(cp, "TOPS20", 6))
  378.       return(':');
  379.    else return('\0');
  380. }
  381.  
  382. /*
  383.  *   result = TransferTo(Shandle1, Shandle2)
  384.  *
  385.  *   Given two servers, each logged in,  transfer file from 1 to 2.
  386.  *   Returns OK if it succeeds, or else one of:
  387.  *       ERR_PERMANENT, ERR_RETRY, or ERR_SYSTEM.
  388.  *
  389.  */     
  390. #define MAXpsave  48   /* max len(A1,A2,A3,A4,a1,a2) */
  391. int
  392. TransferTo(SH1, SH2, f, reqfile, filelist, myctladdr)
  393.    SHandle SH1, SH2;
  394.    struct fileinfo *f;
  395.    char *reqfile, *filelist;
  396.    struct  sockaddr_in *myctladdr;
  397. {
  398.    FILE *listp,
  399.        *d_fpin = NULL;    /* File for data connection (NLST) */
  400.  
  401.    int    data,            /* Socket for TCP data connection */
  402.     code1, code2, 
  403.     count, dir_count, dir_len,
  404.     skip, i, cInt;
  405.    char    replybuf[MAXreply+1],     /* Text of last FTP reply message */
  406.        tycmd[20], filename[100],
  407.     file1[50], file2[50], *cp,
  408.     port_save[MAXpsave+1],
  409.     temp[100], listfile[100], c;
  410.    SHandle sh;
  411.  
  412.    boolean sh1_pasv,
  413.     found_dir = FALSE,
  414.     srcCWDfailed = FALSE,
  415.     dstCWDfailed = FALSE;
  416.  
  417.    if (filelist)
  418.       strcpy(listfile, filelist);
  419.    else
  420.       listfile[0] = '\0';
  421.  
  422.    for (sh = SH1; sh; sh = (sh == SH1) ? SH2: NULL) {
  423.       if (dir_len = strlen(sh->h.dir)) {
  424.      sprintf(tycmd,"CWD %s",sh->h.dir);
  425.      /* fail temporarily if it timed out */
  426.      if (REP_TIMEOUT == (code1 = command(sh, replybuf, tycmd)))
  427.         return(msgifreq("CWD failed -- timeout", code1, sh, replybuf));
  428.  
  429.      /* if we are already in the "pub" directory, 
  430.            if first part of directory is "pub", 
  431.               strip it off and try again 
  432.            else
  433.               try going up one level and try cwd again */
  434.      if (REP_OK !=  code1) {
  435.         /* find out if we are in "pub/" now */
  436.         if (REP_TIMEOUT == (code1 = command(sh, replybuf, "PWD")))
  437.            return(msgifreq("PWD failed -- timeout", code1, sh, replybuf));
  438.         else
  439.            if (REP_OK == code1 && !strncmp(replybuf+4, "\"/pub", 5))
  440.           /* we are in "pub"; is that what we wanted? */
  441.           if (dir_len > 3 && 
  442.               !strncmp(sh->h.dir, "pub/",(dir_len>3)?4:3))
  443.              if (dir_len>4) {
  444.                 sprintf(tycmd,"CWD %s",sh->h.dir+4);
  445.             if (REP_TIMEOUT == (code1 = 
  446.                         command(sh, replybuf, tycmd)))
  447.                return(msgifreq("CWD failed -- timeout", 
  448.                        code1, sh, replybuf));
  449.                 }
  450.              else
  451.                 code1 = REP_OK;
  452.           else {
  453.              /* try going up one level */
  454.              sprintf(tycmd,"CWD ../%s",sh->h.dir);
  455.              if (REP_TIMEOUT == (code1 = command(sh, replybuf, tycmd)))
  456.                 return(msgifreq("CWD failed -- timeout",
  457.                     code1, sh, replybuf));
  458.              }
  459.            else
  460.           code1 = REP_PERMERR;
  461.         }
  462.      /* get the directory delimiter if needed */
  463.      if (REP_OK !=  code1 || (f->multflag && sh == SH2))
  464.         if (strlen(sh->h.dir) &&
  465.            ispunct(sh->h.dir[strlen(sh->h.dir)-1]) &&
  466.         sh->h.dir[strlen(sh->h.dir)-1] != '*')
  467.               sh->dir_delim = sh->h.dir[strlen(sh->h.dir)-1];
  468.         else 
  469.            if (REP_OK != (command(sh, replybuf, "SYST")) ||
  470.                !(sh->dir_delim = scan_system(replybuf+4)))
  471.           sh->dir_delim = '\0';
  472.            else {
  473.           sh->h.dir[strlen(sh->h.dir)] = sh->dir_delim;
  474.           sh->h.dir[strlen(sh->h.dir)] = '\0';
  475.           }
  476.  
  477.      if (REP_OK !=  code1) {
  478.         /* if last char of dir != punc then fail-perm */
  479.         if (! sh->dir_delim) {
  480.            return(msgifreq(
  481.               "CWD failed -- directory delimiter not known", 
  482.               REP_PERMERR, sh, replybuf));
  483.            }
  484.         if (sh == SH1)
  485.            srcCWDfailed = TRUE;
  486.         else
  487.            dstCWDfailed = TRUE;
  488.         }
  489.          }
  490.       } /* for */
  491.         
  492. /* *** if both ends are "UNIX Type: L8" go into image mode. */
  493.         
  494.    /* get list of file names from source host */
  495.    if (srcCWDfailed) {
  496.       strcpy(file1, SH1->h.dir);
  497.       strcat(file1, SH1->h.file);
  498.       }
  499.    else
  500.       strcpy(file1, SH1->h.file);
  501.         
  502.    if (!strlen(listfile)) {
  503.       strcpy(listfile, reqfile);
  504.       strcpy(endof(listfile)-3,"list");
  505.       if (tracefp) {
  506.      fprintf(tracefp,"Creating '%s' list-file\n", listfile);
  507.      fflush(tracefp);
  508.          }
  509.       listp = fopen(listfile, "w");
  510.       sprintf(temp,"%5d\n",0); /* write out number entries to skip */
  511.       fputs(temp, listp);   
  512.    
  513.       if (!f->multflag && (f->reqtype!=VERIFY) && (f->reqtype!=VERIFY_SRC)) {
  514.      fputs(file1, listp);
  515.      if (conference) {
  516.         fputc('\n', listp);
  517.         name_dotfile(file1, temp);
  518.         fputs(temp, listp); /* put in name of dot-file */
  519.         }
  520.          }
  521.       else { /* Use NLST to get list */
  522.      if ((code1 = data_listen(SH1, myctladdr, &data)) != OK) {
  523.         fclose(listp);
  524.         return(code1);
  525.         }
  526.      if ((code1 = command(SH1, replybuf, "NLST %s", file1)) != REP_PRELIM &&
  527.          code1 != REP_OK) {
  528.         fclose(listp);
  529.         data_close(data, d_fpin);
  530.         return(msgifreq("NLST error", code1, SH1, replybuf));
  531.         }
  532.      if ((code2 = data_accept(SH1, &data, &d_fpin)) != OK)
  533.         return(code2);
  534.  
  535.      cp = filename;
  536.      while ((cInt = getc(d_fpin)) != EOF) {
  537.         c = cInt;
  538.         if (c != '\015') {
  539.            putc(c,listp);
  540. /* debugging           fprintf(logfp,"%c",c); */
  541.            if (c != '\n')
  542.           *cp++ = c;
  543.            }    
  544.         else {
  545.            *cp = '\0';
  546.            if (conference) {
  547.           fputc('\n', listp);
  548.           name_dotfile(filename, temp);
  549.           fputs(temp, listp); /* put in name of dot-file */
  550. /* debugging          printf("-->%s %s\n",filename, temp); */
  551.           }
  552.            cp = filename;
  553.            }
  554.         }
  555.      data_close(data, d_fpin);
  556.      while (code1==REP_PRELIM) code1 = getreply(SH1, replybuf);
  557.      if (code1 == REP_PERMERR) {
  558.         fclose(listp);
  559.         return(msgifreq("NLST error", code1, SH1, replybuf));
  560.         }
  561.          }
  562.       fclose(listp);
  563.       } /* if (!strlen(listfile)) */
  564.     
  565.    for (sh = SH1; sh; sh = (sh == SH1) ? SH2: NULL) {
  566.         /* Send type setup commands to each side of connection. */
  567.         if (sh->socket < 0)  {
  568.            tellerr(sh, "CONN NOT OPEN FOR TRANSFER");
  569.            return(ERR_SYSTEM);
  570.            }
  571.         if ((f->reqtype != DFILE) && (f->reqtype != VERIFY_SRC)) {
  572.            if (f->stru != 'F') {
  573.            sprintf(tycmd,"STRU %c",f->stru);
  574.            if (REP_OK !=  (code1 = command(sh, replybuf, tycmd) ))
  575.               return(msgifreq("Host does not support stru", code1, sh, replybuf));
  576.            }
  577.            /* note different default for (f->stru == 'P') */
  578.            if ((f->mode != 'S') || (f->stru == 'P')) {
  579.           sprintf(tycmd,"MODE %c",f->mode);
  580.           if (REP_OK != (code1 = command(sh, replybuf, tycmd) ))
  581.              return(msgifreq("Host does not support mode", code1, sh, replybuf));
  582.           }
  583.            if ((strcmp(f->filetype,"A N")!=0) || (f->stru == 'P')) {
  584.           sprintf(tycmd,"TYPE %s",f->filetype);
  585.           if (REP_OK != (code1 = command(sh, replybuf, tycmd) ))
  586.              return(msgifreq("Host does not support file type", code1, sh, replybuf));
  587.           }
  588.            }
  589.       } /* for */
  590.  
  591.  
  592.    /* Not all hosts support PASV; try one & if it fails try the other. */
  593. #ifdef DST_FIRST
  594.    sh = SH2;
  595.    sh1_pasv = FALSE;
  596. #else
  597.    sh = SH1;
  598.    sh1_pasv = TRUE;
  599. #endif
  600.    if (REP_PERMERR == (code1 = command(sh, replybuf, "PASV")) ) {
  601.            /* it failed permanently, so try the other end */
  602.            if (sh1_pasv) {
  603.           sh = SH2;
  604.           sh1_pasv = FALSE;
  605.           }
  606.        else {
  607.           sh = SH1;
  608.           sh1_pasv = TRUE;
  609.           }
  610.        if (REP_PERMERR == (code1 = command(sh, replybuf, "PASV")) )
  611.           return(msgifreq("Neither host supports PASV", code1, sh, replybuf));
  612.        }
  613.    if (code1 != REP_OK) 
  614.        return(msgifreq("Temporary error in PASV", code1, sh, replybuf));
  615.         
  616.    if (! scan_port(port_save, MAXpsave, &replybuf[5])) {
  617.        reply_err("Malformed PASV reply", sh, replybuf);
  618.        return(ERR_PERMANENT);
  619.        }
  620.         
  621.     if ((f->reqtype == COPY) || (f->reqtype == MOVE)) {
  622.        /* Send PORT command to other host */         
  623.        sh = (sh1_pasv)? SH2 : SH1;
  624.        if (REP_OK != (code1 = command(sh, replybuf, "PORT %s", port_save)) )
  625.           return(msgifreq("PORT error", code1, sh, replybuf));
  626.        }
  627.           
  628.    /* get list of files */
  629.    listp = fopen(listfile, "r+");
  630.    if (listp == NULL) {
  631.       tellerr(SH1,"Can't find list of remote files.");
  632.       return(ERR_PERMANENT);
  633.       }
  634.    /* read number of files to skip */
  635.    fgets(filename, sizeof(filename), listp);
  636.    if (skip = atoi(filename)) {
  637.      skip--;    /* otherwise diskless hosts may be missing part of last file */
  638.      if (skip && tracefp) 
  639.         fprintf(tracefp,"Skipping %d file(s).\n", skip);
  640.      }
  641.         
  642. /* write out number of entries to skip */          
  643. #define save_skip() {rewind(listp); \
  644.              sprintf(temp,"%5d\n",count-1); \
  645.              fputs(temp, listp); \
  646.              fclose(listp);\
  647.              if (filelist && !strlen(filelist))\
  648.                 strcpy(filelist, listfile);}
  649.  
  650.    count = 0;
  651.    found_dir = FALSE;
  652.    while (fgets(filename, sizeof(filename), listp) != NULL) {
  653.         /* loop on each file name */
  654.         if ((cp = index(filename, '\n')) != NULL)
  655.            *cp = '\0';
  656.            
  657.         /* check for BSD directory notation */
  658.         if ((!strlen(filename)) || (index(filename, ' ')) || 
  659.         (filename[strlen(filename)-1] == ':')) {
  660.            found_dir = TRUE;
  661.            break;
  662.            }
  663.         count++;
  664.         
  665.         /* munge file names */
  666.         strcpy(file1, filename);
  667.         if ((f->reqtype != VERIFY_SRC) && (f->reqtype != DFILE)) {
  668.                strcpy(file2, (dstCWDfailed)? SH2->h.dir : "");
  669.            strcat(file2, ((!strlen(SH2->h.file)) ||
  670.                           (f->multflag && (f->creation != APPE))||
  671.                   (conference && count>1))?
  672.                  filename : SH2->h.file);
  673.            }
  674.            
  675.         if (((f->reqtype == COPY) && (count > skip)) || 
  676.             (f->reqtype == MOVE)) {
  677.            fprintf(logfp, "Transferring %s\n", file1);
  678.            fflush(logfp);
  679.            if (count != 1) {
  680.           sh = (sh1_pasv)? SH1:SH2;
  681.           if (REP_OK != (code1 = command(sh, replybuf, "PASV"))) {
  682.              save_skip();
  683.              return(msgifreq("PASV command failed", code1, sh, replybuf));
  684.              } 
  685.          if (! scan_port(port_save, MAXpsave, &replybuf[5])) {
  686.              fclose(listp);
  687.              reply_err("Malformed PASV reply", sh, replybuf);
  688.              return(ERR_PERMANENT);
  689.              }
  690.           sh = (sh1_pasv)? SH2:SH1;
  691.           if (REP_OK != (code1 = command(sh, replybuf,
  692.                   "PORT %s", port_save) )) {
  693.              save_skip();
  694.              return(msgifreq("PORT error", code1, sh, replybuf));
  695.              }
  696.           }
  697.  
  698.            /* Now open the DT connection(s) */
  699.            /* STOR, 5 second pause, RETR -- For the TOPS-20s */
  700.          
  701.            sendcmd(SH2, "%s %s", (f->creation==APPE)?"APPE":
  702.                           (f->creation==STOU)?"STOU":
  703.                         "STOR", 
  704.                      file2);
  705.            sleep(5);
  706.            sendcmd(SH1, "RETR %s", file1);
  707.            
  708.            code1 = code2 = REP_PRELIM;
  709.            while ((code1<=REP_PRELIM)||(code2<=REP_PRELIM)) {
  710.           sh = SH1;
  711.           if (code1<=REP_PRELIM) code1  = getreply(SH1, replybuf);
  712.           if ((code1==REP_PERMERR) || (code1==REP_TEMPERR)) break;
  713.           sh = SH2;
  714.           if (code2<=REP_PRELIM) code2 = getreply(SH2, replybuf);
  715.           if ((code2==REP_PERMERR) || (code2==REP_TEMPERR)) break;
  716.               }
  717.            if (code1 != REP_OK || code2 != REP_OK) {
  718.           if (!tracefp)
  719.              fprintf(logfp,"  %s ==> %s\n", sh->h.host, replybuf);
  720.           if ((conference || f->multflag) &&
  721.               (code1 == REP_PERMERR || code2 == REP_PERMERR)) {
  722.              if (f->multflag && code1 <= REP_PRELIM && SH2->dir_delim) {
  723.             for (i = strlen(file1)-1; i ; i--)
  724.                 if (file1[i] == SH2->dir_delim) break;
  725.             if (i) {
  726.                strncpy(temp, file1, 
  727.                        (SH2->dir_delim == '/')? i: i+1);
  728.                if (REP_OK == (code2 = command(SH2, replybuf,"MKD %s",temp)) ||
  729.                    REP_OK == (code2 = command(SH2, replybuf,"XMKD %s",temp))) {
  730.                   code2 = REP_PRELIM;
  731.                   sendcmd(SH2, "%s %s", 
  732.                    (f->creation==APPE)?"APPE":
  733.                       (f->creation==STOU)?"STOU": "STOR", 
  734.                 file2);
  735.                   while ((code1<=REP_PRELIM)||
  736.                        (code2<=REP_PRELIM)) {
  737.                        if (code1<=REP_PRELIM)
  738.                         code1 = getreply(SH1, replybuf);
  739.                      if ((code1==REP_PERMERR) || 
  740.                           (code1==REP_TEMPERR)) 
  741.                     break;
  742.                      if (code2<=REP_PRELIM)
  743.                         code2 = getreply(SH2, replybuf);
  744.                      if ((code2==REP_PERMERR) || 
  745.                           (code2==REP_TEMPERR)) 
  746.                     break;
  747.                      }
  748.                   }
  749.                }
  750.             if (code1 != REP_OK || code2 != REP_OK)
  751.                if (! sendabort(SH1)) {
  752.                   count += 2;
  753.                   save_skip();
  754.                   return(ERR_RETRY);
  755.                   }
  756.             }
  757.              else if (code2 <= REP_PRELIM) {
  758.             if (! sendabort(SH2)) {
  759.                count += 2;
  760.                save_skip();
  761.                return(ERR_RETRY);
  762.                }
  763.             }
  764.              }
  765.           else {
  766.              save_skip();
  767.              return( (code1==REP_PERMERR || code2==REP_PERMERR)
  768.                       ? ERR_PERMANENT :
  769.                     ERR_RETRY );
  770.              }
  771.           }
  772.            }
  773.         else if ((f->reqtype == VERIFY) || (f->reqtype == VERIFY_SRC)) {
  774.            if ((! f->multflag) && (substr(filename, SH1->h.file)==NULL)) {
  775.           tellerr(SH1,
  776.              "Source file \'%s\' not found -- %swildcard matching.",
  777.             SH1->h.file, (f->multflag) ? NULL : "NO ");
  778.           fclose(listp);  /* no skipping in this case */
  779.           return(ERR_PERMANENT);
  780.           }
  781.            if (count == 1)
  782.               fprintf(logfp,"Matching file names:\n");
  783.            fprintf(logfp,"    %s\n", file1);
  784.            }
  785.         if ((f->reqtype == DFILE) || (f->reqtype == MOVE)) {
  786.            fprintf(logfp, "Deleting %s.\n", file1);
  787.            code1 = REP_PRELIM;
  788.            sendcmd(SH1, "DELE %s", file1);
  789.            while (code1==REP_PRELIM) code1 = getreply(SH1, replybuf);
  790.            if (code1 != REP_OK) {
  791.           fclose(listp);  /* no skipping in this case */
  792.           return(msgifreq("DELE error", code1, SH1, replybuf));
  793.           }
  794.            }
  795.       } /* while */
  796.    fflush(logfp);
  797.      
  798.    if (found_dir && f->multflag &&
  799.               ((f->reqtype == VERIFY) || (f->reqtype == VERIFY_SRC))) {
  800.      dir_count = 0;
  801.      rewind(listp);
  802.      while (fgets(filename, sizeof(filename), listp) != NULL) 
  803.         { /* loop on each file name */
  804.         if ((cp = index(filename, '\n')) != NULL)
  805.            *cp = '\0';
  806.            
  807.         /* check for BSD directory notation */
  808.         if (index(filename, ':')) {
  809.            if (!dir_count)
  810.           fprintf(logfp,"Matching directory:\n");
  811.            fprintf(logfp,"    %s\n",filename);
  812.            dir_count++;
  813.            }
  814.         }
  815.       }
  816.    fclose(listp);
  817.       
  818.    if (!count) {
  819.      tellerr(SH1,"Source file \'%s\' not found.", file1);
  820.      return(ERR_PERMANENT);
  821.          }
  822.    fflush(logfp);
  823.    return(OK);
  824.  
  825. } /* TransferTo */
  826.  
  827. /*
  828.  *     sendcmd(SHandle, format, arg1, arg2, ...)
  829.  *
  830.  *  Format command string  and send to specified host.
  831.  *
  832.  */
  833. sendcmd(SH, format, arg1, arg2, arg3, arg4, arg5, arg6)
  834.    SHandle SH;
  835.    char *format;
  836. {
  837.    char cmdbuff[256];
  838.    if (SH) {
  839.     sprintf(cmdbuff, format, arg1, arg2, arg3, arg4, arg5, arg6);
  840.     if (tracefp) {
  841.        fprintf(tracefp, "  %s <== %s\n", SH->h.host, 
  842.               (strncmp(format,"PASS",4) == 0)? "PASS XXX": cmdbuff);
  843.        fflush(tracefp);
  844.            }
  845.     fputs(cmdbuff, SH->fpout);
  846.     fputs("\r\n", SH->fpout);
  847.     fflush(SH->fpout);
  848.     }
  849. } /* sendcmd */
  850.                                  
  851. int
  852. sendabort(SH)
  853.    SHandle SH;
  854. {
  855.    int temp1, temp2;
  856.    char msg[2];
  857.  
  858.    char    replybuf[MAXreply+1]; /* Text of last FTP reply message */
  859.  
  860.    fprintf(SH->fpout,"%c%c",IAC,IP);
  861.    (void) fflush(SH->fpout);
  862.    *msg = IAC;
  863.    *(msg+1) = DM;
  864.    if (send(fileno(SH->fpout),msg,2,MSG_OOB) != 2)
  865.       perror("abort");
  866.    
  867.    /* get the reply to the previous command and the reply to the ABORT */
  868.    temp1 = command(SH, replybuf,"ABOR");
  869.    temp2 = getreply(SH, replybuf);
  870.    return((temp1 && temp1 != REP_PERMERR && 
  871.                temp2 != REP_PERMERR)? TRUE: FALSE);
  872. }   
  873.  
  874. /*
  875.  *    result = command(SHandle, format, arg1, arg2, ...)
  876.  *
  877.  *  Format command string and send to specified host, and then
  878.  *  call getreply() and return result code.
  879.  *
  880.  */    
  881. int 
  882. command(SH, ptr, format, arg1, arg2, arg3, arg4, arg5, arg6)
  883.    SHandle SH;
  884.    char *ptr, *format;
  885. {
  886.    if (SH) {
  887.     sendcmd(SH, format, arg1, arg2, arg3, arg4, arg5, arg6);
  888.     return(getreply(SH, ptr));
  889.     }
  890.    else return(REP_PERMERR);
  891. } /* command */
  892.  
  893.  
  894. /*
  895. *  int = getreply(SHandle)
  896. *
  897. *  Receive next reply message from remote host, and return binary
  898. *  value of reply number as result.  Actual text is stored in global
  899. *  array reply_text.
  900. *
  901. *  This routine waits for first reply, then if more input is buffered
  902. *  in the kernel it gets the next reply.  It returns only the last
  903. *  complete reply when no more input is buffered.
  904. *
  905. */
  906.  
  907. int
  908. getreply(SH, ptr)
  909.    SHandle SH;
  910.    char *ptr;
  911. {
  912.    register char c;
  913.    int firstdigit, n, cInt;
  914.    register char *cp  = ptr;
  915.    register int cpcnt = MAXreply;
  916.    char *startline, *tmp;
  917.    
  918.    *ptr = '\0';
  919.    
  920.    for (;;) {     /*  Loop through one line of reply... */
  921.       /* time out and return 0 after N seconds */
  922.       for (n = 0; n < FTPTIMEOUT; n++) {
  923.          if (! empty(SH->fpin)) break;
  924.      sleep(1);
  925.      }
  926.       if (n >= FTPTIMEOUT)
  927.          return(REP_TIMEOUT);
  928.  
  929.       startline = cp;
  930.       while ((cInt = getc(SH->fpin)) != '\n') {
  931.      if (cInt == EOF) {
  932.           fprintf(logfp, "Host %s closed Conn\n", SH->h.host);
  933.           return(REP_TEMPERR);
  934.               }
  935.      c = cInt;
  936.      if (cpcnt == MAXreply) {
  937.           if (!isdigit(c)) {
  938.              /* Syntax error... reply does not begin with digit!! */
  939.              /* Invent code 599 for the text... */
  940.              strcpy(ptr, "599 ");
  941.              cpcnt -= 4;
  942.              cp += 4;
  943.              firstdigit = 5;
  944.              }
  945.           else
  946.              firstdigit = c - '0';
  947.           }
  948.                 
  949.      if ((c != '\r') && (--cpcnt > 0))  *cp++ = c;
  950.          }
  951.         
  952.       *cp = '\0';
  953.       if (tracefp) {
  954.      fprintf(tracefp, "  %s ==> %s\n", SH->h.host, startline);
  955.      fflush(tracefp);
  956.      }
  957.           
  958.       /* End of line. Test for continuation... */
  959.       tmp = ptr+3;
  960.       if ((*tmp != '-')  ||
  961.         (startline[3] != '-' &&
  962.         (strncmp(ptr, startline, 3) == 0)) )  {
  963.        /* Have complete reply.  But if there is more input
  964.         *   buffered, start over... */
  965.        if (empty(SH->fpin)) {
  966.           return(firstdigit);
  967.           }
  968.             
  969.        cp  = ptr;
  970.        cpcnt = MAXreply;
  971.        }
  972.         
  973.       }
  974. } /* getreply */
  975.  
  976.     /*
  977.      *        boolean empty(FILE *)
  978.      *
  979.      *   Based on routine from User FTP from 4.2BSD.
  980.      */
  981. fd_select(fd)
  982.    int fd;
  983. {
  984.    int fd_width = getdtablesize();
  985.    
  986. #ifdef BSD4_3
  987.    fd_set mask, dummy;
  988. #else   
  989.    long mask = (1 << fd),
  990.        dummy = 0;
  991. #endif    
  992.  
  993.    struct timeval t;
  994.    int i;
  995.  
  996. #ifdef BSD4_3
  997.    FD_ZERO(&mask);
  998.    FD_SET(fd, &mask);
  999.    FD_ZERO(&dummy);
  1000. #endif
  1001.    
  1002.    t.tv_sec = t.tv_usec = 0;
  1003.    if (fd  > fd_width) {
  1004.       fprintf(stderr, "Error: maximum fd count (%d) exceeded.\n",fd_width);
  1005.       return(0);    /* assume that it is empty */
  1006.       }
  1007.    if ((i = select(fd_width, &mask, &dummy, &dummy, &t)) == -1) {
  1008.       perror("fts: Select error");
  1009.       return(1);    /* assume that it is not empty */
  1010.       }
  1011.    return(i);
  1012. }     
  1013.  
  1014. empty(f)
  1015.     FILE *f;
  1016. {
  1017.    if (f->_cnt > 0)
  1018.       return (0);
  1019.    return((fd_select(fileno(f)) == 0)? TRUE: FALSE);
  1020. } /* empty */
  1021.  
  1022. boolean
  1023. scan_port(pp, maxlen, ptr) 
  1024.    /* Scan reply for port parameters */
  1025.    char *pp;
  1026.    int maxlen;
  1027.    char *ptr;
  1028. {
  1029.    char *cp, *start, digit[2];
  1030.    int n, totlen, params;
  1031.     
  1032.    /* Position cp on first digit */
  1033.    for (cp = ptr; (*cp) && !isdigit(*cp); cp++ ) ;
  1034.    start = cp;
  1035.    n = 0;
  1036.    for (totlen=0 ; ; cp++, totlen++) {
  1037.     if ((! *cp) || *cp == ')' || *cp == ',' || *cp == ' ') {
  1038.        if (n > 255)
  1039.           return(FALSE);
  1040.        else 
  1041.           n = 0;
  1042.        }
  1043.     else {
  1044.        strncpy(digit, cp, 1);
  1045.        digit[1] = '\0';
  1046.        if (isdigit(digit[0])) 
  1047.           n = (n*10) + atoi(digit);
  1048.        else
  1049.           return(FALSE);
  1050.        }
  1051.     if ((! *cp) || *cp == ')')
  1052.        break;
  1053.     }
  1054.    if ((totlen==0) || (totlen >= maxlen))
  1055.       return(FALSE);
  1056.    else {
  1057.       strncpy(pp,start,totlen);
  1058.       *(pp+totlen) = '\0';
  1059.       return(TRUE);
  1060.       }
  1061. } /* scan_port */
  1062.  
  1063. /* 
  1064. *  tellerr(Server-handle, format, arg1, arg2, ...)
  1065. *       --  Format error message associated with specified host
  1066. */
  1067.  
  1068. tellerr(SH, format, arg1, arg2, arg3, arg4)
  1069.    SHandle SH;
  1070.    char *format;
  1071. {
  1072.     char temp[256];
  1073.     sprintf(temp, format, arg1, arg2, arg3, arg4);
  1074.  
  1075.     fprintf(logfp, "\n%s -- %s\n", SH->h.host,  temp);
  1076. }
  1077.  
  1078.  
  1079. /* 
  1080. *  serror(server-handle, string)  -- like the system routine perror()
  1081. */
  1082.  
  1083. serror(SH, cp) 
  1084.    SHandle SH;
  1085.    char *cp;
  1086. {
  1087.    tellerr(SH, "%s: %s", cp, sys_errlist[errno]);
  1088. }
  1089.  
  1090. reply_err(msg, sh, reply)
  1091.    char *msg;
  1092.    SHandle sh;
  1093.    char *reply;
  1094. {
  1095.    tellerr(sh, "%s\n\tError: %s", msg, reply);
  1096. }
  1097.  
  1098. int
  1099. msgifreq(msg, code, sh, reply)
  1100.     char *msg;
  1101.     int code;
  1102.     SHandle sh;
  1103.    char *reply;
  1104. {
  1105.    switch (code) {
  1106.       case REP_OK:
  1107.          return(OK);
  1108.       case REP_TIMEOUT:
  1109.      tellerr(sh, "FTP timeout");
  1110.      return(ERR_RETRY);
  1111.       case REP_PERMERR:
  1112.          reply_err(msg, sh, reply);
  1113.      return(ERR_PERMANENT);
  1114.       default:
  1115.      reply_err(msg, sh, reply);
  1116.      return(ERR_RETRY);
  1117.       }
  1118. } /* msgifreq */
  1119.