home *** CD-ROM | disk | FTP | other *** search
/ Super Net 1 / SUPERNET_1.iso / PC / OTROS / UNIX / ARCHIE / CLIENTS / XARCHIE-.1 / FTP.C < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-22  |  46.5 KB  |  1,651 lines

  1. /*
  2.  * ftp.c : This code implements an asynchronous ftp client.
  3.  *
  4.  * George Ferguson, ferguson@cs.rochester.edu, 23 Apr 1993.
  5.  *
  6.  * The client is represented by an instance of the FtpContext structure,
  7.  * which includes the state of the client with respect to a finite-state
  8.  * model of the FTP process. All aspects of the connection are contained
  9.  * in the structure, so a process can have multiple outstanding FTP
  10.  * sessions (subject to filesystem limitations, of course).
  11.  *
  12.  * Refer to RFC959 for details of the state model.
  13.  *
  14.  * Two external functions must be provided:
  15.  *   void RegisterFtpFd( ftpc, fd, flags, func )
  16.  *      Should arrange to call FUNC passing FTPC whenever FD is ready
  17.  *      as indicated by FLAGS (from <fcntl.h>).
  18.  *   void UnregisterFtpFd( ftpc, fd )
  19.  *      Cancels a previous registration of FD for FTPC.
  20.  * Samples of these using select(2) are given below in the STANDALONE
  21.  * section. Xarchie uses the XtAppAddInput() mechanism. The code also
  22.  * uses alert0() and status0() for messages, sysError() for error
  23.  * messages from system calls, and ftpPrompt() if prompting is enabled.
  24.  *
  25.  * To use these routines:
  26.  *   ftpc = ftpNewContext(...);
  27.  *   ftpStart(ftpc);
  28.  * The function you registered as the "done" parameter in ftpNewContext
  29.  * will be called back when the process finishes (for any reason). You
  30.  * should call ftpFreeContext() in this callback (or register it as the
  31.  * callback, or register NULL).
  32.  *
  33.  * This code is decidely *not* a general-purpose FTP library. It is
  34.  * designed to support the batch transfers required by xarchie. I suppose
  35.  * that it could be modified to be more general without too much effort.
  36.  * In particular, many of the routines called by ftpProcessReply could
  37.  * be made into external interface functions. Volunteers?
  38.  */
  39. #include <stdio.h>
  40. #include <errno.h>
  41. #include "config.h"
  42. #ifdef HAVE_SYS_PARAM_H
  43. #include <sys/param.h>
  44. #endif
  45. #include "ftp.h"    /* includes <sys/types.h> and <netinet/in.h> */
  46. #include "sysdefs.h"    /* char *malloc() */
  47. #include "stringdefs.h" /* char *strcpy(), *index(); */
  48. #include "debug.h"    /* MSG[0-3]() macros */
  49.  
  50. /* These functions have to be provided: */
  51. extern void RegisterFtpFd(/* ftpc, fd, O_RDWR | O_RDONLY, func */);
  52. extern void UnregisterFtpFd(/* ftpc, fd */);
  53. extern int ftpPrompt(/* ftpc */);
  54. extern void status0(/* str */);
  55. extern void alert0(/* str */);
  56. extern void sysError(/* str */);
  57.  
  58. /*
  59.  * Portable non-blocking I/O macros, I hope.
  60.  *   hp300 : From Andy.Linton@comp.vuw.ac.nz
  61.  *   sgi   : From amoss@cs.huji.ac.il
  62.  */
  63. #if defined(hp300)
  64.   /* Here's for BSD, maybe, but Sys5 can't tell this from EOF. */
  65. # include <fcntl.h>
  66. # define MAKE_NONBLOCKING(fd) fcntl(fd,F_SETFL,O_NDELAY)
  67. # define ITWOULDBLOCK EWOULDBLOCK
  68. #else
  69. #if defined(sgi)
  70. # include <fcntl.h>
  71. # define MAKE_NONBLOCKING(fd) fcntl(fd,F_SETFL,O_NONBLOCK)
  72. # define ITWOULDBLOCK EWOULDBLOCK
  73. #else
  74.   /* This is POSIX, the default, which uses EAGAIN. */
  75. # include <fcntl.h>
  76. # define MAKE_NONBLOCKING(fd) fcntl(fd,F_SETFL,O_NONBLOCK)
  77. # define ITWOULDBLOCK EAGAIN
  78. #endif
  79. #endif
  80. /*
  81.  * Networking includes from Brendan Kehoe (dirsend.c)
  82.  */
  83. #include <netdb.h>
  84. #include <sys/socket.h>
  85. #ifndef hpux
  86. # include <arpa/inet.h>
  87. #endif
  88. /* Interactive UNIX keeps some of the socket definitions in funny places.  */
  89. #ifdef ISC
  90. # include <net/errno.h>        /* I wonder about this... */
  91. #endif /* ISC */
  92.  
  93. #ifndef IPPORT_FTP
  94. #define IPPORT_FTP 21        /* From <netinet/in.h> */
  95. #endif
  96.  
  97. #define NewString(S)        strcpy(malloc(strlen(S)+1),S)
  98. #define ftpfilecmdstr(X)    ((X)==FTP_GET ? "GET" : "PUT")
  99.  
  100. /*
  101.  * Functions defined here:
  102.  */
  103. FtpContext *ftpNewContext();
  104. void ftpStart(),ftpAbort();
  105. void ftpFreeContext();
  106.  
  107. /* Initialization */
  108. static int ftpGetHostAndPort();
  109. /* Connection establishment */
  110. static void ftpConnectCallback();
  111. static int ftpGetSocket(),ftpConnect();
  112. /* Reading server replies */
  113. static void ftpInitReply(),ftpReplyCallback();
  114. /* Main FTP client state-machine */
  115. static void ftpProcessReply();
  116. static void ftpAlert(),ftpHandleErrorThenQuit(),ftpDone();
  117. static void ftpSendType(),ftpSendNextCwd();
  118. static int ftpSendPort();
  119. static void ftpSendGetPut();
  120. static int ftpAcceptDataConn(),ftpSetupLocalData();
  121. /* Data connection processing */
  122. static void ftpDataCallback();
  123. static void ftpReadData(),ftpWriteData(),ftpCleanupDataConn();
  124. /* Sending commands to the server */
  125. static void ftpStartSendingCmd(),ftpWriteCallback();
  126. static void ftpSendCmd(),ftpSendAbort();
  127. #ifdef DEBUG
  128. char *ftpstatestr();
  129. #endif
  130.  
  131. /*    -    -    -    -    -    -    -    -    */
  132. /* Exported functions: */
  133.  
  134. /*
  135.  * Returns a pointer to a new FtpContext for which the connection is
  136.  * started. The external function RegisterFtpFd() is used to register
  137.  * the control socket for subsequent processing by ftpProcess().
  138.  */
  139. FtpContext *
  140. ftpNewContext(hostname,user,pass,cwd,local_dir,type,stripcr,prompt,
  141.           filecmd,files,num_files,trace,done1,done)
  142. char *hostname,*user,*pass,*cwd,*local_dir;
  143. int type,stripcr,prompt,filecmd;
  144. char **files;
  145. int num_files;
  146. FtpTraceProc trace;
  147. FtpCallbackProc done1,done;
  148. {
  149.     FtpContext *ftpc;
  150.     int i;
  151.  
  152.     /* Initialize the data structure */
  153.     if ((ftpc=(FtpContext *)malloc(sizeof(FtpContext))) == NULL) {
  154.     fprintf(stderr,"ftpNewContext: malloc failed\n");
  155.     return(NULL);
  156.     }
  157.     /* User-specified stuff */
  158.     ftpc->hostname = NewString(hostname);
  159.     ftpc->user = NewString(user);
  160.     ftpc->pass = NewString(pass);
  161.     ftpc->cwd = NewString(cwd);
  162.     ftpc->wd = ftpc->cwd;
  163.     ftpc->local_dir = NewString(local_dir);
  164.     ftpc->type = type;
  165.     ftpc->stripcr = stripcr;
  166.     ftpc->prompt = prompt;
  167.     ftpc->filecmd = filecmd;
  168.     ftpc->num_files = num_files;
  169.     ftpc->files = (char **)calloc(num_files,sizeof(char *));
  170.     for (i=0; i < num_files; i++)
  171.     ftpc->files[i] = NewString(files[i]);
  172.     ftpc->this_file = -1;    /* preincrement later */
  173.     ftpc->this_size = 0;
  174.     ftpc->trace = trace;
  175.     ftpc->done1 = done1;
  176.     ftpc->done = done;
  177.     /* Default stuff */
  178.     ftpc->reply[0] = '\0';
  179.     ftpc->stripcr = 1;
  180.     /* Undefined stuff */
  181.     ftpc->ctrl = ftpc->data = ftpc->port = ftpc->local_fd = -1;
  182.     ftpc->state = ftpc->iostate = ftpc->reply_state = ftpc->saved_state = 0;
  183.     ftpc->retcode = ftpc->tmpcode = 0;
  184.     ftpc->cmd = NULL;
  185.     /* Go start the connection */
  186.     if (ftpGetHostAndPort(ftpc) < 0) {
  187.     ftpFreeContext(ftpc);
  188.     return(NULL);
  189.     }
  190.     ftpc->state = 0;
  191.     return(ftpc);
  192. }
  193.  
  194. void
  195. ftpFreeContext(ftpc)
  196. FtpContext *ftpc;
  197. {
  198.     int i;
  199.  
  200.     DEBUG1("ftpFreeContext: ftpc=0x%x\n",ftpc);
  201.     if (ftpc->ctrl != -1) {
  202.     UnregisterFtpFd(ftpc,ftpc->ctrl);
  203.     DEBUG1("ftpFreeContext: closing ctrl %d\n",ftpc->ctrl);
  204.     close(ftpc->ctrl);
  205.     }
  206.     if (ftpc->data != -1) {
  207.     UnregisterFtpFd(ftpc,ftpc->data);
  208.     DEBUG1("ftpFreeContext: closing data %d\n",ftpc->data);
  209.     close(ftpc->data);
  210.     }
  211.     if (ftpc->port != -1) {
  212.     DEBUG1("ftpFreeContext: closing port %d\n",ftpc->port);
  213.     close(ftpc->port);
  214.     }
  215.     if (ftpc->local_fd != -1) {
  216.     DEBUG1("ftpFreeContext: closing local_fd %d\n",ftpc->local_fd);
  217.     close(ftpc->local_fd);
  218.     }
  219.     if (ftpc->hostname)
  220.     free(ftpc->hostname);
  221.     if (ftpc->user)
  222.     free(ftpc->user);
  223.     if (ftpc->pass)
  224.     free(ftpc->pass);
  225.     if (ftpc->cwd)
  226.     free(ftpc->cwd);
  227.     if (ftpc->local_dir)
  228.     free(ftpc->local_dir);
  229.     if (ftpc->files) {
  230.     for (i=0; i < ftpc->num_files; i++)
  231.         if (ftpc->files[i]) free(ftpc->files[i]);
  232.     free((char *)(ftpc->files));
  233.     }
  234.     if (ftpc->h_addr_list)
  235.     free((char *)(ftpc->h_addr_list));
  236.     DEBUG0("ftpFreeContext: done\n");
  237. }
  238.  
  239. void
  240. ftpStart(ftpc)
  241. FtpContext *ftpc;
  242. {
  243.     DEBUG1("ftpStart: ftpc=0x%x\n",ftpc);
  244.     ftpc->state = FTPS_OPEN;
  245.     ftpConnectCallback(ftpc);        /* get things started */
  246.     DEBUG0("ftpStart: done\n");
  247. }
  248.  
  249. void
  250. ftpAbort(ftpc)
  251. FtpContext *ftpc;
  252. {
  253.     DEBUG1("ftpAbort: ftpc=0x%x\n",ftpc);
  254.     /*
  255.      * If we're aborting before any files transferred or after an
  256.      * abort or quit, just close down the whole connection.
  257.      */
  258.     if (ftpc->state < FTPS_READY || ftpc->state > FTPS_EOF) {
  259.     ftpDone(ftpc);
  260.     return;
  261.     }
  262.     /*
  263.      * Close the dataconn so we don't get swamped in select() while
  264.      * waiting for the response to the abort on the ctrlconn.
  265.      */
  266.     if (ftpc->data != -1) {
  267.     UnregisterFtpFd(ftpc,ftpc->data);
  268.     }
  269.     /* Then send the abort sequence */
  270.     ftpSendAbort(ftpc);
  271.     DEBUG0("ftpAbort: done\n");
  272. }
  273.  
  274. /*    -    -    -    -    -    -    -    -    */
  275. /* Initialization: */
  276. /*
  277.  * This function is called from ftpNewContext() to initialize the host
  278.  * address information in the FTPC.
  279.  */
  280. static int
  281. ftpGetHostAndPort(ftpc)
  282. FtpContext *ftpc;
  283. {
  284.     struct servent *serv;
  285.     struct hostent *host;
  286.     unsigned long hostaddr;
  287.     char msg[256];
  288.     int i;
  289.  
  290.     DEBUG2("ftpGetHostAndPort: ftpc=0x%x: \"%s\"\n",ftpc,ftpc->hostname);
  291.     /*
  292.      * Get ftp port
  293.      */
  294.     DEBUG0("ftpGetHostAndPort: getting ftp service port\n");
  295.     serv = getservbyname("ftp","tcp");
  296.     /* UCX needs 0 or -1 */
  297.     if (serv == (struct servent *)0 || serv == (struct servent *)-1) {
  298.     alert0("Can't find service 'ftp/tcp' in list of services -- using default port");
  299.     ftpc->servport = htons((unsigned short)IPPORT_FTP);
  300.     } else {
  301.     ftpc->servport = (unsigned short)serv->s_port;
  302.     }
  303.     DEBUG1("ftpGetHostAndPort: ftp service port is %d\n",ftpc->servport);
  304.     /*
  305.      * Get host address
  306.      */
  307.     sprintf(msg,"Getting address for host \"%.200s\"",ftpc->hostname);
  308.     status0(msg);
  309.     if ((host=gethostbyname(ftpc->hostname)) == NULL) {
  310.     DEBUG0("ftpGetHostAndPort: gethostbyname failed, trying inet_addr()\n");
  311.     /*
  312.      * If gethostbyname fails, then maybe we've been given an IP
  313.      * address directly. Let's see.
  314.      */
  315.     hostaddr = inet_addr(ftpc->hostname);
  316.     if (hostaddr == (unsigned long)-1) {
  317.         /*
  318.          * Nope - complete failure.
  319.          */
  320.         sprintf(msg,"Can't find address of host \"%.200s\"",
  321.             ftpc->hostname);
  322.         alert0(msg);
  323.         return(-1);
  324.     } else {
  325.         /*
  326.          * inet_addr succeeded, so we make a dummy array of hostaddrs.
  327.          */
  328.         ftpc->h_addr_list = (char **)calloc(2,sizeof(char *));
  329.         bcopy((char *)&hostaddr,ftpc->h_addr_list[0],sizeof(char *));
  330.         hostaddr = (unsigned long)0;
  331.         bcopy((char *)&hostaddr,ftpc->h_addr_list[1],sizeof(char *));
  332.         ftpc->this_addr = ftpc->h_addr_list-1; /* preincrement later */
  333.         DEBUG1("ftpGetHostAndPort: inet_addr returned %s\n",
  334.            inet_ntoa(*(struct in_addr*)(ftpc->h_addr_list[0])));
  335.         return(0);
  336.     }
  337.     } else {
  338.     /*
  339.      * If gethostbyname succeeeds, it fills in a list of addresses.
  340.      * We'll copy them into the ftpc.
  341.      */
  342.     for (i=0; host->h_addr_list[i]; i++)
  343.         /*EMPTY*/;
  344.     DEBUG1("ftpGetHostAndPort: gethostbynname returned %d addr(s)\n",i);
  345.     ftpc->h_addr_list = (char **)calloc(i+1,sizeof(char *));
  346.     for (i=0; host->h_addr_list[i]; i++) {
  347.         bcopy(host->h_addr_list[i],
  348.           (char *)(ftpc->h_addr_list+i),sizeof(char *));
  349.         DEBUG2("ftpGetHostAndPort: h_addr_list[%d] = %s\n",i,
  350.            inet_ntoa(*(struct in_addr*)(ftpc->h_addr_list+i)));
  351.     }
  352.     bzero((char *)(ftpc->h_addr_list+i),sizeof(char *));
  353.     ftpc->this_addr = ftpc->h_addr_list-1;    /* preincrement later */
  354.     return(0);
  355.     }
  356. }
  357.  
  358. /*    -    -    -    -    -    -    -    -    */
  359. /* Connection establishment: */
  360. /*
  361.  * This function goes through the list of addresses trying to connect to
  362.  * the server. It is initially called from ftpNewContext(), and subsequently
  363.  * whenever the ctrl socket it ready for writing (indicating connect()
  364.  * completed, possibly with an error).
  365.  */
  366. static void
  367. ftpConnectCallback(ftpc)
  368. FtpContext *ftpc;
  369. {
  370.     int retcode;
  371.     char msg[256];
  372.  
  373.     DEBUG2("ftpConnectCallback: ftpc=0x%x, state=%s\n",
  374.        ftpc,ftpstatestr(ftpc->state));
  375.   redo:
  376.     switch (ftpc->state) {
  377.       case FTPS_OPEN:
  378.     ftpc->this_addr += 1;            /* preincrement */
  379.     DEBUG1("ftpConnectCallback: OPEN: %s\n",
  380.            inet_ntoa(*(struct in_addr*)(ftpc->this_addr)));
  381.     if (*(ftpc->this_addr)) {        /* Not at last host */
  382.         if (ftpGetSocket(ftpc) < 0) {
  383.         ftpDone(ftpc);
  384.         } else {
  385.         /* Arrange to get called back if connect() would block */
  386.         RegisterFtpFd(ftpc,ftpc->ctrl,O_RDWR,ftpConnectCallback);
  387.         ftpc->state = FTPS_CONNECT;
  388.         goto redo;
  389.         }
  390.     } else {                /* All hosts failed */
  391.         sprintf(msg,"Couldn't connect to host \"%s\"",ftpc->hostname);
  392.         alert0(msg);
  393.         ftpDone(ftpc);
  394.     }
  395.     break;
  396.       case FTPS_CONNECT:
  397.     /* We previously registered either above or below */
  398.     UnregisterFtpFd(ftpc,ftpc->ctrl);
  399.     retcode = ftpConnect(ftpc);
  400.     if (retcode < 0) {
  401.         DEBUG1("ftpConnectCallback: ftpConnect failed, closing ctrl %d\n",
  402.            ftpc->ctrl);
  403.         close(ftpc->ctrl);
  404.         ftpc->ctrl = -1;
  405.         ftpc->state = FTPS_OPEN;        /* try next one */
  406.         goto redo;
  407.     } else if (retcode == 0) {
  408.         DEBUG0("ftpConnectCallback: ftpConnect would block\n");
  409.         /* Arrange to get called back when connect() won't block */
  410.         RegisterFtpFd(ftpc,ftpc->ctrl,O_RDWR,ftpConnectCallback);
  411.         ftpc->state = FTPS_CONNECT;        /* try again when ready */
  412.     } else {
  413.         DEBUG0("ftpConnectCallback: ftpConnect ok\n");
  414.         ftpInitReply(ftpc);            /* ok... */
  415.         ftpc->state = FTPS_CONNECTED;    /* move on */
  416.     }
  417.     break;
  418.       default:
  419.     fprintf(stderr,"ftpConnectCallback: unknown state %d\n",ftpc->state);
  420.     abort();
  421.     }
  422.     DEBUG0("ftpConnectCallback: done\n");
  423. }
  424.         
  425. /*    -    -    -    -    -    -    -    -    */
  426. /* Functions called by ftpConnectCallback(): */
  427. /*
  428.  * This function opens a new socket for the ctrl connection, and sets
  429.  * it up for non-blocking IO. Everything is setup for later connect().
  430.  */
  431. static int
  432. ftpGetSocket(ftpc)
  433. FtpContext *ftpc;
  434. {
  435.     DEBUG1("ftpGetSocket: addr=%s\n",
  436.        inet_ntoa(*(struct in_addr*)(ftpc->this_addr)));
  437.     /* Get a socket */
  438.     if ((ftpc->ctrl=socket(AF_INET,SOCK_STREAM,0)) < 0) {
  439.     sysError("socket(ftpGetSocket)");
  440.     return(-1);
  441.     }
  442.     DEBUG1("ftpGetSocket: socket() returned %d\n",ftpc->ctrl);
  443.     /* Setup socket for async i/o */
  444.     MAKE_NONBLOCKING(ftpc->ctrl);
  445.     /* Set up the address spec. */
  446.     bzero((char *)&(ftpc->saddr),sizeof(struct sockaddr_in));
  447.     ftpc->saddr.sin_family = AF_INET;
  448.     ftpc->saddr.sin_port = ftpc->servport;
  449.     ftpc->saddr.sin_addr = *((struct in_addr *)(ftpc->this_addr));
  450.     /* Ready to connect() */
  451.     DEBUG0("ftpGetSocket: done\n");
  452.     return(0);
  453. }
  454.  
  455. /*
  456.  * Calls connect(). Returns -1 if this host is botched, 0 if connect()
  457.  * would have blocked, or 1 if we should proceed. 
  458.  */
  459. static int
  460. ftpConnect(ftpc)
  461. FtpContext *ftpc;
  462. {
  463.     int retcode,addrlen;
  464.     char msg[256];
  465.  
  466.     sprintf(msg,"Connecting to %.200s (%s)",
  467.         ftpc->hostname,inet_ntoa(*(struct in_addr*)(ftpc->this_addr)));
  468.     status0(msg);
  469.     DEBUG0("ftpConnect: calling connect()...\n");
  470.     retcode = connect(ftpc->ctrl,(struct sockaddr *)&(ftpc->saddr),
  471.               sizeof(struct sockaddr_in));
  472.     if (retcode < 0 && errno == EINPROGRESS) {
  473.     DEBUG0("ftpConnect: connect() EINPROGRESS\n");
  474.     return(0);
  475.     } else if (retcode < 0 && errno != EISCONN) {
  476. #ifdef DEBUG
  477.     perror("ftpConnect: connect()");
  478. #endif
  479.     return(-1);
  480.     }
  481. #ifdef DEBUG
  482.       else if (retcode < 0 && errno == EISCONN)
  483.       fprintf(stderr,"ftpConnect: connect() EISCONN\n");
  484.       else
  485.       fprintf(stderr,"ftpConnect: connect() returned ok\n");
  486. #endif
  487.     /* Get name (address) of socket */
  488.     addrlen = sizeof(struct sockaddr_in);
  489.     retcode =
  490.     getsockname(ftpc->ctrl,(struct sockaddr *)&(ftpc->saddr),&addrlen);
  491.     if (retcode < 0) {
  492.     sysError("getsockname(ftpConnect)");
  493.     return(-1);
  494.     }
  495.     DEBUG1("ftpConnect: getsockname() returned %s\n",
  496.        inet_ntoa(ftpc->saddr.sin_addr));
  497.     return(1);
  498. }
  499.  
  500. /*    -    -    -    -    -    -    -    -    */
  501. /* Reading server replies: */
  502. /*
  503.  * This function must be called to initialize the reply in the FTPC and
  504.  * to ensure that the appropriate callback is registered to read it.
  505.  */
  506. static void
  507. ftpInitReply(ftpc)
  508. FtpContext *ftpc;
  509. {
  510.     DEBUG1("ftpInitReply: ftpc=0x%lx\n",ftpc);
  511.     ftpc->retcode = 0;
  512.     ftpc->reply_len = 0;
  513.     *(ftpc->reply) = '\0';
  514.     ftpc->reply_state = FTPS_REPLY_CODE;
  515.     /* We're expecting a reply, so register ctrl for reading only */
  516.     RegisterFtpFd(ftpc,ftpc->ctrl,O_RDONLY,ftpReplyCallback);
  517.     DEBUG0("ftpInitReply: done\n");
  518. }
  519.  
  520. /*
  521.  * This function is the low-level reply parser. It reads and
  522.  * parses reply messages from the server, handles TELNET commands,
  523.  * and translate codes in the reply prefix into integers. It stores the
  524.  * reply code in ftpc->retcode and puts the text of the reply in ftpc->reply.
  525.  * It is called whenever the ctrlconn is ready for reading and we are
  526.  * expecting a reply (ie., after ftpInitReply()). Once the reply is
  527.  * complete, we call ftpProcessReply().
  528.  */
  529. static void
  530. ftpReplyCallback(ftpc)
  531. FtpContext *ftpc;
  532. {
  533.     int n;
  534.     unsigned char c,cc;
  535.  
  536.     for (;;) {
  537. /*
  538.     DEBUG2("ftpReplyCallback: ftpc=0x%x, reply_state=%s\n",
  539.            ftpc,ftpstatestr(ftpc->reply_state));
  540. */
  541.     n = read(ftpc->ctrl,&c,1);        /* Read a byte */
  542.     if (n < 0 && errno != ITWOULDBLOCK) {        /* Error */
  543.         sysError("read(ftpReplyCallback)");
  544.         DEBUG0("ftpReplyCallback: error reading ctrlconn\n");
  545.         ftpDone(ftpc);
  546.         return;
  547.     } else if (n < 0 && errno == ITWOULDBLOCK) {    /* Would block */
  548.         /*DEBUG0("ftpReplyCallback: ctrlconn would block\n");*/
  549.         return;
  550.     } else if (n == 0) {            /* EOF */
  551.         *(ftpc->reply+ftpc->reply_len) = '\0';
  552.         /* Could check against current retcode, if any... */
  553.         if (ftpc->state == FTPS_QUIT)
  554.         ftpc->retcode = FTP_SERVICE_CLOSING;
  555.         else
  556.         ftpc->retcode = FTP_SERVICE_UNAVAILABLE;
  557.         DEBUG1("ftpReplyCallback: EOF: retcode = %d\n",ftpc->retcode);
  558.         /* Reply is done, go process it */
  559.         break;
  560.     }
  561.     /*
  562.      * Otherwise, we got something, process it.
  563.      */
  564.     if (c == IAC) {            /* Telnet IAC */
  565.         ftpc->saved_state = ftpc->reply_state;
  566.         ftpc->reply_state = FTPS_REPLY_IAC1;
  567.         DEBUG1("ftpReplyCallback: IAC (saved_state=%d)\n",ftpc->saved_state);
  568.         continue;
  569.     } else if (c == '\r') {        /* Skip <CR>, is this ok? */
  570.         continue;
  571.     }
  572.     /*
  573.      * We got something that's not IAC, process it.
  574.      */
  575.     /*DEBUG2("ftpReplyCallback: `%c' (%d)\n",c,c);*/
  576.     switch (ftpc->reply_state) {
  577.       case FTPS_REPLY_IAC1:
  578.         DEBUG1("ftpReplyCallback: IAC %c",c);
  579.         ftpc->reply_state = FTPS_REPLY_IAC2;
  580.         break;
  581.       case FTPS_REPLY_IAC2:
  582.         switch (c) {
  583.           case WILL:
  584.           case WONT: c = DONT;
  585.              break;
  586.           case DO:
  587.           case DONT: c = WONT;
  588.              break;
  589.           default:
  590.         DEBUG0(" (ignored)\n");
  591.         continue;
  592.         }
  593.         DEBUG1(", reply %c\n",c);
  594.         cc = IAC;
  595.         write(ftpc->ctrl,&cc,1);
  596.         write(ftpc->ctrl,&c,1);
  597.         ftpc->reply_state = FTPS_REPLY_IAC3;
  598.         break;
  599.       case FTPS_REPLY_IAC3:
  600.         write(ftpc->ctrl,&c,1);
  601.         ftpc->reply_state = ftpc->saved_state;
  602.         DEBUG1("ftpReplyCallback: IAC done, restored state = %d\n",
  603.            ftpc->reply_state);
  604.         break;
  605.       case FTPS_REPLY_CODE:
  606.         if (c < '0' || c > '9') {
  607.         *(ftpc->reply+ftpc->reply_len) = '\0';
  608.         DEBUG1("ftpReplyCallback: CODE: retcode = %d\n",ftpc->retcode);
  609.         goto done;
  610.         }
  611.         *(ftpc->reply+ftpc->reply_len++) = c;
  612.         ftpc->retcode = ftpc->retcode * 10 + c - '0';
  613.         /*DEBUG1("ftpReplyCallback: retcode now %d\n",ftpc->retcode);*/
  614.         if (ftpc->retcode >= 100)
  615.         ftpc->reply_state = FTPS_REPLY_CONT;
  616.         break;
  617.       case FTPS_REPLY_CONT:
  618.         /* we reach here after we finished reading the code or when we
  619.          * struck a line beginning with at least three digits, check if
  620.          * this is the last line of the reply
  621.          */
  622.         *(ftpc->reply+ftpc->reply_len++) = c;
  623.         if (c == '-') {
  624. /*
  625.         DEBUG0("ftpReplyCallback: continuation\n");
  626. */
  627.         ftpc->reply_state = FTPS_REPLY_MORE;
  628.         } else {
  629. /*
  630.         DEBUG0("ftpReplyCallback: final line\n");
  631. */
  632.         ftpc->reply_state = FTPS_REPLY_LAST;
  633.         }
  634.         break;
  635.       case FTPS_REPLY_LAST:
  636.         if (c == '\n') {
  637.         *(ftpc->reply+ftpc->reply_len) = '\0';
  638. /*
  639.         DEBUG1("ftpReplyCallback: LAST: retcode = %d\n",ftpc->retcode);
  640. */
  641.         goto done;
  642.         } else {
  643.         *(ftpc->reply+ftpc->reply_len++) = c;
  644. /*
  645.         DEBUG3("ftpReplyCallback: LAST: %03d: \"%.*s\"\n",
  646.              ftpc->reply_len,ftpc->reply_len,ftpc->reply);
  647. */
  648.         }
  649.         break;
  650.       case FTPS_REPLY_MORE:
  651.         *(ftpc->reply+ftpc->reply_len++) = c;
  652. /*
  653.         DEBUG3("ftpReplyCallback: MORE: %03d: \"%.*s\"\n",
  654.            ftpc->reply_len,ftpc->reply_len,ftpc->reply);
  655. */
  656.         if (c == '\n') {
  657.         ftpc->tmpcode = 0;
  658.         ftpc->reply_state = FTPS_REPLY_CHCK;
  659.         }
  660.         break;
  661.       case FTPS_REPLY_CHCK:
  662.         if (c < '0' || c > '9') {
  663.         *(ftpc->reply+ftpc->reply_len++) = c;
  664. /*
  665.         DEBUG3("ftpReplyCallback: CHCK: %03d: \"%.*s\"\n",
  666.                ftpc->reply_len,ftpc->reply_len,ftpc->reply);
  667. */
  668.         ftpc->reply_state = FTPS_REPLY_MORE;
  669.         } else {
  670.         ftpc->tmpcode = ftpc->tmpcode * 10 + c - '0';
  671.         if (ftpc->tmpcode >= 100) {
  672.             if (ftpc->tmpcode != ftpc->retcode)
  673.             ftpc->reply_state = FTPS_REPLY_MORE;
  674.             else
  675.             ftpc->reply_state = FTPS_REPLY_CONT;
  676.         }
  677.         }
  678.         break;
  679.       default:
  680.         fprintf(stderr,"ftpReplyCallback: unknown reply_state %d\n",
  681.             ftpc->reply_state);
  682.         abort();
  683.     }
  684.     }
  685.     /* if we get here, the reply is complete */
  686.   done:
  687.     UnregisterFtpFd(ftpc,ftpc->ctrl);
  688.     if (ftpc->trace) {
  689.     (*(ftpc->trace))(ftpc,0,ftpc->reply);
  690.     }
  691.     ftpProcessReply(ftpc);
  692. }
  693.  
  694. /*    -    -    -    -    -    -    -    -    */
  695. /*
  696.  * This function implements the high-level FTP protocol using the state
  697.  * field of the FTPC. It is called from ftpReplyCallback() once we've read
  698.  * a reply and need to process it.
  699.  */
  700. static void
  701. ftpProcessReply(ftpc)
  702. FtpContext *ftpc;
  703. {
  704.     char cmd[256];
  705.  
  706.     DEBUG3("ftpProcessReply: ftpc=0x%x, state=%s, retcode=%d\n",
  707.        ftpc,ftpstatestr(ftpc->state),ftpc->retcode);
  708.   redo:
  709.     switch (ftpc->state) {
  710.       case FTPS_CONNECTED:
  711.     if (ftpc->retcode == FTP_SERVICE_RDY_TIME) {    /* delay NNN minutes */
  712.         DEBUG0("ftpProcessReply: server not ready\n");
  713.         ftpAlert(ftpc);
  714.     } else if (ftpc->retcode == FTP_SERVICE_RDY_USER) {/* ready for USER */
  715.         sprintf(cmd,"USER %s",ftpc->user);
  716.         status0(cmd);
  717.         ftpc->state = FTPS_USER;
  718.         ftpSendCmd(ftpc,cmd);
  719.     } else {                /* FTP_SERVICE_UNAVAILABLE */
  720.         ftpAlert(ftpc);
  721.         ftpDone(ftpc);
  722.     }
  723.     break;
  724.       case FTPS_USER:
  725.     if (ftpc->retcode == FTP_LOGIN_OK) {    /* USER ok, no PASS needed */
  726.         ftpc->retcode = FTP_FILE_ACTION_OK;
  727.         ftpc->state = FTPS_CWD;
  728.         goto redo;
  729.     } else if (ftpc->retcode == FTP_LOGIN_NEED_PASSWD) {    /* USER ok   */
  730.         sprintf(cmd,"PASS %s",ftpc->pass);            /* need PASS */
  731.         status0(cmd);
  732.         ftpc->state = FTPS_PASS;
  733.         ftpSendCmd(ftpc,cmd);
  734.     } else {                /* ACCT needed or error */
  735.         ftpHandleErrorThenQuit(ftpc);
  736.     }
  737.     break;
  738.       case FTPS_PASS:
  739.     if (ftpc->retcode == FTP_LOGIN_OK) {    /* PASS ok, ready to go */
  740.         ftpc->retcode = FTP_FILE_ACTION_OK;
  741.         ftpc->state = FTPS_CWD;
  742.         goto redo;
  743.     } else {                /* ACCT needed or error */
  744.         ftpHandleErrorThenQuit(ftpc);
  745.     }
  746.     break;
  747.       case FTPS_CWD:
  748.     /* can come here direct from USER or PASS also... */
  749.     if (ftpc->retcode == FTP_FILE_ACTION_OK) {    /* last CWD ok */
  750.         if (ftpc->wd == NULL || *(ftpc->wd) == '\0') { /* CWD done */
  751.         ftpc->state = FTPS_TYPE;
  752.         ftpSendType(ftpc);
  753.         } else {                /* send next part of CWD */
  754.         ftpc->state = FTPS_CWD;
  755.         ftpSendNextCwd(ftpc);
  756.         }
  757.     } else {                /* last CWD failed */
  758.         ftpHandleErrorThenQuit(ftpc);
  759.     }
  760.     break;
  761.       case FTPS_TYPE:
  762.     if (ftpc->retcode == FTP_COMMAND_OK) {    /* TYPE ok */
  763.         ftpc->retcode = FTP_FILE_ACTION_OK;
  764.         ftpc->state = FTPS_READY;
  765.         goto redo;
  766.     } else {                /* TYPE failed */
  767.         ftpHandleErrorThenQuit(ftpc);
  768.     }
  769.     break;
  770.       case FTPS_READY:
  771.     /* can get here from TYPE or EOF */
  772.     ftpc->this_file += 1;
  773.     if (ftpc->this_file < ftpc->num_files) {  /* files left to transfer */
  774.         sprintf(cmd,"File %s?",ftpc->files[ftpc->this_file]);
  775.         status0(cmd);
  776.         if (ftpc->prompt && !ftpPrompt(ftpc)) {
  777.         goto redo;
  778.         }
  779.         if (ftpSendPort(ftpc) == 0) {    /*   PORT ok locally */
  780.         ftpc->state = FTPS_PORT;
  781.         } else {                /*   PORT failed locally */
  782.         ftpc->state = FTPS_QUIT;    /*     bag the whole thing */
  783.         ftpSendCmd(ftpc,"QUIT");
  784.         }
  785.     } else {                /* no files left to transfer */
  786.         DEBUG0("ftpProcessReply: no more files\n");
  787.         ftpc->state = FTPS_QUIT;
  788.         ftpSendCmd(ftpc,"QUIT");
  789.     }
  790.     break;
  791.       case FTPS_PORT:
  792.     if (ftpc->retcode == FTP_COMMAND_OK) {    /* PORT command ok */
  793.         ftpc->state = FTPS_GETPUT;
  794.         ftpSendGetPut(ftpc);
  795.     } else {                /* PORT failed */
  796.         if (ftpc->port >= 0) {
  797.         DEBUG1("ftpProcessReply: closing port %d\n",ftpc->port);
  798.         close(ftpc->port);
  799.         ftpc->port = -1;
  800.         }
  801.         ftpHandleErrorThenQuit(ftpc);
  802.     }
  803.     break;
  804.       case FTPS_GETPUT:
  805.     if (FTP_REPLY_PRELIM(ftpc->retcode)) { /* dataconn ready */
  806.         status0(ftpc->reply+4);
  807.         if (sscanf(ftpc->reply,"%*[^(](%d bytes)",&(ftpc->this_size)) != 1)
  808.         ftpc->this_size = 0;
  809.         if (ftpAcceptDataConn(ftpc) < 0) {        /*  local failure */
  810.         if (ftpc->port != -1) {            /*    give up */
  811.             DEBUG1("ftpProcessReply: closing port %d\n",ftpc->port);
  812.             close(ftpc->port);
  813.             ftpc->port = -1;
  814.         }
  815.         ftpc->state = FTPS_QUIT;
  816.         ftpSendCmd(ftpc,"QUIT");
  817.         } else if (ftpSetupLocalData(ftpc) < 0) {    /*  local failure */
  818.         UnregisterFtpFd(ftpc,ftpc->data);    /*    don't give up */
  819.         if (ftpc->data != -1) {
  820.             DEBUG1("ftpProcessReply: closing data %d\n",ftpc->data);
  821.             close(ftpc->data);
  822.             ftpc->data = -1;
  823.         }
  824.         ftpc->state = FTPS_READY;        /*    do next file */
  825.         goto redo;
  826.         } else {                /* all ok locally */
  827.         ftpc->state = FTPS_TRANSFER;
  828.         }
  829.     } else if (ftpc->retcode == FTP_FILE_UNAVAILABLE ||    /* datacon */
  830.            ftpc->retcode == FTP_ACTION_NOT_TAKEN) {    /* failed  */
  831.         if (ftpc->port != -1) {                /*  minor  */
  832.         DEBUG1("ftpProcessReply: closing port %d\n",ftpc->port);
  833.         close(ftpc->port);                /*  error */
  834.         ftpc->port = -1;
  835.         }
  836.         ftpAlert(ftpc);                /* error msg */
  837.         ftpc->state = FTPS_READY;        /* next file */
  838.         goto redo;
  839.     } else {
  840.         ftpHandleErrorThenQuit(ftpc);    /* real error */
  841.     }
  842.     break;
  843.       case FTPS_TRANSFER:
  844.     /* Shouldn't get here since dataconn is separately managed */
  845.     DEBUG0("ftpProcessReply: called in state TRANSFER!\n");
  846.     break;
  847.       case FTPS_EOF:
  848.     /* Called when the dataconn is closed, for whatever reason */
  849.     if (!FTP_REPLY_OK(ftpc->retcode)) {    /* dataconn closed error */
  850.         ftpAlert(ftpc);
  851.     }
  852.     ftpc->state = FTPS_READY;
  853.     goto redo;
  854.       case FTPS_QUIT:
  855.     if (ftpc->retcode != FTP_SERVICE_CLOSING) {    /* error */
  856.         ftpAlert(ftpc);
  857.     }
  858.     ftpDone(ftpc);                /* close, deallocate */
  859.     break;
  860.       case FTPS_ABORT:
  861.     if (ftpc->retcode == 552) {        /* NIC-style abort */
  862.         ftpc->state = FTPS_ABORT;        /* get another reply */
  863.     } else {
  864.         if (ftpc->retcode != FTP_DATA_CLOSE_ABORT) { /* 426      */
  865.         ftpAlert(ftpc);                 /* expected */
  866.         }
  867.         ftpc->state = FTPS_ABORT2;        /* get real reply */
  868.         ftpInitReply(ftpc);
  869.     }
  870.     break;
  871.       case FTPS_ABORT2:
  872.     if (!FTP_REPLY_OK(ftpc->retcode)) {    /* abort error */
  873.         ftpAlert(ftpc);
  874.     }
  875.     ftpCleanupDataConn(ftpc);
  876.     ftpc->state = FTPS_READY;
  877.     goto redo;
  878.       default:
  879.     fprintf(stderr,"ftpProcessReply: unknown state: %d\n",ftpc->state);
  880.     abort();
  881.     }
  882.     DEBUG0("ftpProcessReply: done\n");
  883. }
  884.  
  885. /*
  886.  * This function just puts up an FTP error message.
  887.  */
  888. static void
  889. ftpAlert(ftpc)
  890. FtpContext *ftpc;
  891. {
  892.     char buf[256],*msg;
  893.     int len;
  894.  
  895.     msg = ftpc->reply+4;        /* skip code */
  896.     len = strlen(msg);
  897.     if (len < 230) {
  898.     sprintf(buf,"FTP Error %d:\n %s",ftpc->retcode,msg);
  899.     } else {
  900.     sprintf(buf,"FTP Error %d: ...\n %.230s",ftpc->retcode,msg+len-230);
  901.     }
  902.     alert0(buf);
  903. }
  904.  
  905. /*
  906.  * This prints an error message, then sends the QUIT command (or
  907.  * calls ftpDone() if the server shut down).
  908.  */
  909. static void
  910. ftpHandleErrorThenQuit(ftpc)
  911. FtpContext *ftpc;
  912. {
  913.     ftpAlert(ftpc);
  914.     /* Don't bother with QUIT if remote host closing down */
  915.     if (ftpc->retcode == FTP_SERVICE_UNAVAILABLE) {
  916.     ftpDone(ftpc);
  917.     } else {
  918.     status0("Quitting...");
  919.     ftpc->state = FTPS_QUIT;
  920.     ftpSendCmd(ftpc,"QUIT");
  921.     }
  922. }
  923.  
  924. /*
  925.  * This calls the done function (or ftpFreeContext() if none is defined
  926.  * for FTPC. All connections should pass through here to be cleaned up
  927.  * regardless of how they got here.
  928.  */
  929. static void
  930. ftpDone(ftpc)
  931. FtpContext *ftpc;
  932. {
  933.     DEBUG1("ftpDone: ftpc=0x%x\n",ftpc);
  934.     if (ftpc->done != NULL) {
  935.     (*(ftpc->done))(ftpc);
  936.     } else {
  937.     ftpFreeContext(ftpc);
  938.     }
  939.     DEBUG0("ftpDone: done\n");
  940. }
  941.  
  942. /*    -    -    -    -    -    -    -    -    */
  943. /* Functions called by ftpProcessReply(): */
  944.  
  945. static void
  946. ftpSendType(ftpc)
  947. FtpContext *ftpc;
  948. {
  949.     char cmd[16];
  950.  
  951.     DEBUG2("ftpSendType: ftpc=0x%x, type=%d\n",ftpc,ftpc->type);
  952.     ftpc->state = FTPS_TYPE;
  953.     switch (ftpc->type) {
  954.       case TYPE_A:
  955.      sprintf(cmd,"TYPE A");
  956.     break;
  957.       case TYPE_E:
  958.     sprintf(cmd,"TYPE E");
  959.     break;
  960.       case TYPE_I:
  961.     sprintf(cmd,"TYPE I");
  962.     break;
  963.       default:
  964.     sprintf(cmd,"TYPE L %d",(char *)ftpc->type);
  965.     }
  966.     status0(cmd);
  967.     ftpSendCmd(ftpc,cmd);
  968.     DEBUG0("ftpSendType: done\n");
  969. }
  970.  
  971. static void
  972. ftpSendNextCwd(ftpc)
  973. FtpContext *ftpc;
  974. {
  975.     char *slash,cmd[256];
  976.  
  977.     DEBUG2("ftpSendNextCwd: ftpc=0x%x, wd=\"%s\"\n",ftpc,ftpc->wd);
  978.     if (*(ftpc->wd) == '/') {    /* Leading slash treated specially... */
  979.     ftpc->state = FTPS_CWD;
  980.     ftpSendCmd(ftpc,"CWD /");
  981.     ftpc->wd += 1;
  982.     } else {            /* Normal case */
  983.     if ((slash=index(ftpc->wd,'/')) != NULL) {
  984.         *slash = '\0';
  985.     }
  986.     sprintf(cmd,"CWD %s",ftpc->wd);
  987.     status0(cmd);
  988.     ftpc->state = FTPS_CWD;
  989.     ftpSendCmd(ftpc,cmd);
  990.     if (slash) {
  991.         ftpc->wd = slash+1;
  992.     } else {
  993.         /* set wd to end of string */
  994.         while (*(ftpc->wd) != '\0')
  995.         ftpc->wd += 1;
  996.     }
  997.     }
  998.     DEBUG0("ftpSendNextCwd: done\n");
  999. }
  1000.  
  1001. /*
  1002.  * Performs the PORT command. The new port is saved in f->port.
  1003.  * Returns < 0 if some local error, otherwise 0 if we got to sending
  1004.  * the command.
  1005.  */
  1006. static int
  1007. ftpSendPort(ftpc)
  1008. FtpContext *ftpc;
  1009. {
  1010.     char cmd[64];
  1011.     struct sockaddr_in addr;
  1012.     int addrlen;
  1013.  
  1014.     DEBUG1("ftpSendPort: ftpc=0x%lx\n",ftpc);
  1015.     if ((ftpc->port=socket(AF_INET,SOCK_STREAM,0)) < 0) {
  1016.     sysError("socket(ftpSendPort)");
  1017.     DEBUG0("ftpSendPort: returning -1\n");
  1018.     return(-1);
  1019.     }
  1020.     DEBUG1("ftpSendPort: socket() returned %d\n",ftpc->port);
  1021.     addr = ftpc->saddr;
  1022.     addr.sin_port = 0;
  1023.     if (bind(ftpc->port,(struct sockaddr *)&addr,
  1024.          sizeof(struct sockaddr_in)) < 0) {
  1025.     sysError("bind(ftpSendPort)");
  1026.     DEBUG1("ftpSendPort: closing port %d\n",ftpc->port);
  1027.     close(ftpc->port);
  1028.     DEBUG0("ftpSendPort: returning -1\n");
  1029.     return(-1);
  1030.     }
  1031.     DEBUG0("ftpSendPort: bind() succeeded\n");
  1032.     if (listen(ftpc->port,1) < 0) {
  1033.     sysError("listen(ftpSendPort)");
  1034.     DEBUG1("ftpSendPort: closing port %d\n",ftpc->port);
  1035.     close(ftpc->port);
  1036.     DEBUG0("ftpSendPort: returning -1\n");
  1037.     return(-1);
  1038.     }
  1039.     DEBUG0("ftpSendPort: listen() succeeded\n");
  1040.     addrlen = sizeof(struct sockaddr_in);
  1041.     if (getsockname(ftpc->port,(struct sockaddr *)&addr,&addrlen) < 0) {
  1042.     sysError("getsockname(ftpSendPort)");
  1043.     DEBUG1("ftpSendPort: closing port %d\n",ftpc->port);
  1044.     close(ftpc->port);
  1045.     DEBUG0("ftpSendPort: returning -1\n");
  1046.     return(-1);
  1047.     }
  1048.     DEBUG2("ftpSendPort: PORT address: %s; port: %d\n",
  1049.        inet_ntoa(addr.sin_addr),addr.sin_port);
  1050.     sprintf(cmd,"PORT %d,%d,%d,%d,%d,%d",
  1051.         (int)((unsigned char *)&addr.sin_addr.s_addr)[0],
  1052.         (int)((unsigned char *)&addr.sin_addr.s_addr)[1],
  1053.         (int)((unsigned char *)&addr.sin_addr.s_addr)[2],
  1054.         (int)((unsigned char *)&addr.sin_addr.s_addr)[3],
  1055.         (int)((unsigned char *)&addr.sin_port)[0],
  1056.         (int)((unsigned char *)&addr.sin_port)[1]);
  1057.     status0(cmd);
  1058.     ftpc->state = FTPS_PORT;
  1059.     ftpSendCmd(ftpc,cmd);
  1060.     DEBUG0("ftpSendPort: returning 0\n");
  1061.     return(0);
  1062. }
  1063.  
  1064.  
  1065. /*
  1066.  * After PORT is ok, send GET or PUT to open the dataconn.
  1067.  */
  1068. static void
  1069. ftpSendGetPut(ftpc)
  1070. FtpContext *ftpc;
  1071. {
  1072.     char cmd[MAXPATHLEN];
  1073.  
  1074.     DEBUG2("ftpSendGetPut: ftpc=0x%x, fcmd=%d\n",ftpc,ftpc->filecmd);
  1075.     ftpc->state = FTPS_GETPUT;
  1076.     switch (ftpc->filecmd) {
  1077.       case FTP_GET:
  1078.     sprintf(cmd,"RETR %s",ftpc->files[ftpc->this_file]);
  1079.     break;
  1080.       case FTP_PUT:
  1081.     sprintf(cmd,"STOR %s",ftpc->files[ftpc->this_file]);
  1082.     break;
  1083.     }
  1084.     status0(cmd);
  1085.     ftpSendCmd(ftpc,cmd);
  1086.     DEBUG0("ftpSendGetPut: done\n");
  1087. }
  1088.  
  1089. /*
  1090.  * After GET/PUT, accepts the connection from the PORT and closes the
  1091.  * the PORT. Returns the dataconn fd (also in ftpc->data).
  1092.  */
  1093. static int
  1094. ftpAcceptDataConn(ftpc)
  1095. FtpContext *ftpc;
  1096. {
  1097.     int datacon,addrlen;
  1098.     struct sockaddr_in addr;
  1099.  
  1100.     DEBUG1("ftpAcceptDataConn: fcmd %d successful\n",ftpc->filecmd);
  1101.     addrlen = sizeof(struct sockaddr_in);
  1102.     if ((datacon=accept(ftpc->port,(struct sockaddr *)&addr,&addrlen)) < 0) {
  1103.     sysError("accept(ftpAcceptDataConn)");
  1104.     DEBUG0("ftpAcceptDataConn: returning -1\n");
  1105.     return(-1);
  1106.     }
  1107.     DEBUG1("ftpAcceptDataConn: closing port %d\n",ftpc->port);
  1108.     close(ftpc->port);
  1109.     ftpc->port = -1;
  1110.     DEBUG1("ftpAcceptDataConn: registering dataconn %d\n",datacon);
  1111.     ftpc->data = datacon;
  1112.     MAKE_NONBLOCKING(ftpc->data);
  1113.     RegisterFtpFd(ftpc,ftpc->data,O_RDONLY,ftpDataCallback);
  1114.     DEBUG1("ftpAcceptDataConn: returning dataconn = %d\n",datacon);
  1115.     return(datacon);
  1116. }
  1117.  
  1118. /*
  1119.  * Once the remote file is ready for transfer through the dataconn, this
  1120.  * function sets up the local end. Returns -1 on error, otherwise 0.
  1121.  */
  1122. static int
  1123. ftpSetupLocalData(ftpc)
  1124. FtpContext *ftpc;
  1125. {
  1126.     char filename[MAXPATHLEN];
  1127.  
  1128.     DEBUG1("ftpSetupLocalData: ftpc=0x%x\n",ftpc);
  1129.     if (ftpc->local_dir && *(ftpc->local_dir)) {
  1130.     sprintf(filename,"%s/%s",ftpc->local_dir,ftpc->files[ftpc->this_file]);
  1131.     } else {
  1132.     strcpy(filename,ftpc->files[ftpc->this_file]);
  1133.     }
  1134.     DEBUG1("ftpSetupLocalData: opening \"%s\"\n",filename);
  1135.     if (ftpc->filecmd == FTP_GET) {
  1136.     ftpc->local_fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0644);
  1137.     } else {
  1138.     ftpc->local_fd = open(filename,O_RDONLY,0);
  1139.     }
  1140.     if (ftpc->local_fd < 0) {
  1141.     sysError(filename);
  1142.     DEBUG0("ftpSetupLocalData: returning -1\n");
  1143.     return(-1);
  1144.     }
  1145.     DEBUG1("ftpSetupLocalData: got local fd = %d\n",ftpc->local_fd);
  1146.     ftpc->num_bytes = 0;
  1147.     DEBUG0("ftpSetupLocalData: returning 0\n");
  1148.     return(0);
  1149. }
  1150.  
  1151. /*    -    -    -    -    -    -    -    -    */
  1152. /* Data connection processing: */
  1153. /*
  1154.  * This function is called whenever the dataconn is ready for read/write.
  1155.  */
  1156. static void
  1157. ftpDataCallback(ftpc)
  1158. FtpContext *ftpc;
  1159. {
  1160.     DEBUG2("ftpDataCallback: ftpc=0x%x, fcmd=%d\n",ftpc,ftpc->filecmd);
  1161.     switch (ftpc->filecmd) {
  1162.       case FTP_GET:
  1163.     ftpReadData(ftpc);
  1164.     break;
  1165.       case FTP_PUT:
  1166.     ftpWriteData(ftpc);
  1167.     break;
  1168.       default:
  1169.     fprintf(stderr,"ftpDataCallback: unknown cmd: %d\n",ftpc->filecmd);
  1170.     abort();
  1171.     }
  1172.     DEBUG0("ftpDataCallback: done\n");
  1173. }
  1174.  
  1175. /*
  1176.  * This function is called to read from the dataconn and write to the
  1177.  * local_fd. On EOF, it sets the state field to FTPS_EOF and calls
  1178.  * ftpInitReply() to continue the session. On error, it calls
  1179.  * ftpSendAbort() and then ftpInitReply(). Otherwise the transfer
  1180.  * is ongoing, so it doesn't do anything.
  1181.  */
  1182. static void
  1183. ftpReadData(ftpc)
  1184. FtpContext *ftpc;
  1185. {
  1186.     char buf[BUFSIZ*4],*s,*t,msg[256];
  1187.     int nread,i;
  1188.  
  1189.     DEBUG0("ftpReadData: reading\n");
  1190.     /* Read one chunk of stuff and write it locally (don't loop or
  1191.      * X won't get a chance to dispatch).
  1192.      */
  1193.     nread = read(ftpc->data,buf,sizeof(buf));
  1194.     if (nread > 0) {
  1195.     /* Strip CR's if necessary */
  1196.     if (ftpc->type == TYPE_A && ftpc->stripcr) {
  1197.         DEBUG0("ftpReadData: stripping <CR>\n");
  1198.         for (s=t=buf,i=nread; i--; ++s) {
  1199.         if (*s != '\r')
  1200.             *t++ = *s;
  1201.         }
  1202.     }
  1203.     /* Write locally */
  1204.     if (write(ftpc->local_fd,buf,nread) != nread) {
  1205.         /* Error when writing */
  1206.         sysError("write(ftpReadData)");
  1207.         ftpCleanupDataConn(ftpc);
  1208.         ftpSendAbort(ftpc);
  1209.         ftpInitReply(ftpc);
  1210.     } else {
  1211.         /* write was ok */
  1212.         ftpc->num_bytes += nread;
  1213.         DEBUG1("ftpReadData: total = %d\n",ftpc->num_bytes);
  1214.         if (ftpc->this_size != 0) {
  1215.         sprintf(msg,"%.200s: %d/%d",ftpc->files[ftpc->this_file],
  1216.             (char *)(ftpc->this_size),(char *)(ftpc->num_bytes));
  1217.         } else {
  1218.         sprintf(msg,"%.200s: %d bytes",ftpc->files[ftpc->this_file],
  1219.             (char *)(ftpc->num_bytes));
  1220.         }
  1221.         status0(msg);
  1222.     }
  1223.     } else if (nread < 0 && errno == ITWOULDBLOCK) {
  1224.     /* Nothing more ready now, keep waiting */
  1225.     DEBUG0("ftpReadData: dataconn would block\n");
  1226.     } else if (nread < 0) {
  1227.     /* Error when reading */
  1228.     sysError("read(ftpReadData)");
  1229.     DEBUG0("ftpReadData: error reading dataconn\n");
  1230.     ftpCleanupDataConn(ftpc);
  1231.     ftpSendAbort(ftpc);
  1232.     ftpInitReply(ftpc);
  1233.     } else if (nread == 0) {
  1234.     /* We got EOF on remote file. */
  1235.     DEBUG0("ftpReadData: EOF on dataconn\n");
  1236.     ftpCleanupDataConn(ftpc);
  1237.     if (ftpc->done1 != NULL)
  1238.         (*(ftpc->done1))(ftpc);
  1239.     ftpc->state = FTPS_EOF;
  1240.     ftpInitReply(ftpc);
  1241.     }
  1242. }
  1243.  
  1244. /*ARGSUSED*/
  1245. static void
  1246. ftpWriteData(ftpc)
  1247. FtpContext *ftpc;
  1248. {
  1249.     /*EMPTY*/
  1250. }
  1251.  
  1252. static void
  1253. ftpCleanupDataConn(ftpc)
  1254. FtpContext *ftpc;
  1255. {
  1256.     /* Note: We may have already unregistered, eg, in ftpAbort(). */
  1257.     if (ftpc->state != FTPS_ABORT2)
  1258.     UnregisterFtpFd(ftpc,ftpc->data);
  1259.     if (ftpc->data != -1) {
  1260.     DEBUG1("ftpCleanupDataConn: closing data %d\n",ftpc->data);
  1261.     close(ftpc->data);
  1262.     ftpc->data = -1;
  1263.     }
  1264.     if (ftpc->local_fd != -1) {
  1265.     DEBUG1("ftpCleanupDataConn: closing local_fd %d\n",ftpc->local_fd);
  1266.     close(ftpc->local_fd);
  1267.     ftpc->local_fd = -1;
  1268.     }
  1269. }
  1270.  
  1271. /*    -    -    -    -    -    -    -    -    */
  1272. /* Sending commands to the server: */
  1273. /*
  1274.  * Adds CRLF and calls to ftpStartSendingCmd().
  1275.  */
  1276. static void
  1277. ftpSendCmd(ftpc,cmd)
  1278. FtpContext *ftpc;
  1279. char *cmd;
  1280. {
  1281.     char buf[MAXPATHLEN];
  1282.  
  1283.     if (ftpc->trace) {
  1284.     (*(ftpc->trace))(ftpc,1,cmd);
  1285.     }
  1286.     sprintf(buf,"%s\r\n",cmd);
  1287.     ftpStartSendingCmd(ftpc,buf);
  1288. }
  1289.  
  1290. /*
  1291.  * Handles abort processing by sending the Telnet abort sequence in the
  1292.  * out-of-band stream of the ctrl conn and the ABOR command on the regular
  1293.  * stream.
  1294.  */
  1295. static void
  1296. ftpSendAbort(ftpc)
  1297. FtpContext *ftpc;
  1298. {
  1299.     char buf[8];
  1300.     int sent;
  1301.  
  1302.     DEBUG1("ftpSendAbort: ftpc=0x%lx\n",ftpc);
  1303.     DEBUG0("ftpSendAbort: sending <IAC><IP><IAC> in OOB\n");
  1304.     sprintf(buf,"%c%c%c",IAC,IP,IAC);
  1305.     do {
  1306.     sent = send(ftpc->ctrl,buf,3,MSG_OOB);
  1307.     } while (sent == -1 && errno == EINTR);
  1308.     if (sent != 3) {
  1309.     sysError("send(ftpSendAbort)");
  1310.     ftpDone(ftpc);
  1311.     }
  1312.     DEBUG0("ftpSendAbort: sending <DM>ABOR\n");
  1313.     sprintf(buf,"%cABOR\r\n",DM);
  1314.     do {
  1315.     sent = write(ftpc->ctrl,buf,strlen(buf));
  1316.     } while (sent == -1 && errno == EINTR);
  1317.     if (sent != strlen(buf)) {
  1318.     sysError("write(ftpSendAbort)");
  1319.     ftpDone(ftpc);
  1320.     }
  1321.     ftpc->state = FTPS_ABORT;
  1322.     ftpInitReply(ftpc);
  1323.     status0("Aborting...");
  1324.     DEBUG0("ftpSendAbort: done\n");
  1325. }
  1326.  
  1327. /*
  1328.  * This function starts sending CMD to the server over the control
  1329.  * connection. The writes can block, hence this has to be able to finish
  1330.  * later, but since they usually don't block, we don't set up to retry
  1331.  * unless we have to. If the command is completely sent, then we call
  1332.  * ftpInitReply() otherwise we setup to finish by registering the ctrlconn
  1333.  * for writing via callback ftpWriteCallback().
  1334.  */
  1335. static void
  1336. ftpStartSendingCmd(ftpc,cmd)
  1337. FtpContext *ftpc;
  1338. char *cmd;
  1339. {
  1340.     int cmdlen,numsent;
  1341.  
  1342.     DEBUG2("ftpStartSendingCmd: ftpc=0x%x: \"%s\"\n",ftpc,cmd);
  1343.     cmdlen = strlen(cmd);
  1344.     numsent = write(ftpc->ctrl,cmd,cmdlen);
  1345.     if (numsent < 0 && errno != ITWOULDBLOCK) {
  1346.     sysError("write(ftpStartSendingCmd)");
  1347.     DEBUG0("ftpStartSendingCmd: error writing ctrlconn\n");
  1348.     ftpDone(ftpc);
  1349.     return;
  1350.     } else if (numsent == cmdlen) {
  1351.     DEBUG0("ftpStartSendingCmd: write all done\n");
  1352.     ftpInitReply(ftpc);
  1353.     return;
  1354.     } else if (errno == ITWOULDBLOCK) {
  1355.     /* Otherwise the write would block, nothing sent */
  1356.     DEBUG0("ftpStartSendingCmd: write would block\n");
  1357.     numsent = 0;
  1358.     } else {
  1359.     /* Incomplete write */
  1360.     DEBUG1("ftpStartSendingCmd: write incomplete (%d bytes sent)\n",numsent);
  1361.     }
  1362.     /* Either way, if we get here there's something left do */
  1363.     cmd += numsent;
  1364.     if ((ftpc->cmd=malloc(strlen(cmd)+1)) == NULL) {
  1365.     sysError("malloc(ftpStartSendingCmd)");
  1366.     ftpDone(ftpc);
  1367.     return;
  1368.     }
  1369.     strcpy(ftpc->cmd,cmd);
  1370.     DEBUG1("ftpStartSendingCmd: pending: \"%s\"\n",ftpc->cmd);
  1371.     RegisterFtpFd(ftpc,ftpc->ctrl,O_WRONLY,ftpWriteCallback);
  1372. }
  1373.  
  1374. /*
  1375.  * This function is called when the ctrlconn is ready for writing.
  1376.  * It sends as much as possible. If that completes the cmd, then it
  1377.  * calls ftpInitReply(), otherwise updates the cmd with what's left
  1378.  * and returns.
  1379.  */
  1380. static void
  1381. ftpWriteCallback(ftpc)
  1382. FtpContext *ftpc;
  1383. {
  1384.     char *cmd;
  1385.     int cmdlen,numsent;
  1386.  
  1387.     cmd = ftpc->cmd;
  1388.     DEBUG2("ftpWriteCallback: ftpc=0x%x: \"%s\"\n",ftpc,cmd);
  1389.     cmdlen = strlen(ftpc->cmd);
  1390.     numsent = write(ftpc->ctrl,cmd,cmdlen);
  1391.     if (numsent < 0 && errno != ITWOULDBLOCK) {
  1392.     sysError("write(ftpWriteCallback)");
  1393.     DEBUG0("ftpWriteCallback: error writing ctrlconn\n");
  1394.     ftpDone(ftpc);
  1395.     return;
  1396.     } else if (numsent == cmdlen) {
  1397.     DEBUG0("ftpWriteCallback: write all done\n");
  1398.     UnregisterFtpFd(ftpc,ftpc->ctrl);
  1399.     free(ftpc->cmd);
  1400.     ftpInitReply(ftpc);
  1401.     return;
  1402.     } else if (errno == ITWOULDBLOCK) {
  1403.     /* Otherwise the write would block, nothing sent */
  1404.     DEBUG0("ftpWriteCallback: write would block\n");
  1405.     numsent = 0;
  1406.     } else {
  1407.     /* Incomplete write */
  1408.     DEBUG1("ftpWriteCallback: write incomplete (%d bytes sent)\n",numsent);
  1409.     }
  1410.     /* Either way, if we get here there's something left do */
  1411.     cmd += numsent;
  1412.     free(ftpc->cmd);
  1413.     if ((ftpc->cmd=malloc(strlen(cmd)+1)) == NULL) {
  1414.     sysError("malloc(ftpWriteCallback)");
  1415.     ftpDone(ftpc);
  1416.     return;
  1417.     }
  1418.     strcpy(ftpc->cmd,cmd);
  1419.     DEBUG1("ftpWriteCallback: pending: \"%s\"\n",ftpc->cmd);
  1420. }
  1421.  
  1422. /*    -    -    -    -    -    -    -    -    */
  1423. /* Printing utilities for debugging */
  1424.  
  1425. #ifdef DEBUG
  1426. char *
  1427. ftpstatestr(state)
  1428. int state;
  1429. {
  1430.     char buf[8];
  1431.  
  1432.     switch (state) {
  1433.     /* state */
  1434.       case FTPS_OPEN:        return("OPEN");
  1435.       case FTPS_CONNECT:    return("CONNECT");
  1436.       case FTPS_CONNECTED:    return("CONNECTED");
  1437.       case FTPS_USER:        return("USER");
  1438.       case FTPS_PASS:        return("PASS");
  1439.       case FTPS_CWD:        return("CWD");
  1440.       case FTPS_TYPE:        return("TYPE");
  1441.       case FTPS_READY:        return("READY");
  1442.       case FTPS_PORT:        return("PORT");
  1443.       case FTPS_GETPUT:        return("GETPUT");
  1444.       case FTPS_TRANSFER:    return("TRANSFER");
  1445.       case FTPS_EOF:        return("EOF");
  1446.       case FTPS_QUIT:        return("QUIT");
  1447.       case FTPS_ABORT:        return("ABORT");
  1448.       case FTPS_ABORT2:        return("ABORT2");
  1449.     /* reply_state */
  1450.       case FTPS_REPLY_CODE:    return("CODE");
  1451.       case FTPS_REPLY_CONT:    return("CONT");
  1452.       case FTPS_REPLY_LAST:    return("LAST");
  1453.       case FTPS_REPLY_MORE:    return("MORE");
  1454.       case FTPS_REPLY_CHCK:    return("CHCK");
  1455.       case FTPS_REPLY_IAC1:    return("IAC1");
  1456.       case FTPS_REPLY_IAC2:    return("IAC2");
  1457.       case FTPS_REPLY_IAC3:    return("IAC3");
  1458.       default:            sprintf(buf,"?%d?",state);
  1459.                 return(buf);
  1460.     }
  1461. }
  1462. #endif /* DEBUG */
  1463.  
  1464.  
  1465. /*    -    -    -    -    -    -    -    -    */
  1466. /* Sample code for standalone client */
  1467.  
  1468. #ifdef STANDALONE
  1469.  
  1470. /* This is from Brendan... */
  1471. #include <sys/types.h>        /* this may/will define FD_SET etc */
  1472. #ifdef u3b2
  1473. # include <sys/inet.h>        /* THIS does FD_SET etc on AT&T 3b2s.  */
  1474. #endif
  1475.  
  1476. fd_set readfds,writefds;
  1477. typedef void (*PF)();
  1478. PF funcs[FD_SETSIZE];
  1479. FtpContext *contexts[FD_SETSIZE];
  1480.  
  1481. void
  1482. RegisterFtpFd(ftpc,fd,flags,func)
  1483. FtpContext *ftpc;
  1484. int fd,flags;
  1485. void (*func)();
  1486. {
  1487.     switch (flags) {
  1488.       case O_RDONLY:
  1489.     fprintf(stderr,"REGISTER fd %d, flags=RO\n",fd);
  1490.     FD_SET(fd,&readfds);
  1491.     FD_CLR(fd,&writefds);
  1492.     break;
  1493.       case O_WRONLY:
  1494.     fprintf(stderr,"REGISTER fd %d, flags=WO\n",fd);
  1495.     FD_CLR(fd,&readfds);
  1496.     FD_SET(fd,&writefds);
  1497.     break;
  1498.       case O_RDWR:
  1499.     fprintf(stderr,"REGISTER fd %d, flags=RW\n",fd);
  1500.     FD_SET(fd,&readfds);
  1501.     FD_SET(fd,&writefds);
  1502.     break;
  1503.     }
  1504.     funcs[fd] = func;
  1505.     contexts[fd] = ftpc;
  1506. }
  1507.  
  1508. /*ARGSUSED*/
  1509. void
  1510. UnregisterFtpFd(ftpc,fd)
  1511. FtpContext *ftpc;
  1512. int fd;
  1513. {
  1514.     fprintf(stderr,"UNREGISTER fd %d\n",fd);
  1515.     FD_CLR(fd,&readfds);
  1516.     FD_CLR(fd,&writefds);
  1517.     funcs[fd] = NULL;
  1518.     contexts[fd] = NULL;
  1519. }
  1520.  
  1521. void
  1522. done(ftpc)
  1523. FtpContext *ftpc;
  1524. {
  1525.     fprintf(stderr,"DONE!\n");
  1526.     ftpFreeContext(ftpc);
  1527.     exit(0);
  1528. }
  1529.     
  1530. void
  1531. done1(ftpc)
  1532. FtpContext *ftpc;
  1533. {
  1534.     fprintf(stderr,"DONE1: \"%s\"\n",ftpc->files[ftpc->this_file]);
  1535. }
  1536.     
  1537. /*ARGSUSED*/
  1538. void
  1539. trace(ftpc,who,text)
  1540. FtpContext *ftpc;
  1541. int who;        /* 0 => recvd, non-0 => sent */
  1542. char *text;
  1543. {
  1544.     fprintf(stderr,"TRACE: ");
  1545.     if (who)    /* non-zero == us */
  1546.     fprintf(stderr,"ftp> ");
  1547.     fprintf(stderr,"%s",text);
  1548.     if (*(text+strlen(text)-1) != '\n')
  1549.     fprintf(stderr,"\n",text);
  1550. }
  1551.     
  1552. char *program;
  1553.  
  1554. main(argc,argv)
  1555. int argc;
  1556. char *argv[];
  1557. {
  1558.     char *host,*cwd;
  1559.     FtpContext *ftpc;
  1560.     fd_set rfds,wfds;
  1561.     int fd;
  1562.  
  1563.     if (argc < 3) {
  1564.     fprintf(stderr,"usage: %s host cwd files...\n",argv[0]);
  1565.     exit(1);
  1566.     }
  1567.     argc -= 1;
  1568.     program = *argv++;
  1569.     argc -= 1;
  1570.     host = *argv++;
  1571.     argc -= 1;
  1572.     cwd = *argv++;
  1573.     FD_ZERO(&rfds);
  1574.     FD_ZERO(&wfds);
  1575.     ftpc = ftpNewContext(host,"anonymous","ferguson@cs.rochester.edu",
  1576.              cwd,TYPE_I,1,1,FTP_GET,argv,argc,trace,done1,done);
  1577.     printf("Calling ftpStart...\n");
  1578.     ftpStart(ftpc);
  1579.     while (1) {
  1580.     bcopy((char *)&readfds,(char *)&rfds,sizeof(fd_set));
  1581.     bcopy((char *)&writefds,(char *)&wfds,sizeof(fd_set));
  1582.     fprintf(stderr,"select(): R=");
  1583.     for (fd=0; fd < FD_SETSIZE; fd++) {
  1584.         if (FD_ISSET(fd,&rfds))
  1585.         fprintf(stderr,"%d,",fd);
  1586.     }
  1587.     fprintf(stderr," W=");
  1588.     for (fd=0; fd < FD_SETSIZE; fd++) {
  1589.         if (FD_ISSET(fd,&wfds))
  1590.         fprintf(stderr,"%d,",fd);
  1591.     }
  1592.     fprintf(stderr,"\n");
  1593.     if (select(FD_SETSIZE,&rfds,&wfds,NULL,NULL) < 0) {
  1594.         perror(program);
  1595.         exit(1);
  1596.     }
  1597.     for (fd=0; fd < FD_SETSIZE; fd++) {
  1598.         if (FD_ISSET(fd,&rfds) || FD_ISSET(fd,&wfds)) {
  1599.         fprintf(stderr,"selected fd %d...\n",fd);
  1600.         (*(funcs[fd]))(contexts[fd]);
  1601.         }
  1602.     }
  1603.     }
  1604. }
  1605.  
  1606. void
  1607. sysError(s)
  1608. char *s;
  1609. {
  1610.     extern int errno;
  1611.     extern char *sys_errlist[];
  1612.     fprintf(stderr,"SYSERR: %s: %s\n",s,sys_errlist[errno]);
  1613. }
  1614.  
  1615. void
  1616. alert0(s)
  1617. char *s;
  1618. {
  1619.     fprintf(stderr,"ALERT: %s\n",s);
  1620. }
  1621.  
  1622. void
  1623. status0(s)
  1624. char *s;
  1625. {
  1626.     fprintf(stderr,"STATUS: %s\n",s);
  1627. }
  1628.  
  1629. int
  1630. ftpPrompt(ftpc)
  1631. FtpContext *ftpc;
  1632. {
  1633.     char c;
  1634.  
  1635.     fprintf(stderr,"CONFIRM: %s %s [ynaq]",
  1636.         ftpfilecmdstr(ftpc->filecmd),ftpc->files[ftpc->this_file]);
  1637.     c = getchar();
  1638.     if (c != '\n')
  1639.     (void)getchar();
  1640.     switch (c) {
  1641.       case 'y': return(1);
  1642.       case 'n': return(0);
  1643.       case 'a': ftpc->prompt = 0;
  1644.             return(1);
  1645.       case 'q': ftpc->this_file = ftpc->num_files;
  1646.             return(0);
  1647.       default: return(1);
  1648.     }
  1649. }
  1650. #endif /*STANDALONE*/
  1651.