home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / internet / tcpip / src205 / TCPIP_Src / FTP / c / ftpcli < prev    next >
Encoding:
Text File  |  1995-04-17  |  60.7 KB  |  2,602 lines

  1. /* FTP client (interactive user) code */
  2. #define LINELEN         128     /* Length of command buffer */
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <time.h>
  7. #include <stdarg.h>
  8. #include <ctype.h>
  9. #include "alarm.h"
  10. #include "global.h"
  11. #include "mbuf.h"
  12. #include "domain.h"
  13. #include "netuser.h"
  14. #include "icmp.h"
  15. #include "timer.h"
  16. #include "tcp.h"
  17. #include "ftp.h"
  18. #include "session.h"
  19. #include "cmdparse.h"
  20. #include "misc.h"
  21. #include "Terminal.h"
  22. #include "vterm.h"
  23. #include "bbc.h"
  24. #include "files.h"
  25. #include "pathent.h"
  26. #include "var.h"
  27.  
  28. #include "os.h"
  29. #include "swis.h"
  30.  
  31. extern os_error *create_path(char *path, int size, int type);
  32. static int create_fetchedfile(struct ftp *ftp, char *localname, char *remotename, char *givenname);
  33. static int create_localfile(struct ftp *ftp, char *localname, int filetype);
  34. extern char *fttoa(int t);
  35.  
  36. extern struct session *current;
  37. struct session *current_ftp;
  38. extern char nospace[];
  39. extern char badhost[];
  40. static char notsess[]   = "Not an FTP session!\r\n";
  41. static char cantwrite[] = "Can't write %s\r\n";
  42. static char cantread[]  = "Can't read %s\r\n";
  43. static char nomemforop[] = "Insufficient memory for this operation\r\n";
  44.  
  45. static int  dotrace(int, char **);
  46. static int  doprompt(int, char **);
  47. static int  dosite(int, char **);
  48. static int  donothing(int, char **);
  49. static int  doftpquit(int, char **);
  50. static int  dowinclose(int, char **);
  51. static int  dowinopen(int, char **);
  52. static int  doftpcd(int, char **);
  53. static int  domkdir(int, char **);
  54. static int  dormdir(int, char **);
  55. static int  dodelete(int, char **);
  56. static int  doascii(int, char **);
  57. static int  dobinary(int, char **);
  58. static int  dotype(int, char **);
  59. static int  doget(int, char **);
  60. static int  doreget(int, char **);
  61. static int  domget(int, char **);
  62. static int  dobatch(int, char **);
  63. static int  dopath(int, char **);
  64. static int  doquote(int, char **);
  65. static int  doview(int, char **);
  66. static int  dohash(int, char **);
  67. static int  dolist(int, char **);
  68. static int  dols(int, char **);
  69. static int  doput(int, char **);
  70. static int  dopwd(int, char **);
  71. static int  doftpcdup(int, char **);
  72. static int  dospool(int, char **);
  73.  
  74. static void doreply(struct ftp *);
  75. static void ftpsetup(struct ftp *, void (*)(), void (*)(), void (*)());
  76. static void ftpccs(struct tcb *, char, char);
  77. static void ftpcds(struct tcb *, char, char);
  78. static int  sndftpmsg(struct ftp *, char *, ...);
  79. static void prompt(struct ftp *ftp);
  80. static int batch_ftp(struct ftp *ftp);
  81. static int batch_open(struct ftp *ftp, char *batch);
  82. static int mprocess(struct ftp *ftp);
  83.  
  84. static int doopttype(int, char **);
  85. static int doopthash(int, char **);
  86. static int dooptpath(int, char **);
  87. static int dooptwinsz(int, char **);
  88. static int doopttrace(int, char **);
  89. static int dooptprompt(int, char **);
  90.  
  91. void ftp_mpcleanup(struct ftp *ftp);
  92.  
  93. struct cmds ftpabort[] = {
  94.   "",             donothing,    0,    NULLCHAR,               NULLCHAR,
  95.   "abort",        doabort,      0,    NULLCHAR,               NULLCHAR,
  96.   "hash",         dohash,       2,    "hash lumpsize",        NULLCHAR,
  97.   "prompt",       doprompt,     0,    NULLCHAR,               NULLCHAR,
  98.   "spool",        dospool,      0,    "spool [open [<file>]] | [pause] | [continue] | [pause]", NULLCHAR,
  99.   "trace",        dotrace,      0,    NULLCHAR,               NULLCHAR,
  100.   "winclose",     dowinclose,   0,    "winclose",             NULLCHAR,
  101.   "winopen",      dowinopen,    0,    "winopen",              NULLCHAR,
  102.   NULLCHAR,       NULLFP,       0,    "Only valid command is \"abort\"", NULLCHAR,
  103. };
  104.  
  105. struct cmds ftpcmds[] = {
  106.   "",             donothing,    0,    NULLCHAR,               NULLCHAR,
  107.   "ascii",        doascii,      0,    NULLCHAR,               NULLCHAR,
  108.   "abort",        doabort,      0,    NULLCHAR,               NULLCHAR,
  109.   "binary",       dobinary,     0,    NULLCHAR,               NULLCHAR,
  110.   "cd",           doftpcd,      2,    "cd <directory>",       NULLCHAR,
  111.   "cdup",         doftpcdup,    0,    NULLCHAR,               NULLCHAR,
  112.   "delete",       dodelete,     0,    NULLCHAR,               NULLCHAR,
  113.   "dir",          dolist,       0,    NULLCHAR,               NULLCHAR,
  114.   "list",         dolist,       0,    NULLCHAR,               NULLCHAR,
  115.   "get",          doget,        2,    "get remotefile <localfile>",  NULLCHAR,
  116.   "hash",         dohash,       2,    "hash lumpsize",        NULLCHAR,
  117.   "image",        dobinary,     0,    NULLCHAR,               NULLCHAR,
  118.   "ls",           dols,         0,    NULLCHAR,               NULLCHAR,
  119.   "mget",         domget,       0,    "mget <filelist>",      NULLCHAR,
  120.   "mkdir",        domkdir,      2,    "mkdir <directory>",    NULLCHAR,
  121.   "nlst",         dols,         0,    NULLCHAR,               NULLCHAR,
  122.   "path",         dopath,       0,    "path [<path process options>|off|default]", NULLCHAR,
  123.   "prompt",       doprompt,     0,    NULLCHAR,               NULLCHAR,
  124.   "put",          doput,        2,    "put localfile <remotefile>",  NULLCHAR,
  125.   "pwd",          dopwd,        0,    NULLCHAR,               NULLCHAR,
  126.   "quote",        doquote,      0,    "quote <command>", NULLCHAR,
  127.   "reget",        doreget,      2,    "reget remotefile <localfile>",  NULLCHAR,
  128.   "rmdir",        dormdir,      2,    "rmdir <directory>",    NULLCHAR,
  129.   "site",         dosite,       0,    "site <command>",       NULLCHAR,
  130.   "source",       dobatch,      0,    "source <ftp command file>", NULLCHAR,
  131.   "spool",        dospool,      0,    "spool [open [<file>]] | [pause] | [continue] | [pause]", NULLCHAR,
  132.   "trace",        dotrace,      0,    NULLCHAR,               NULLCHAR,
  133.   "type",         dotype,       0,    NULLCHAR,               NULLCHAR,
  134.   "view",         doview,       1,    "view remotefile",      NULLCHAR,
  135.   "winclose",     dowinclose,   0,    "winclose",             NULLCHAR,
  136.   "winopen",      dowinopen,    0,    "winopen",              NULLCHAR,
  137.   "quit",         doftpquit,    0,    "quit",                 NULLCHAR,
  138.   "bye",          doftpquit,    0,    "bye",                  NULLCHAR,
  139.   NULLCHAR,       NULLFP,       0,    NULLCHAR,               NULLCHAR,
  140. };
  141.  
  142. struct cmds ftpopts[] = {
  143.   "type",         doopttype,    0,    NULLCHAR,               NULLCHAR,
  144.   "hash",         doopthash,    0,    NULLCHAR,               NULLCHAR,
  145.   "path",         dooptpath,    0,    NULLCHAR,               NULLCHAR,
  146.   "prompt",       dooptprompt,  0,    NULLCHAR,               NULLCHAR,
  147.   "trace",        doopttrace,   0,    NULLCHAR,               NULLCHAR,
  148.   "winsize",      dooptwinsz,   0,    NULLCHAR,               NULLCHAR,
  149.   NULLCHAR,       NULLFP,       0,    "Only valid command is \"abort\"", NULLCHAR,
  150. };
  151.  
  152. #define PROMPT_NODIR    0
  153. #define PROMPT_SHORTDIR 1
  154. #define PROMPT_LONGDIR  2
  155.  
  156. extern varlist global_vars;
  157.  
  158. /*
  159.  * Default options set by ftpopt commands
  160.  */
  161. static pathent_str *default_pathopts = PATH_OFF;
  162. static int default_win_xv = 80;
  163. static int default_win_yv = 24;
  164. static int default_win_xe = 80;
  165. static int default_win_ye = 24;
  166. static int default_trace  = 0;
  167. static int default_prompt = 0;
  168. static int default_hash   = 0;
  169. static int default_type   = ASCII_TYPE;
  170.  
  171. /*
  172.  * Auto login FTP
  173.  */
  174. int doaftp(int argc, char **argv)
  175. {
  176.   extern char ftphostfile[];
  177.  
  178.   FILE *fp;
  179.   int n, line;
  180.   char *p;
  181.   char ln[256];
  182.   char buf[256];
  183.   long pos, defpos = -1L;
  184.   struct ftp *ftp;
  185.  
  186.   char missing[] = "Missing %s in FTPHosts file at line %d\r\n";  
  187.  
  188.   char *hostname  = NULL; /* Host name from command or Accounts file */
  189.   char *username  = NULL; /* username from accounts file */
  190.   char *password  = NULL; /* password from accounts file */
  191.   char *source    = NULL; /* Source file from accounts file */
  192.   char *menu      = NULL; /* Menu from accounts file */
  193.   char *directory = NULL; /* Default directory */
  194.  
  195.   if (fp = fopen(ftphostfile, "r"), fp==NULL)
  196.   {
  197.     cwprintf(NULL, "FTP hosts file \"%s\" not found.\r\n", ftphostfile);
  198.     return 1;
  199.   }  
  200. /*    
  201.   bbc_vdu(4);
  202.   bbc_vdu(26);
  203.   bbc_vdu(30);
  204. */
  205.   line = 0;
  206.   pos = 0L;
  207.   n = strlen(argv[1]);
  208.  
  209. /*
  210.   printf("searching for %s\n", argv[1]);
  211. */
  212.   while (fgets(ln, 256, fp)!=NULL)
  213.   {
  214.     ++line;
  215.     /*
  216.      * Skip parameters or command for other entries...
  217.      */
  218. /*
  219.     printf("Checking %s", buf);
  220. */    
  221.  
  222.     if (*ln=='#' || *ln==' ' || *ln=='\t' || *ln=='\n')
  223.       continue;
  224.  
  225.     var_translate(global_vars, ln, 0, buf, 256);
  226.  
  227.     if (!strncmp(buf, argv[1], n) && buf[n]==':')
  228.     {
  229.       p = buf+n+1;
  230.       goto found;
  231.     }
  232.     else if (!strncmp(buf, "default", 7) && buf[7]==':')
  233.       defpos = pos;
  234.     pos = ftell(fp);
  235.   }
  236.  
  237. /*
  238.   printf("Checking for default\n");
  239. */  
  240.   /*
  241.    * Not found, use default entry for login etc
  242.    */
  243.   if (defpos<0)
  244.   {
  245.     /*
  246.      * No default - print error and return
  247.      */
  248.     cwprintf(NULL, "No entry found for %s, and no default either\r\n", argv[1]);
  249.     fclose(fp);
  250.     return 1;
  251.   }
  252.   else
  253.   {
  254.     /*
  255.      * Position at default entry and refill the buffer
  256.      * and assume that the given parameter was the hostname.
  257.      */
  258.     fseek(fp, defpos, SEEK_SET);
  259.     fgets(ln, 256, fp);
  260.     var_translate(global_vars, ln, 0, buf, 256);
  261.     hostname = strdup(argv[1]);
  262.     n = 7; /* length of "default" */
  263.   }
  264.  
  265.  
  266. found:
  267.   p = strtok(buf+n+1, " \t\n");
  268.  
  269.   for (;;)
  270.   {
  271.     if (p==NULL)
  272.     {
  273. /*
  274.       printf("Get another line...\n");
  275. */      
  276.       while (p = fgets(ln, 256, fp), p!=NULL && (*ln=='#' || *ln=='\n'))
  277.       {
  278. /*
  279.         printf("Read %s",buf);
  280. */        
  281.         ++line;
  282.       }
  283.       /*
  284.        * If end of file, or start of next entry
  285.        */
  286.       if (p==NULL || (*ln!=' ' && *ln!='\t'))
  287.         break;
  288.     
  289.       var_translate(global_vars, ln, 0, buf, 256);
  290.  
  291.       if (p = strtok(buf, " \t\n"), p==NULL)
  292.         break;
  293.     }
  294.  
  295. /*
  296.     printf("p = %s\n", (p)?p:"<null>");
  297. */    
  298.  
  299.     if (!strcmp(p, "host"))
  300.     {
  301.       if (p = strtok(NULL, " \t\n"), p==NULL)
  302.       {
  303.         cwprintf(NULL, missing, "host name", line);
  304.         goto error;
  305.       }
  306.       else
  307.         hostname = strdup(p);
  308.     }
  309.     else if (!strcmp(p, "login"))
  310.     {
  311.       if (p = strtok(NULL, " \t\n"), p==NULL)
  312.       {
  313.         cwprintf(NULL, missing, "login name", line);
  314.         goto error;
  315.       }
  316.       else
  317.         username = strdup(p);
  318.     }
  319.     else if (!strcmp(p, "password"))
  320.     {
  321.       if (p = strtok(NULL, " \t\n"), p==NULL)
  322.       {
  323.         cwprintf(NULL, missing, "password", line);
  324.         goto error;
  325.       }
  326.       else
  327.         password = strdup(p);
  328.     }
  329.     else if (!strcmp(p, "source"))
  330.     {
  331.       if (p = strtok(NULL, " \t\n"), p==NULL)
  332.       {
  333.         cwprintf(NULL, missing, "command file name", line);
  334.         goto error;
  335.       }
  336.       else
  337.         source = strdup(p);
  338.     }
  339.     else if (!strcmp(p, "directory"))
  340.     {
  341.       if (p = strtok(NULL, " \t\n"), p==NULL)
  342.       {
  343.         cwprintf(NULL, missing, "directory name", line);
  344.         goto error;
  345.       }
  346.       else
  347.         directory = strdup(p);
  348.     }
  349.     else if (!strcmp(p, "menu"))
  350.     {
  351.       if (p = strtok(NULL, " \t\n"), p==NULL)
  352.       {
  353.         cwprintf(NULL, missing, "menu file name", line);
  354.         goto error;
  355.       }
  356.       else
  357.         menu = strdup(p);
  358.     }
  359.     p = strtok(NULL, " \t\n");
  360.   }  
  361.  
  362.   fclose(fp);
  363.   fp = NULL;
  364.  
  365. /*
  366.   printf("hostname = %s\n", (hostname)?hostname:"<null>");
  367.   printf("login    = %s\n", (username)?username:"<null>");
  368.   printf("password = %s\n", (password)?password:"<null>");
  369.   printf("menu     = %s\n", (menu)?menu:"<null>");
  370.   printf("source   = %s\n", (source)?source:"<null>");
  371. */
  372.   /*
  373.    * Substitute new hostname into original command
  374.    */
  375.   if (argv[1] = hostname, hostname==NULL)
  376.   {
  377.     cwprintf(NULL, "No hostname specified\r\n");
  378.     goto error;
  379.   }
  380.  
  381.   if (doftp(2, argv)==0)
  382.   {
  383.     free(hostname);
  384.     ftp = current_ftp->cb.ftp;
  385.     ftp->username = username;
  386.     ftp->password = password;
  387.     ftp->cd       = directory;
  388.  
  389.     if (menu!=NULL)
  390.     {
  391.       vterm_attach_macros(ftp->window->vt, menu);
  392.       free(menu);
  393.     }
  394.  
  395.     if (argc>2 && *argv[2]!='>')
  396.     {
  397.       if (source)
  398.         ftp->nextsource = strdup(argv[2]);
  399.       else
  400.         source = strdup(argv[2]);
  401.     }
  402.  
  403.     if (source)
  404.     {
  405.       if (!batch_open(ftp, source) && ftp->nextsource!=NULL)
  406.       {
  407.         batch_open(ftp, ftp->nextsource);
  408.         free(ftp->nextsource);
  409.         ftp->nextsource = NULL;
  410.       }
  411.       free(source);
  412.     }
  413.  
  414.     if (argc>2 && *argv[argc-1]=='>')
  415.     {
  416.       char *s[3];
  417.       s[0] = "spool";
  418.       s[1] = "open";
  419.       s[2] = argv[argc-1]+1;
  420.       dospool(3, s);
  421.     }
  422.  
  423.     return 0;
  424.   }
  425.  
  426. error:
  427.   if (fp!=NULL)
  428.     fclose(fp);
  429.   if (hostname)
  430.     free(hostname);
  431.   if (username)
  432.     free(username);
  433.   if (password)
  434.     free(password);
  435.   if (menu)
  436.     free(menu);
  437.   if (source)
  438.     free(source);
  439.   if (directory)
  440.     free(directory);
  441.  
  442.   return 1;
  443. }
  444.  
  445. /* Handle top-level FTP command */
  446. int doftp(int argc, char **argv)
  447. {
  448.   int used_args;
  449.   struct session *s;
  450.   struct ftp *ftp;
  451.   struct tcb *tcb;
  452.   struct socket lsocket,fsocket;
  453.  
  454.   lsocket.address = ip_addr;
  455.   lsocket.port = lport++;
  456.  
  457.   if ((fsocket.address = resolve(argv[1])) == 0)
  458.   {
  459.     cwprintf(NULL, badhost,argv[1]);
  460.     return 1;
  461.   }
  462.  
  463.   if (argc<3)
  464.   {
  465.     fsocket.port = FTP_PORT;
  466.     used_args = 2;
  467.   }
  468.   else
  469.   {
  470.     if (*argv[2]!='\\')
  471.     {
  472.       fsocket.port = atoi(argv[2]);
  473.       used_args = 3;
  474.     }
  475.     else
  476.     {
  477.       fsocket.port = FTP_PORT;
  478.       used_args = 2;
  479.     }
  480.   }
  481.   /* Allocate a session control block */
  482.   if ((s = newsession()) == NULLSESSION)
  483.   {
  484.     cwprintf(NULL, "Too many sessions\r\n");
  485.     return 1;
  486.   }
  487.   if ((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
  488.     strcpy(s->name,argv[1]);
  489.   s->type = FTP;
  490.   s->parse = (void(*)())ftpparse;
  491.  
  492.   /* Allocate an FTP control block */
  493.   if ((ftp = ftp_create(LINELEN)) == NULLFTP)
  494.   {
  495.     s->type = FREE;
  496.     cwprintf(NULL, nospace);
  497.     return 1;
  498.   }
  499.  
  500.   ftp->window = Window_Open(s, "FTP", term_SIXTEEN | term_CARET);
  501.  
  502.   /* ftpopt winsize */
  503.   vterm_resize(ftp->window->vt, default_win_xe, default_win_ye);
  504.   vterm_visible(ftp->window->vt, default_win_xv, default_win_yv);
  505.  
  506.   vterm_parse(ftp->window->vt, argc-used_args, &argv[used_args]);
  507.  
  508.   current       = s;
  509.   current_ftp   = s;
  510.   ftp->state    = STARTUP_STATE;
  511.   s->cb.ftp     = ftp;     /* Downward link */
  512.   ftp->session  = s;       /* Upward link */
  513.   s->window     = ftp->window;
  514.   ftp->session->echo = TRUE;
  515.   ftp->trace    = default_trace;                     /* ftpopt trace */
  516.   ftp->prompt   = default_prompt;                    /* ftpopt prompt */
  517.   ftp->hash     = default_hash;                      /* ftpopt hash */
  518.   ftp->pathopts = duppathent(default_pathopts);      /* ftpopt path */
  519.   ftp->flags    = 0;
  520.  
  521.   /* Now open the control connection */
  522.   tcb = open_tcp(&lsocket, &fsocket, TCP_ACTIVE, 0, (void(*)())ftpccr, NULLVFP, (void(*)())ftpccs, 0,( char *)ftp);
  523.   ftp->control = tcb;
  524.   go(s);
  525.   return 0;
  526. }
  527.  
  528. /* Parse user FTP commands */
  529. void ftpparse(struct session *active, char *line, int16 len)
  530. {
  531.   struct mbuf *bp;
  532.   extern struct cmds cmds[];
  533.   if (active == NULL)
  534.     active = current;
  535.   current_ftp = active;
  536.  
  537.   /* direct '*' prefixed commands to main command window */
  538.   if (*line=='*')
  539.   {
  540.     cmdparse(cmds, line+1, NULL);
  541.     prompt(active->cb.ftp);
  542.     return;
  543.   }
  544.  
  545.   switch(active->cb.ftp->state)
  546.   {
  547.   case RECEIVING_STATE1:
  548.   case SENDING_FILE_STATE1:
  549.   case RECEIVING_STATE2:
  550.   case SENDING_FILE_STATE2:
  551.     /* The only command allowed in data transfer state is ABORT */
  552.     if (cmdparse(ftpabort, line, active->window) == -1)
  553.     {
  554.       cwprintf(active->window, "Transfer in progress; only ABORT is acceptable\r\n");
  555.     }
  556.     break;
  557.   case COMMAND_STATE:
  558.     /* Save it now because cmdparse modifies the original */
  559.     bp = qdata(line,len);
  560.     if (cmdparse(ftpcmds, line, active->window) == -1)
  561.     {
  562.       /* Send it direct */
  563.       if (bp != NULLBUF)
  564.       {
  565.         send_tcp(active->cb.ftp->control,bp);
  566.         active->cb.ftp->replycount++;
  567.       }
  568.       else
  569.         cwprintf(active->window, nospace);
  570.     }
  571.     else
  572.     {
  573.       free_p(bp);
  574.     }
  575.     break;
  576.   case STARTUP_STATE:             /* Startup up autologin */
  577.     cwprintf(active->window, "Not connected yet, ignoring %s\r\n",line);
  578.     break;
  579.   case USER_STATE:                /* Got the user name */
  580.     line[len] = '\0';
  581.     sndftpmsg(active->cb.ftp,"USER %s",line);
  582.     break;
  583.   case PASS_STATE:                /* Got the password */
  584.     cooked();
  585.     active->raw = FALSE;
  586.         vterm_setflags(active->window->vt,
  587.            /* Clear */ VTSW_CHAT|VTSW_LINE|VTSW_NEWLINE|VTSW_WRAP|VTSW_ECHO,
  588.            /* Set */   VTSW_LINE|VTSW_NEWLINE|VTSW_WRAP|VTSW_ECHO );
  589.     line[len] = '\0';
  590.     sndftpmsg(active->cb.ftp,"PASS %s",line);
  591.     break;
  592.   }
  593. }
  594.  
  595. /* Handle null line to avoid trapping on first command in table */
  596. static int donothing(int argc, char **argv)
  597. {
  598.   argc = argc;
  599.   argv = argv;
  600.   return 0;
  601. }
  602.  
  603. /* Spool control */
  604. static int dospool(int argc, char **argv)
  605. {
  606.   int a = 0;
  607.   struct ftp *ftp;
  608.  
  609.   char filename[256];
  610.  
  611.   ftp = current_ftp->cb.ftp;
  612.  
  613.   if (argc<2)
  614.   {
  615.     switch(vterm_spool(ftp->window->vt, NULL, 0))
  616.     {
  617.       case 0:
  618.         cwprintf(ftp->window, "Closed\r\n");
  619.         break;
  620.       case 1:
  621.         cwprintf(ftp->window, "Open\r\n");
  622.         break;
  623.       case 2:
  624.         cwprintf(ftp->window, "Paused\r\n");
  625.         break;
  626.     }
  627.   }
  628.   else
  629.   {
  630.     if (!strncmp(argv[1], "open", strlen(argv[1])))
  631.     {
  632.       if (argc<3)
  633.         a = vterm_spool(ftp->window->vt, "*", 1);
  634.       else
  635.       {
  636.         strcpy(filename, argv[2]);
  637.         if (create_localfile(ftp, filename, -1))
  638.           a = vterm_spool(ftp->window->vt, argv[2], 1);
  639.         else
  640.           a = 0;
  641.       }
  642.     }
  643.     else if (!strncmp(argv[1], "pause", strlen(argv[1])))
  644.       a = vterm_spool(ftp->window->vt, NULL, 2);
  645.     else if (!strncmp(argv[1], "continue", strlen(argv[1])))
  646.       a = vterm_spool(ftp->window->vt, NULL, 3);
  647.     else if (!strncmp(argv[1], "close", strlen(argv[1])))
  648.       a = vterm_spool(ftp->window->vt, NULL, 1);
  649.     else
  650.     {
  651.       /* Unknown command - complain and return */
  652.       cwprintf(ftp->window, "Spool commands are open|close|pause|continue\r\n");
  653.       return 1;
  654.     }
  655.     /* process spool command answer */
  656.     switch (a)
  657.     {
  658.       case 1:
  659.         cwprintf(ftp->window, "Spooling to %s\r\n", (argc<3 || *argv[2]=='*')?"temp file":filename);
  660.         break;
  661.       case 0:
  662.         cwprintf(ftp->window, "Can't open file %s\r\n", (argc<3 || *argv[2]=='*')?"temp file":filename);
  663.         break;
  664.       case -1:
  665.         cwprintf(ftp->window, "Spool allready open\r\n");
  666.         break;
  667.       case -2:
  668.         cwprintf(ftp->window, "Spool allready paused\r\n");
  669.         break;
  670.       case -3:
  671.         cwprintf(ftp->window, "Spool not paused\r\n");
  672.         break;
  673.       case -4:
  674.         cwprintf(ftp->window, "Not spooling\r\n");
  675.         break;
  676.     }  
  677.   }
  678.   prompt(ftp);
  679.   return 0;
  680. }
  681.  
  682. /*
  683.  * Set default trace level
  684.  */
  685. static int dotrace(int argc, char **argv)
  686. {
  687.   register struct ftp *ftp;
  688.  
  689.   ftp = current_ftp->cb.ftp;
  690.  
  691.   if (argc<2)
  692.     cwprintf(ftp->window, "FTP trace level is %d\r\n", ftp->trace);
  693.   else
  694.     ftp->trace = atoi(argv[1]);
  695.  
  696.   prompt(ftp);
  697.   return 0;
  698. }
  699.  
  700. /*
  701.  * Set default prompt type
  702.  */
  703. static int doprompt(int argc, char **argv)
  704. {
  705.   register struct ftp *ftp;
  706.  
  707.   ftp = current_ftp->cb.ftp;
  708.  
  709.   if (argc<2)
  710.   {
  711.     char *type;
  712.     switch (ftp->prompt)
  713.     {
  714.       case PROMPT_SHORTDIR:
  715.         type = "Short directory";
  716.         break;
  717.       case PROMPT_LONGDIR:
  718.         type = "Full directory";
  719.         break;
  720.       default:
  721.         type = "No directory";
  722.     }      
  723.     cwprintf(ftp->window, "FTP prompt type: %s\r\n", type);
  724.   }
  725.   else
  726.   {
  727.     if (!strncmp(argv[1], "nodir", strlen(argv[1])))
  728.       ftp->prompt = PROMPT_NODIR;
  729.     else if (!strncmp(argv[1], "short", strlen(argv[1])))
  730.       ftp->prompt = PROMPT_SHORTDIR;
  731.     else if (!strncmp(argv[1], "long", strlen(argv[1])))
  732.       ftp->prompt = PROMPT_LONGDIR;
  733.     else if (*argv[1]=='=')
  734.       cwprintf(ftp->window, "Reply counter: %d\r\n", ftp->replycount);
  735.     else if (*argv[1]=='+')
  736.       cwprintf(ftp->window, "Reply counter: %d\r\n", ftp->replycount++);
  737.     else if (*argv[1]=='-')
  738.       cwprintf(ftp->window, "Reply counter: %d\r\n", ftp->replycount--);
  739.     else
  740.       cwprintf(ftp->window, "Unknown prompt type. Valid prompts are: nodir short long\r\n");
  741.   }
  742.   prompt(ftp);
  743.   return 0;
  744. }
  745.  
  746. static int dowinclose(int argc, char **argv)
  747. {
  748.   register struct ftp *ftp;
  749.  
  750.   argc = argc;
  751.   argv = argv;
  752.  
  753.   ftp = current_ftp->cb.ftp;
  754.   vterm_close(current_ftp->window->vt);
  755.   current_ftp->window->Flags.flags.closing = 1;
  756.   prompt(ftp);
  757.  
  758.   return 0;
  759. }
  760.  
  761. static int dowinopen(int argc, char **argv)
  762. {
  763.   register struct ftp *ftp;
  764.  
  765.   argc = argc;
  766.   argv = argv;
  767.  
  768.   ftp = current_ftp->cb.ftp;
  769.   vterm_open(current_ftp->window->vt);
  770.   current_ftp->window->Flags.flags.closing = 0;
  771.   prompt(ftp);
  772.  
  773.   return 0;
  774. }
  775.  
  776. static int doftpquit(int argc, char **argv)
  777. {
  778.   int a;
  779.   register struct ftp *ftp;
  780.  
  781.   argc = argc;
  782.  
  783.   ftp = current_ftp->cb.ftp;
  784.   a = sndftpmsg(ftp,"QUIT\r\n");
  785.   prompt(ftp);
  786.  
  787.   return a;
  788. }
  789.  
  790.  
  791. /* Translate 'cd' to 'cwd' for convenience */
  792. static int doftpcd(int argc, char **argv)
  793. {
  794.   register struct ftp *ftp;
  795.  
  796.   argc = argc;
  797.  
  798.   ftp = current_ftp->cb.ftp;
  799.   ftp->flags |= (FTP_TRAP_257|FTP_SUPP_257);
  800.   sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
  801.   return sndftpmsg(ftp,"PWD\r\n");
  802. }
  803.  
  804. /* Translate 'cd' to 'cwd' for convenience */
  805. static int doftpcdup(int argc, char **argv)
  806. {
  807.   register struct ftp *ftp;
  808.  
  809.   argc = argc;
  810.   argv = argv;
  811.  
  812.   ftp = current_ftp->cb.ftp;
  813.   sndftpmsg(ftp,"CDUP\r\n");
  814.   ftp->flags |= (FTP_TRAP_257|FTP_SUPP_257);
  815.   return sndftpmsg(ftp,"PWD\r\n");
  816. }
  817.  
  818. /* Translate 'cd' to 'cwd' for convenience */
  819. static int dopwd(int argc, char **argv)
  820. {
  821.   register struct ftp *ftp;
  822.  
  823.   argc = argc;
  824.   argv = argv;
  825.  
  826.   ftp = current_ftp->cb.ftp;
  827.   ftp->flags |= FTP_TRAP_257;
  828.   return sndftpmsg(ftp,"PWD\r\n");
  829. }
  830.  
  831. /* Translate 'mkdir' to 'xmkd' for convenience */
  832. static int domkdir(int argc, char **argv)
  833. {
  834.   register struct ftp *ftp;
  835.  
  836.   argc = argc;
  837.  
  838.   ftp = current_ftp->cb.ftp;
  839.   return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
  840. }
  841.  
  842. static int dohash(int argc, char **argv)
  843. {
  844.   register struct ftp *ftp;
  845.  
  846.   argc = argc;
  847.  
  848.   ftp = current_ftp->cb.ftp;
  849.   ftp->hash = atoi(argv[1]);
  850.   ftp->last = 0;
  851.  
  852.   prompt(ftp);
  853.   return(0);
  854. }
  855.  
  856. /* Translate 'rmdir' to 'xrmd' for convenience */
  857. static int dormdir(int argc, char **argv)
  858. {
  859.   register struct ftp *ftp;
  860.  
  861.   argc = argc;
  862.  
  863.   ftp = current_ftp->cb.ftp;
  864.   return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
  865. }
  866.  
  867. static int dodelete(int argc, char **argv)
  868. {
  869.   register struct ftp *ftp;
  870.  
  871.   argc = argc;
  872.  
  873.   ftp = current_ftp->cb.ftp;
  874.   return sndftpmsg(ftp,"DELE %s\r\n",argv[1]);
  875. }
  876.  
  877. static int dobinary(int argc, char **argv)
  878. {
  879.   register struct ftp *ftp;
  880.  
  881.   argc = argc;
  882.   argv = argv;
  883.  
  884.   ftp = current_ftp->cb.ftp;
  885.   ftp->type = IMAGE_TYPE;
  886.   sndftpmsg(ftp,"TYPE I\r\n");
  887.   return(0);
  888. }
  889.  
  890. static int doascii(int argc, char **argv)
  891. {
  892.   register struct ftp *ftp;
  893.  
  894.   argc = argc;
  895.   argv = argv;
  896.  
  897.   ftp = current_ftp->cb.ftp;
  898.   ftp->type = ASCII_TYPE;
  899.   sndftpmsg(ftp,"TYPE A\r\n");
  900.  
  901.   return(0);
  902. }
  903.  
  904. /* Handle "type" command from user */
  905. static int dotype(int argc, char **argv)
  906. {
  907.   register struct ftp *ftp;
  908.  
  909.   ftp = current_ftp->cb.ftp;
  910.   if (argc < 2)
  911.   {
  912.     switch(ftp->type)
  913.     {
  914.     case IMAGE_TYPE:
  915.       cwprintf(ftp->window, "Image\r\n");
  916.       break;
  917.     case ASCII_TYPE:
  918.       cwprintf(ftp->window, "Ascii\r\n");
  919.       break;
  920.     }
  921.     return 0;
  922.   }
  923.   switch(*argv[1])
  924.   {
  925.   case 'i':
  926.   case 'b':
  927.     ftp->type = IMAGE_TYPE;
  928.     sndftpmsg(ftp,"TYPE I\r\n");
  929.     break;
  930.   case 'a':
  931.     ftp->type = ASCII_TYPE;
  932.     sndftpmsg(ftp,"TYPE A\r\n");
  933.     break;
  934.   case 'l':
  935.     ftp->type = IMAGE_TYPE;
  936.     sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
  937.     break;
  938.   default:
  939.     cwprintf(ftp->window, "Invalid type %s\r\n",argv[1]);
  940.     return 1;
  941.   }
  942.   return 0;
  943. }
  944.  
  945. /* Verbatim command to server */
  946. static int doquote(int argc, char **argv)
  947. {
  948.   char localname[256];
  949.   register struct ftp *ftp;
  950.  
  951.   ftp = current_ftp->cb.ftp;
  952.   if (ftp == NULLFTP)
  953.   {
  954.     cwprintf(ftp->window, notsess);
  955.     return 1;
  956.   }
  957.  
  958.   if (ftp->fp != NULLFILE && ftp->fp != stdout)
  959.     fclose(ftp->fp);
  960.   ftp->restart = 0;  
  961.  
  962.   ftp->fp = NULLFILE;
  963.  
  964.   if (argc < 3)
  965.   {
  966.     ftp->fp = stdout;
  967.   }
  968.   else
  969.   {
  970.     strcpy(localname, argv[2]);
  971.     if (!create_localfile(ftp, localname, 0xfff))
  972.       return 1;
  973.  
  974.     /* Set variable to name of most recent file */
  975.     var_create_string(global_vars, "ftp_list", 0, localname);
  976.  
  977.     if ((ftp->fp = fopen(localname, "w")), ftp->fp == NULLFILE)
  978.     {
  979.       cwprintf(ftp->window, cantwrite, localname);
  980.       return 1;
  981.     }
  982.   }
  983.  
  984.   ftp->state = RECEIVING_STATE1;
  985.   ftp->expected_size = 0;
  986.   ftp->start_time = alarm_timenow () ;
  987.   if ( ftp->type == IMAGE_TYPE )
  988.   {
  989.     ftp->type = IMAGE_PENDING ;
  990.     sndftpmsg(ftp,"TYPE A\r\n");
  991.   }
  992.   ftpsetup(ftp,(void(*)())ftpdr,NULLVFP,(void(*)())ftpcds);
  993.   /* Generate the command to start the transfer */
  994.   return sndftpmsg(ftp,"%s\r\n",argv[1]);
  995. }
  996.  
  997. static int dosite(int argc, char **argv)
  998. {
  999.   int i;
  1000.   char buf[512];
  1001.   char *p = buf;
  1002.   struct ftp *ftp;
  1003.   
  1004.   ftp = current_ftp->cb.ftp;
  1005.  
  1006.   for (i=1; i<argc; i++)
  1007.     p += sprintf(p, "%s%s",(p==buf)?"":" ", argv[i]);
  1008.   
  1009.   return sndftpmsg(ftp,"%s\r\n",buf);
  1010. }
  1011.  
  1012.  
  1013. /* Set current path processing options */
  1014. static int dopath(int argc, char **argv)
  1015. {
  1016.   int i, n;
  1017.   struct ftp *ftp;
  1018.   char opts[256];
  1019.   char *p;
  1020.   pathent_str *pe;
  1021.  
  1022.   ftp = current_ftp->cb.ftp;
  1023.  
  1024.   if (ftp == NULLFTP)
  1025.   {
  1026.     cwprintf(NULL, notsess);
  1027.     return 1;
  1028.   }
  1029.  
  1030.   if (argc>1)
  1031.   {
  1032.     if ((argc==2) && !strncmp(argv[1], "off", strlen(argv[1])))
  1033.     {
  1034.       delpathent(ftp->pathopts);
  1035.       ftp->pathopts = PATH_OFF;
  1036.     }
  1037.     else if ((argc==2) && !strncmp(argv[1], "auto", strlen(argv[1])))
  1038.     {
  1039.       delpathent(ftp->pathopts);
  1040.       ftp->pathopts = PATH_DEF;
  1041.     }
  1042.     else if ((argc==2) && !strncmp(argv[1], "type", strlen(argv[1])))
  1043.     {
  1044.       delpathent(ftp->pathopts);
  1045.       ftp->pathopts = PATH_TYPE;
  1046.     }
  1047.     else if ((argc>=2) && !strncmp(argv[1], "test", strlen(argv[1])))
  1048.     {
  1049.       int filetype;
  1050.       char filename[256];
  1051.  
  1052.       if (pathmap(argv[2], filename, &filetype, ftp->pathopts))
  1053.         cwprintf(ftp->window, "%s (%s)\r\n", filename, fttoa(filetype));
  1054.       else
  1055.         cwprintf(ftp->window, "No convertion applicable\r\n");
  1056.     }
  1057.     else
  1058.     {
  1059.       /* Reconstrunct original path command */
  1060.       *opts = '$';
  1061.       p = opts+1;
  1062.       for (i=1; i<argc; i++)
  1063.       {
  1064.         n = sprintf(p, " %s", argv[i]);
  1065.         p += n;
  1066.       }
  1067.       if (pe = getpathent(opts), pe!=NULL)
  1068.       {
  1069.         delpathent(ftp->pathopts);
  1070.         ftp->pathopts = pe;
  1071.       }
  1072.       else
  1073.       {
  1074.         cwprintf(ftp->window, "Parse error in path options.\r\n");
  1075.         prompt(ftp);
  1076.         return 1;
  1077.       }
  1078.     }
  1079.   }
  1080.   else
  1081.   {
  1082.     if (ftp->pathopts == PATH_OFF)
  1083.       cwprintf(ftp->window, "Path processing off\r\n");
  1084.     else if (ftp->pathopts == PATH_DEF)
  1085.       cwprintf(ftp->window, "Auto path processing by extension\r\n");
  1086.     else
  1087.     {
  1088.       cwprintf(ftp->window, "Preset path processing:\r\n");
  1089.       cwprintf(ftp->window, "  Path : %s\r\n", (ftp->pathopts->path)?ftp->pathopts->path:"<none>");
  1090.       cwprintf(ftp->window, "  Exts :");
  1091.       for (n=0; n<ftp->pathopts->numexts; n++)
  1092.         cwprintf(ftp->window, " %s", ftp->pathopts->exts[n]);
  1093.       cwprintf(ftp->window, "\r\n");
  1094.       cwprintf(ftp->window, "  On   : %s\r\n", pe_optflagstostr(ftp->pathopts->setopts, opts) );
  1095.       cwprintf(ftp->window, "  Off  : %s\r\n", pe_optflagstostr(ftp->pathopts->clropts | (1<<31), opts) );
  1096.       cwprintf(ftp->window, "  Type : %s\r\n", fttoa(ftp->pathopts->filetype));
  1097.       cwprintf(ftp->window, "\r\n");
  1098.     }
  1099.   }
  1100.  
  1101.   prompt(ftp);
  1102.   return 0;
  1103. }
  1104.  
  1105.  
  1106. /*
  1107.  * Start processing a batch file.
  1108.  */
  1109. static int dobatch(int argc, char **argv)
  1110. {
  1111.   int a;
  1112.   struct ftp *ftp;
  1113.  
  1114.   ftp = current_ftp->cb.ftp;
  1115.  
  1116.   if (ftp == NULLFTP)
  1117.   {
  1118.     cwprintf(NULL, notsess);
  1119.     return 1;
  1120.   }
  1121.  
  1122.   if (ftp->batch!=NULL)
  1123.   {
  1124.     cwprintf(ftp->window, "Batch allready in progress.\r\n");
  1125.     return 1;
  1126.   }
  1127.  
  1128.   if (argc<2)
  1129.   {
  1130.     cwprintf(ftp->window, "No batch in progress.\r\n");
  1131.     return 0;
  1132.   }
  1133.  
  1134.   if (a = batch_open(ftp, argv[1]), a==0)
  1135.     cwprintf(ftp->window, "Cant find batch file \"%s\".\r\n", argv[1]);
  1136.  
  1137.   prompt(ftp);
  1138.   return a;
  1139. }
  1140.  
  1141. static int batch_open(struct ftp *ftp, char *batch)
  1142. {
  1143.   extern char scripts[];
  1144.   char file[256];
  1145.  
  1146.   if (strpbrk(batch, "@&%$:<>"))
  1147.     sprintf(file, "%s", batch);
  1148.   else
  1149.     sprintf(file, "%s.%s", scripts, batch);
  1150.   if (ftp->batch = fopen(file, "r"), ftp->batch!=NULL)
  1151.     return 1;
  1152.  
  1153.   sprintf(file, "%s%s", RESROOT, batch);
  1154.   if (ftp->batch = fopen(file, "r"), ftp->batch!=NULL)
  1155.     return 1;
  1156.     
  1157.   sprintf(file, "%s^.%s", RESROOT, batch);
  1158.   if (ftp->batch = fopen(file, "r"), ftp->batch!=NULL)
  1159.     return 1;
  1160.     
  1161.   sprintf(file, "@.%s", batch);
  1162.   if (ftp->batch = fopen(file, "r"), ftp->batch!=NULL)
  1163.     return 1;
  1164.     
  1165.   sprintf(file, "$.%s", batch);
  1166.   if (ftp->batch = fopen(file, "r"), ftp->batch!=NULL)
  1167.     return 1;
  1168.     
  1169.   cwprintf(ftp->window, "Cant find batch file \"%s\".\r\n", batch);
  1170.   return 0;
  1171. }
  1172.  
  1173.  
  1174. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  1175. static int doget(int argc, char **argv)
  1176. {
  1177.   char *remotename, localname[256];
  1178.   register struct ftp *ftp;
  1179.   char *mode;
  1180.  
  1181.   ftp = current_ftp->cb.ftp;
  1182.   if (ftp == NULLFTP)
  1183.   {
  1184.     cwprintf(NULL, notsess);
  1185.     return 1;
  1186.   }
  1187.   remotename = argv[1];
  1188.  
  1189.   if (!create_fetchedfile(ftp, localname, remotename, (argc<3)?NULL:argv[2]))
  1190.     return 1;
  1191.  
  1192.   /* Set variable to name of most recent file */
  1193.   var_create_string(global_vars, "ftp_data", 0, localname);
  1194.  
  1195.   if (ftp->fp != NULLFILE && ftp->fp != stdout)
  1196.     fclose(ftp->fp);
  1197.   ftp->restart = 0;  
  1198.  
  1199.   ftp->fp = NULLFILE;
  1200.  
  1201.   if (ftp->type == IMAGE_PENDING)
  1202.   {
  1203.     ftp->type = IMAGE_TYPE ;
  1204.     sndftpmsg(ftp,"TYPE I\r\n");
  1205.   }
  1206.  
  1207.   if (ftp->type == IMAGE_TYPE)
  1208.     mode = binmode[WRITE_BINARY];
  1209.   else
  1210.     mode = "w";
  1211.  
  1212.   if ((ftp->fp = fopen(localname,mode)) == NULLFILE)
  1213.   {
  1214.     cwprintf(ftp->window, cantwrite, localname);
  1215.     return 1;
  1216.   }
  1217.   if (ftp->type == IMAGE_TYPE)
  1218.     setvbuf(ftp->fp, NULL, _IOFBF, 16384);
  1219.   
  1220.   ftp->last = 0;
  1221.   ftp->state = RECEIVING_STATE1;
  1222.   ftp->expected_size = 0;
  1223.   ftp->start_time = alarm_timenow () ;
  1224.   ftpsetup(ftp,(void(*)())ftpdr, NULLVFP, (void(*)())ftpcds);
  1225.  
  1226.   /* Generate the command to start the transfer */
  1227.   return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  1228. }
  1229.  
  1230. static int doreget(int argc, char **argv)
  1231. {
  1232.   char *remotename, localname[256];
  1233.   register struct ftp *ftp;
  1234.   char *mode;
  1235.  
  1236.   ftp = current_ftp->cb.ftp;
  1237.   if (ftp == NULLFTP)
  1238.   {
  1239.     cwprintf(NULL, notsess);
  1240.     return 1;
  1241.   }
  1242.   remotename = argv[1];
  1243.  
  1244.   if (!create_fetchedfile(ftp, localname, remotename, (argc<3)?NULL:argv[2]))
  1245.     return 1;
  1246.  
  1247.   if (ftp->fp != NULLFILE && ftp->fp != stdout)
  1248.     fclose(ftp->fp);
  1249.  
  1250.   ftp->fp = NULLFILE;
  1251.  
  1252.   if (ftp->type == IMAGE_PENDING)
  1253.   {
  1254.     ftp->type = IMAGE_TYPE ;
  1255.     sndftpmsg(ftp,"TYPE I\r\n");
  1256.   }
  1257.  
  1258.   if (ftp->type == IMAGE_TYPE)
  1259.     mode = "ab";
  1260.   else
  1261.     mode = "a";
  1262.  
  1263.   if ((ftp->fp = fopen(localname,mode)) == NULLFILE)
  1264.   {
  1265.     cwprintf(ftp->window, cantwrite, localname);
  1266.     return 1;
  1267.   }
  1268.   if (ftp->type == IMAGE_TYPE)
  1269.     setvbuf(ftp->fp, NULL, _IOFBF, 16384);
  1270.  
  1271.   ftp->restart = ftell(ftp->fp);
  1272.   
  1273.   ftp->last = 0;
  1274.   ftp->state = RECEIVING_STATE1;
  1275.   ftp->expected_size = 0;
  1276.   ftp->start_time = alarm_timenow () ;
  1277.   ftpsetup(ftp,(void(*)())ftpdr, NULLVFP, (void(*)())ftpcds);
  1278.  
  1279.   /* Generate the commands to start the transfer */
  1280.   sndftpmsg(ftp,"REST %ld\r\n",ftp->restart);
  1281.   return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  1282. }
  1283.  
  1284. /* Start receive transfer. Syntax: view <remote name> */
  1285. static int doview(int argc, char **argv)
  1286. {
  1287.   char *remotename;
  1288.   register struct ftp *ftp;
  1289.  
  1290.   ftp = current_ftp->cb.ftp;
  1291.   if (ftp == NULLFTP)
  1292.   {
  1293.     cwprintf(ftp->window, notsess);
  1294.     return 1;
  1295.   }
  1296.   remotename = argv[1];
  1297.  
  1298.   if (ftp->fp != NULLFILE && ftp->fp != stdout)
  1299.     fclose(ftp->fp);
  1300.   ftp->fp = NULLFILE;
  1301.   ftp->restart = 0;  
  1302.  
  1303.   if (ftp->type == IMAGE_PENDING)
  1304.   {
  1305.     ftp->type = IMAGE_TYPE ;
  1306.     sndftpmsg(ftp,"TYPE I\r\n");
  1307.   }
  1308.  
  1309.   ftp->fp = stdout ;
  1310.   ftp->last = 0;
  1311.   ftp->state = RECEIVING_STATE1;
  1312.   ftp->expected_size = 0;
  1313.   ftp->start_time = alarm_timenow () ;
  1314.   ftpsetup(ftp,(void(*)())ftpdr, NULLVFP, (void(*)())ftpcds);
  1315.   /* Generate the command to start the transfer */
  1316.   return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  1317. }
  1318.  
  1319. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  1320. static int dolist(int argc, char **argv)
  1321. {
  1322.   char localname[256];
  1323.   register struct ftp *ftp;
  1324.  
  1325.   ftp = current_ftp->cb.ftp;
  1326.   if (ftp == NULLFTP)
  1327.   {
  1328.     cwprintf(ftp->window, notsess);
  1329.     return 1;
  1330.   }
  1331.   if (ftp->fp != NULLFILE && ftp->fp != stdout)
  1332.     fclose(ftp->fp);
  1333.   ftp->fp = NULLFILE;
  1334.   ftp->restart = 0;  
  1335.  
  1336.   if (argc < 3)
  1337.   {
  1338.     ftp->fp = stdout;
  1339.   }
  1340.   else
  1341.   {
  1342.     strcpy(localname, argv[2]);
  1343.     if (!create_localfile(ftp, localname, 0xfff))
  1344.       return 1;
  1345.  
  1346.     /* Set variable to name of most recent file */
  1347.     var_create_string(global_vars, "ftp_list", 0, localname);
  1348.  
  1349.     if ((ftp->fp = fopen(localname, "w")), ftp->fp == NULLFILE)
  1350.     {
  1351.       cwprintf(ftp->window, cantwrite, localname);
  1352.       return 1;
  1353.     }
  1354.   }
  1355.   ftp->state = RECEIVING_STATE1;
  1356.   ftp->expected_size = 0;
  1357.   ftp->start_time = alarm_timenow () ;
  1358.   ftpsetup(ftp,(void(*)())ftpdr,NULLVFP,(void(*)())ftpcds);
  1359.   /* Generate the command to start the transfer
  1360.      It's done this way to avoid confusing the 4.2 FTP server
  1361.      if there's no argument */
  1362.   if (argc > 1)
  1363.   {
  1364.     if ( ftp->type == IMAGE_TYPE )
  1365.     {
  1366.       ftp->type = IMAGE_PENDING ;
  1367.       sndftpmsg(ftp,"TYPE A\r\n");
  1368.     }
  1369.     return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
  1370.   }
  1371.   else
  1372.   {
  1373.     if ( ftp->type == IMAGE_TYPE )
  1374.     {
  1375.       ftp->type = IMAGE_PENDING ;
  1376.       sndftpmsg(ftp,"TYPE A\r\n");
  1377.     }
  1378.     return sndftpmsg(ftp,"LIST\r\n","");
  1379.   }
  1380. }
  1381. /* Abbreviated (name only) list of remote directory.
  1382.  * Syntax: ls <remote directory/file> [<local name>]
  1383.  */
  1384. static int dols(int argc, char **argv)
  1385. {
  1386.   char localname[256];
  1387.   register struct ftp *ftp;
  1388.  
  1389.   ftp = current_ftp->cb.ftp;
  1390.   if (ftp == NULLFTP)
  1391.   {
  1392.     cwprintf(ftp->window, notsess);
  1393.     return 1;
  1394.   }
  1395.   if (ftp->fp != NULLFILE && ftp->fp != stdout)
  1396.     fclose(ftp->fp);
  1397.   ftp->fp = NULLFILE;
  1398.   ftp->restart = 0;  
  1399.  
  1400.   if (argc < 3)
  1401.   {
  1402.     ftp->fp = stdout;
  1403.   }
  1404.   else
  1405.   {
  1406.     strcpy(localname, argv[2]);
  1407.     if (!create_localfile(ftp, localname, 0xfff))
  1408.       return 1;
  1409.  
  1410.     /* Set variable to name of most recent file */
  1411.     var_create_string(global_vars, "ftp_list", 0, localname);
  1412.  
  1413.     if ((ftp->fp = fopen(localname, "w")), ftp->fp == NULLFILE)
  1414.     {
  1415.       cwprintf(ftp->window, cantwrite,argv[2]);
  1416.       return 1;
  1417.     }
  1418.   }
  1419.   ftp->state = RECEIVING_STATE1;
  1420.   ftp->expected_size = 0;
  1421.   ftp->start_time = alarm_timenow () ;
  1422.  
  1423.   if ( ftp->type == IMAGE_TYPE )
  1424.   {
  1425.     ftp->type = IMAGE_PENDING ;
  1426.     sndftpmsg(ftp,"TYPE A\r\n");
  1427.   }
  1428.  
  1429.   ftpsetup(ftp,(void(*)())ftpdr,NULLVFP,(void(*)())ftpcds);
  1430.   /* Generate the command to start the transfer */
  1431.   if (argc > 1)
  1432.   {
  1433.     return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
  1434.   }
  1435.   else
  1436.   {
  1437.     return sndftpmsg(ftp,"NLST\r\n","");
  1438.   }
  1439. }
  1440. /* Start transmit. Syntax: put <local name> [<remote name>] */
  1441. static int doput(int argc, char **argv)
  1442. {
  1443.   char *remotename,*localname;
  1444.   char *mode;
  1445.   struct ftp *ftp;
  1446.  
  1447.   if ((ftp = current_ftp->cb.ftp) == NULLFTP)
  1448.   {
  1449.     cwprintf(ftp->window, notsess);
  1450.     return 1;
  1451.   }
  1452.   localname = argv[1];
  1453.   if (argc < 3)
  1454.     remotename = localname;
  1455.   else
  1456.       remotename = argv[2];
  1457.  
  1458.   if (ftp->fp != NULLFILE && ftp->fp != stdout)
  1459.     fclose(ftp->fp);
  1460.   ftp->restart = 0;  
  1461.  
  1462.   if (ftp->type == IMAGE_PENDING)
  1463.   {
  1464.     ftp->type = IMAGE_TYPE ;
  1465.     sndftpmsg(ftp,"TYPE I\r\n");
  1466.   }
  1467.  
  1468.   if (ftp->type == IMAGE_TYPE)
  1469.     mode = binmode[READ_BINARY];
  1470.   else
  1471.     mode = "r";
  1472.  
  1473.   if ((ftp->fp = fopen(localname,mode)) == NULLFILE)
  1474.   {
  1475.     cwprintf(ftp->window, cantread, localname);
  1476.     return 1;
  1477.   }
  1478.   fseek(ftp->fp,0,SEEK_END);
  1479.   ftp->expected_size = ftell(ftp->fp);
  1480.   fseek(ftp->fp,0,SEEK_SET);
  1481.   ftp->last = 0;
  1482.   ftp->state = SENDING_FILE_STATE1;
  1483.   ftp->start_time = alarm_timenow () ;
  1484.   ftpsetup(ftp, NULLVFP, (void(*)())ftpdt, (void(*)())ftpcds);
  1485.  
  1486.   /* Generate the command to start the transfer */
  1487.   return sndftpmsg(ftp,"STOR %s\r\n",remotename);
  1488. }
  1489. /* Abort a GET or PUT operation in progress. Note: this will leave
  1490.  * the partial file on the local or remote system
  1491.  */
  1492. int doabort(int argc, char **argv)
  1493. {
  1494.   register struct ftp *ftp;
  1495.  
  1496.   argc = argc;
  1497.   argv = argv;
  1498.  
  1499.   ftp = current_ftp->cb.ftp;
  1500.  
  1501.   /* Close the local file */
  1502.   if (ftp->fp != NULLFILE && ftp->fp != stdout)
  1503.     fclose(ftp->fp);
  1504.   ftp->fp = NULLFILE;
  1505.  
  1506.   ftp->restart = 0;
  1507.   ftp->replycount = 0;
  1508.  
  1509.   ftp_mpcleanup(ftp);
  1510.  
  1511.   if (ftp->batch)
  1512.   {
  1513.     cwprintf(ftp->window, "Batch file closed\r\n");
  1514.     fclose(ftp->batch);
  1515.     ftp->batch = NULL;
  1516.   }
  1517.  
  1518.   switch(ftp->state)
  1519.   {
  1520.   case SENDING_FILE_STATE1:
  1521.   case SENDING_FILE_STATE2:
  1522.     /* Send a premature EOF.
  1523.        Unfortunately we can't just reset the connection
  1524.        since the remote side might end up waiting forever
  1525.        for us to send something. */
  1526.     close_tcp(ftp->data);
  1527.     cwprintf(ftp->window, "Put aborted\r\n");
  1528.     break;
  1529.   case RECEIVING_STATE1:
  1530.   case RECEIVING_STATE2:
  1531.     /* Just exterminate the data channel TCB; this will
  1532.                      * generate a RST on the next data packet which will
  1533.                      * abort the sender
  1534.                      */
  1535.     del_tcp(ftp->data);
  1536.     ftp->data = NULLTCB;
  1537.     cwprintf(ftp->window, "Get aborted\r\n");
  1538.     break;
  1539.   }
  1540.  
  1541.   ftp->state = COMMAND_STATE;
  1542.   prompt(ftp);
  1543.   return 0;
  1544. }
  1545. /* create data port, and send PORT message */
  1546. static void ftpsetup(struct ftp *ftp, void (*recv)(),
  1547.                      void (*send)(), void (*state)())
  1548. {
  1549.   struct socket lsocket;
  1550.   struct mbuf *bp;
  1551.  
  1552.   lsocket.address = ip_addr;
  1553.   lsocket.port = lport++;
  1554.  
  1555.   /* Compose and send PORT a,a,a,a,p,p message */
  1556.  
  1557.   if ((bp = alloc_mbuf(35)) == NULLBUF)
  1558.   {   /* 5 more than worst case */
  1559.     cwprintf(ftp->window, nospace);
  1560.     return;
  1561.   }
  1562.   /* I know, this looks gross, but it works! */
  1563.   sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  1564.           hibyte(hiword(lsocket.address)),
  1565.           lobyte(hiword(lsocket.address)),
  1566.           hibyte(loword(lsocket.address)),
  1567.           lobyte(loword(lsocket.address)),
  1568.           hibyte(lsocket.port),
  1569.           lobyte(lsocket.port) );
  1570.   bp->cnt = strlen(bp->data);
  1571.   send_tcp(ftp->control,bp);
  1572.   ftp->replycount++;
  1573.  
  1574.   /* Post a listen on the data connection */
  1575.   ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
  1576.                        recv,send,state,0,(char *)ftp);
  1577. }
  1578.  
  1579. /* FTP Client Control channel Receiver upcall routine */
  1580. void ftpccr(register struct tcb *tcb, int16 cnt)
  1581. {
  1582.   extern int ttyflow;
  1583.   struct mbuf *bp;
  1584.   struct ftp *ftp;
  1585.   char c;
  1586.  
  1587.   if ((ftp = (struct ftp *)tcb->user) == NULLFTP)
  1588.   {
  1589.     /* Unknown connection; kill it */
  1590.     close_tcp(tcb);
  1591.     return;
  1592.   }
  1593.   /* Hold output if we're not the current session */
  1594.   if (ftp->window == NULL && (mode != CONV_MODE || current == NULLSESSION
  1595.       || ttyflow == 0 || current->cb.ftp != ftp))
  1596.     return;
  1597.  
  1598.   if (recv_tcp(tcb,&bp,cnt) > 0)
  1599.   {
  1600.     while(pullone(&bp,&c) == 1)
  1601.   {
  1602.       switch(c)
  1603.   {
  1604.       case '\n':      /* Complete line; process it */
  1605.  
  1606.         ftp->buf[ftp->cnt] = '\0';
  1607.         doreply(ftp);
  1608.         ftp->cnt = 0;
  1609.         break;
  1610.       default:
  1611.         if (ftp->cnt != LINELEN-1)
  1612.           ftp->buf[ftp->cnt++] = c;
  1613.         break;
  1614.       }
  1615.     }
  1616.   }
  1617. }
  1618.  
  1619. int extract_line_number (char *l)
  1620. {
  1621.   int i=0, c;
  1622.  
  1623.   for (c=0; c<3; c++)
  1624.   {
  1625.     if (isdigit(l[c]))
  1626.       i = i*10 + (l[c]-'0');
  1627.     else
  1628.       break;
  1629.   }
  1630.   return (i);
  1631. }
  1632.  
  1633. /* Process replies from the server */
  1634. static void doreply(register struct ftp *ftp)
  1635. {
  1636.   int code, more;
  1637.  
  1638.   if (ftp->cnt<3)
  1639.     return;
  1640.  
  1641.   if (ftp->trace>9)
  1642.     cwprintf(ftp->window, "Cur State: %d\r\n", ftp->state);
  1643.  
  1644.   code = extract_line_number(ftp->buf);
  1645.   more = (ftp->buf[3]=='-');
  1646.  
  1647.   if (ftp->state == CONTINUATION_STATE)
  1648.   {
  1649.     if (ftp->continue_val == code)
  1650.       ftp->state = ftp->last_state;
  1651.     else
  1652.       return;
  1653.   }
  1654.  
  1655.   if (more)
  1656.   {
  1657.     cwprintf(ftp->window, "%s\r\n", (ftp->trace)?ftp->buf:ftp->buf+4);
  1658.     ftp->last_state = ftp->state;
  1659.     ftp->state = CONTINUATION_STATE;
  1660.     ftp->continue_val = code;
  1661.     return;
  1662.   }
  1663.  
  1664.   switch(ftp->state)
  1665.   {
  1666.     case SENDING_FILE_STATE1:
  1667.     case SENDING_FILE_STATE2:
  1668.     case RECEIVING_STATE1:
  1669.     case RECEIVING_STATE2:
  1670.       --ftp->replycount;
  1671.       if (ftp->buf[0] == '4' || ftp->buf[0] == '5')
  1672.       {
  1673.         char *s[1];
  1674.         doabort(0, s);
  1675.       }
  1676.       else if (code == 150)
  1677.       {
  1678.         /* Opening message - see if we can find a file size... */
  1679.         char *s = strchr(ftp->buf , '(');
  1680.         if (sscanf(s, "(%lu bytes)", &ftp->expected_size) < 1)
  1681.           ftp->expected_size = 0;
  1682.       }
  1683.       else if (code==226)
  1684.       {
  1685.         if (ftp->state == SENDING_FILE_STATE1)
  1686.           ftp->state = SENDING_FILE_STATE2;
  1687.         else if (ftp->state == SENDING_FILE_STATE2)
  1688.           ftp->state = COMMAND_STATE;
  1689.         else if (ftp->state == RECEIVING_STATE1)
  1690.           ftp->state = RECEIVING_STATE2;
  1691.         else if (ftp->state == RECEIVING_STATE2)
  1692.           ftp->state = COMMAND_STATE;
  1693.       }
  1694.       if (ftp->trace || ftp->buf[0] == '4' || ftp->buf[0] == '5')
  1695.         cwprintf(ftp->window, "%s\r\n", (ftp->trace)?ftp->buf:ftp->buf+4);
  1696.       break;
  1697.     case STARTUP_STATE:
  1698.       cwprintf(ftp->window, "%s\r\n", (ftp->trace)?ftp->buf:ftp->buf+4);
  1699.       if (code==220)
  1700.       {
  1701.         ftp->state = USER_STATE;
  1702.         if (ftp->trace || !ftp->username)
  1703.           cwprintf(ftp->window, "Enter user name: ");
  1704.         if (ftp->username)
  1705.         {
  1706.           cwprintf(ftp->window, "Username Entered\r\n");
  1707.           sndftpmsg(ftp,"USER %s\r\n",ftp->username);
  1708.         }
  1709.       }
  1710.       else
  1711.       {
  1712.         ftp->state = COMMAND_STATE;
  1713.         ftp->replycount = 0;
  1714.       }
  1715.       break;
  1716.     case USER_STATE:
  1717.       --ftp->replycount;
  1718.       cwprintf(ftp->window, "%s\r\n", (ftp->trace)?ftp->buf:ftp->buf+4);
  1719.       if (code==331)
  1720.       {
  1721.         ftp->state = PASS_STATE;
  1722.         noecho();
  1723.         ftp->session->echo = FALSE;
  1724.         if (ftp->trace || !ftp->password)
  1725.           cwprintf(ftp->window, "Password: ");
  1726.         vterm_setflags(ftp->window->vt,VTSW_ECHO,0);
  1727.         if (ftp->password)
  1728.         {
  1729.           cwprintf(ftp->window, "Password Entered\r\n");
  1730.           sndftpmsg(ftp,"PASS %s\r\n",ftp->password);
  1731.         }
  1732.       }
  1733.       else
  1734.       {
  1735.         ftp->state = COMMAND_STATE;
  1736.         ftp->replycount = 0;
  1737.       }
  1738.       break;
  1739.     case PASS_STATE:
  1740.       --ftp->replycount;
  1741.       cwprintf(ftp->window, "%s\r\n", (ftp->trace)?ftp->buf:ftp->buf+4);
  1742.       echo();
  1743.       ftp->session->echo = TRUE;
  1744.       ftp->state = COMMAND_STATE;
  1745.       ftp->replycount = 0;
  1746.       vterm_setflags(ftp->window->vt,VTSW_ECHO,VTSW_ECHO);
  1747.       /*
  1748.        * Set default transfer type at server
  1749.        */
  1750.       ftp->type = (default_type==IMAGE_TYPE)?IMAGE_TYPE:ASCII_TYPE;
  1751.       sndftpmsg(ftp,"TYPE %c\r\n",(default_type==IMAGE_TYPE)?'I':'A');
  1752.       /*
  1753.        * Set the default directory if given and
  1754.        * do a pwd to get the name of the dir you end up in
  1755.        */
  1756.       ftp->flags |= FTP_TRAP_257;
  1757.       if (ftp->cd!=NULL)
  1758.         sndftpmsg(ftp, "CWD %s\r\n", ftp->cd);
  1759.       sndftpmsg(ftp,"PWD\r\n");
  1760.       break;
  1761.     case COMMAND_STATE:
  1762.       switch (code)
  1763.       {
  1764.         case 257:
  1765.           /*
  1766.            * Capture current directory
  1767.            */
  1768.           if (ftp->flags & FTP_TRAP_257)
  1769.           {
  1770.             char *p, *q;
  1771.  
  1772.             if (ftp->cd!=NULL)
  1773.               free(ftp->cd);
  1774.  
  1775.             if (p = strchr(ftp->buf+4, '\"'), p!=NULL)
  1776.             {
  1777.               if (q = strchr(p+1, '\"'), q!=NULL)
  1778.                 ftp->cd = strndup(p+1, q-(p+1));
  1779.             }
  1780.             ftp->flags &= ~FTP_TRAP_257;
  1781.           }
  1782.  
  1783.           if (ftp->flags & FTP_SUPP_257 && !ftp->trace)
  1784.           {
  1785.             ftp->flags &= ~FTP_SUPP_257;
  1786.             break;
  1787.           }
  1788.  
  1789.         default:
  1790.           cwprintf(ftp->window, "%s\r\n", (ftp->trace)?ftp->buf:ftp->buf+4);
  1791.       }
  1792.       --ftp->replycount;
  1793.       break;
  1794.   }
  1795.   if (ftp->trace>9)
  1796.     cwprintf(ftp->window, "State now: %d\r\n", ftp->state);
  1797.   if (ftp->trace>9)
  1798.     cwprintf(ftp->window, "Reply counter: %d\r\n", ftp->replycount);
  1799.  
  1800.   if (ftp->replycount<=0 && ftp->state==COMMAND_STATE)
  1801.   {
  1802.     ftp->replycount = 0;
  1803.     prompt(ftp);
  1804.   }
  1805. }
  1806.  
  1807. /* FTP Client Control channel State change upcall routine */
  1808. static void ftpccs(register struct tcb *tcb, char old, char new)
  1809. {
  1810.   extern int ttyflow;
  1811.   struct ftp *ftp;
  1812.   char notify = 0;
  1813.   extern char *tcpstates[];
  1814.   extern char *reasons[];
  1815.   extern char *unreach[];
  1816.   extern char *exceed[];
  1817.  
  1818.   old = old;
  1819.  
  1820.   /* Can't add a check for unknown connection here, it would loop
  1821.            * on a close upcall! We're just careful later on.
  1822.            */
  1823.   ftp = (struct ftp *)tcb->user;
  1824.  
  1825.   if (ftp->window || (ttyflow && current != NULLSESSION && current->cb.ftp == ftp))
  1826.     notify = 1;
  1827.  
  1828.   switch(new)
  1829.   {
  1830.   case CLOSE_WAIT:
  1831.     if (notify)
  1832.       cwprintf(ftp->window, "%s\r\n",tcpstates[new]);
  1833.     close_tcp(tcb);
  1834.     break;
  1835.   case CLOSED:    /* heh heh */
  1836.     if (notify)
  1837.     {
  1838.       cwprintf(ftp->window, "%s (%s",tcpstates[new],reasons[tcb->reason]);
  1839.       if (tcb->reason == NETWORK)
  1840.       {
  1841.         switch(tcb->type)
  1842.         {
  1843.         case DEST_UNREACH:
  1844.           cwprintf(ftp->window, ": %s unreachable",unreach[tcb->code]);
  1845.           break;
  1846.         case TIME_EXCEED:
  1847.           cwprintf(ftp->window, ": %s time exceeded",exceed[tcb->code]);
  1848.           break;
  1849.         }
  1850.       }
  1851.       cwprintf(ftp->window, ")\r\n");
  1852.       cmdmode();
  1853.     }
  1854.     del_tcp(tcb);
  1855.     if (ftp != NULLFTP)
  1856.     {
  1857.       if (ftp->window)
  1858.       {
  1859.         ftp->window->Attr = ATTR_REVERSE;
  1860.         ftp->window->Flags.flags.dont_destroy = FALSE;
  1861.         ftp->window->Session = NULL;
  1862.         Window_CloseDown(ftp->window);
  1863.       }
  1864.       ftp_delete(ftp);
  1865.     }
  1866.     break;
  1867.   default:
  1868.     if (notify)
  1869.       cwprintf(ftp->window, "%s\r\n",tcpstates[new]);
  1870.     break;
  1871.   }
  1872. }
  1873. /* FTP Client Data channel State change upcall handler */
  1874. static void ftpcds(struct tcb *tcb, char old, char new)
  1875. {
  1876.   extern int ttyflow;
  1877.   struct ftp *ftp;
  1878.  
  1879.   old = old;
  1880.  
  1881.   if ((ftp = (struct ftp *)tcb->user) == NULLFTP)
  1882.   {
  1883.     /* Unknown connection, kill it */
  1884.     close_tcp(tcb);
  1885.     return;
  1886.   }
  1887.   switch(new)
  1888.   {
  1889. /*  case FINWAIT2: */
  1890.   case TIME_WAIT:
  1891.     if (ftp->state == SENDING_FILE_STATE1 || ftp->state == SENDING_FILE_STATE2)
  1892.     {
  1893.       /* We've received an ack of our FIN, so
  1894.        * return to command mode
  1895.        */
  1896.       if (ftp->state == SENDING_FILE_STATE1)
  1897.         ftp->state = SENDING_FILE_STATE2;
  1898.       else
  1899.         ftp->state = COMMAND_STATE;
  1900.       ftp->replycount = 0;
  1901.       if (ftp->window != NULL || (ttyflow && current != NULLSESSION && current->cb.ftp == ftp))
  1902.       {
  1903.         int  a = alarm_timedifference(ftp->start_time, alarm_timenow()) / 100;
  1904.         long b = tcb->snd.una - tcb->iss - 2;
  1905.  
  1906.         if (ftp->hash > 0)
  1907.           cwputchar(ftp->window, '\n');
  1908.         if (a == 0)
  1909.           a++;
  1910.         cwprintf(ftp->window, "Put complete, %lu bytes sent (%lu cps)\r\n", b, b/a);
  1911.         if (ftp->state == COMMAND_STATE)
  1912.           prompt(ftp);
  1913.       }
  1914.     }
  1915.     break;
  1916.   case CLOSE_WAIT:
  1917.     close_tcp(tcb);
  1918.     if (ftp->state == RECEIVING_STATE1 || ftp->state == RECEIVING_STATE2)
  1919.     {
  1920.       /* End of file received on incoming file */
  1921.       int to_screen = (ftp->fp==stdout);
  1922.  
  1923.       if (ftp->fp != stdout)
  1924.         fclose(ftp->fp);
  1925.       ftp->fp = NULLFILE;
  1926.       if (ftp->state == RECEIVING_STATE1)
  1927.         ftp->state = RECEIVING_STATE2;
  1928.       else
  1929.         ftp->state = COMMAND_STATE;
  1930.       ftp->replycount = 0;
  1931.       if (ftp->window != NULL || (ttyflow && current != NULLSESSION && current->cb.ftp == ftp))
  1932.       {
  1933.         int  a;
  1934.         long b;
  1935.         if (!to_screen || ftp->trace)
  1936.         {
  1937.           a = alarm_timedifference(ftp->start_time, alarm_timenow()) / 100;
  1938.           b = tcb->rcv.nxt - tcb->irs - 2;
  1939.  
  1940.           if ( ftp->hash > 0 )
  1941.             cwputchar(ftp->window, '\n');
  1942.           if (a == 0)
  1943.             a++;
  1944.           if (ftp->restart)
  1945.           {
  1946.             cwprintf(ftp->window, "Get complete, remaining %lu of %lu bytes received (%lu cps)\r\n",
  1947.                      b, b+ftp->restart, b/a);
  1948.             ftp->restart = 0;
  1949.           }
  1950.           else
  1951.             cwprintf(ftp->window, "Get complete, %lu bytes received (%lu cps)\r\n", b, b/a);
  1952.         }
  1953.         if (ftp->state == COMMAND_STATE)
  1954.           prompt(ftp);
  1955.       }
  1956.     }
  1957.     break;
  1958.   case CLOSED:
  1959.     ftp->data = NULLTCB;
  1960.     del_tcp(tcb);
  1961.     break;
  1962.   }
  1963. }
  1964.  
  1965. /* Send a message on the control channel */
  1966. static int sndftpmsg(struct ftp *ftp, char *fmt, ...)
  1967. {
  1968.   va_list argptr;
  1969.   struct mbuf *bp;
  1970.   int16 len;
  1971.  
  1972.   len = strlen(fmt)+256;
  1973.  
  1974.   if ((bp = alloc_mbuf(len)) == NULLBUF)
  1975.   {
  1976.     cwprintf(ftp->window, nospace);
  1977.     return 1;
  1978.   }
  1979.  
  1980.   va_start(argptr,fmt);
  1981.   vsprintf(bp->data,fmt,argptr);
  1982.   va_end(argptr);
  1983.   bp->cnt = strlen(bp->data);
  1984.   send_tcp(ftp->control,bp);
  1985.  
  1986.   ftp->replycount++;
  1987.  
  1988.   return 0;
  1989. }
  1990.  
  1991.  
  1992. static void prompt(struct ftp *ftp)
  1993. {
  1994.   /*
  1995.    * A conditional newline - only doe a newline if cursor not at start of line
  1996.    */
  1997.   vterm_newline(ftp->window->vt);
  1998.  
  1999.   if (ftp->state==RECEIVING_STATE1 || ftp->state==SENDING_FILE_STATE1 &&
  2000.       ftp->state==RECEIVING_STATE2 || ftp->state==SENDING_FILE_STATE2)
  2001.     cwprintf(ftp->window, "%s- ",(ftp->batch)?"batch":(ftp->mpstate==FTPM_IDLE)?"ftp":"mget");
  2002.   else if (!mprocess(ftp) && !batch_ftp(ftp))
  2003.  
  2004.   /*
  2005.    * Contructs a suitable prompt that wont take up too much screen
  2006.    * Likely this will need alot more fiddling before everyone is happy...
  2007.    */
  2008.   {
  2009.     int mode;
  2010.  
  2011.     mode = ftp->prompt;
  2012.     if (ftp->cd==NULL && (mode==PROMPT_SHORTDIR || mode==PROMPT_LONGDIR))
  2013.       mode = PROMPT_NODIR;
  2014.  
  2015.     if (mode==PROMPT_SHORTDIR)
  2016.     {
  2017.       char *s;
  2018.       int n = strlen(ftp->cd);
  2019.  
  2020.  
  2021.       /*
  2022.        * Try to determine what kind of file system we are
  2023.        * dealing with and so work out how/where to split
  2024.        * the leaf directory off - look for unix '/', dos '\'
  2025.        * then checks to see if there is both '[' and the last
  2026.        * char is ']', and so is talking to VMS, so use .'., but strip
  2027.        * trailing ']', failing that, it may even be RISC OS local
  2028.        * server, else it just prints last 16 characters...
  2029.        */
  2030.       if (s = strrchr(ftp->cd, '/'), s!=NULL)
  2031.         cwprintf(ftp->window, "%s> ", (s[1])?s+1:s);
  2032.       else if (s = strrchr(ftp->cd, '\\'), s!=NULL)
  2033.         cwprintf(ftp->window, "%s> ", (s[1])?s+1:s);
  2034.       else if (s = strchr(ftp->cd, '['), s!=NULL && ftp->cd[n-1]==']')
  2035.       {
  2036.         /*
  2037.          * UGH - horrible VMS!
  2038.          */
  2039.         char *q;
  2040.         char buf[20];
  2041.   
  2042.         if (q = strrchr(s, '.'), q!=NULL)
  2043.           s = q+1;
  2044.   
  2045.         memset(buf, '\0', 20);
  2046.         if (s>(ftp->cd+(n-16)))
  2047.         {
  2048.           s = ftp->cd+(n-16);
  2049.           strcpy(buf+3, s);
  2050.           buf[0] = '.';  buf[1] = '.';  buf[2] = '.';
  2051.         }
  2052.         else
  2053.           strcpy(buf, s);
  2054.         buf[strlen(buf)-1] = '\0';
  2055.         cwprintf(ftp->window, "%s> ", buf);
  2056.       }    
  2057.       else if (s = strrchr(ftp->cd, '.'), s!=NULL)
  2058.         cwprintf(ftp->window, "%s> ", (s[1])?s+1:s);
  2059.     }
  2060.     else if (mode==PROMPT_LONGDIR)
  2061.     {
  2062.       if (strlen(ftp->cd)>20)
  2063.       {
  2064.         cwprintf(ftp->window, "%s\r\n", ftp->cd);
  2065.         cwprintf(ftp->window, "> ");
  2066.       }
  2067.       else
  2068.         cwprintf(ftp->window, "%s> ", ftp->cd);
  2069.     }
  2070.     else
  2071.       cwprintf(ftp->window, "ftp> ");
  2072.   }
  2073. }
  2074.  
  2075. static int batch_ftp(struct ftp *ftp)
  2076. {
  2077.   char *p;
  2078.   char buf[256];
  2079.  
  2080.   if (!ftp->batch)
  2081.     return 0;
  2082.  
  2083.   cwprintf(ftp->window, "batch> ");
  2084.  
  2085.   do
  2086.   {
  2087.     p = fgets(buf, 256, ftp->batch);
  2088.     rip(buf);
  2089.   }
  2090.   while (p!=NULL && (*buf=='\0' || *buf=='#'));
  2091.  
  2092.   if (p!=NULL)
  2093.   {
  2094.     vterm_printf(ftp->window->vt, ATTRIB_BOLD, "%s\r\n", buf);
  2095.     ftpparse(ftp->session, buf, strlen(buf));
  2096.   }
  2097.   else
  2098.   {
  2099.     vterm_printf(ftp->window->vt, ATTRIB_BOLD, "Batch completed.\r\n");
  2100.     fclose(ftp->batch);
  2101.     ftp->batch = NULL;
  2102.     if (ftp->nextsource!=NULL)
  2103.     {
  2104.       if (!batch_open(ftp, ftp->nextsource))
  2105.         cwprintf(ftp->window, "Cant find batch file \"%s\".\r\n", ftp->nextsource);
  2106.       free(ftp->nextsource);
  2107.       ftp->nextsource = NULL;
  2108.       prompt(ftp);
  2109.       return 1;
  2110.     }
  2111.   }  
  2112.   return 0;
  2113. }
  2114.  
  2115. void ftp_mpcleanup(struct ftp *ftp)
  2116. {
  2117.   int i;
  2118.   
  2119.   ftp->margp = 0;
  2120.  
  2121.   if (ftp->margv!=NULL)
  2122.   {
  2123.     for (i=0; ftp->margv[i]!=NULL; i++)
  2124.       free(ftp->margv[i]);
  2125.     free(ftp->margv);
  2126.     ftp->margv = NULL;
  2127.   }
  2128.  
  2129.   if (ftp->mfp!=NULL)
  2130.   {
  2131.     fclose(ftp->mfp);
  2132.     ftp->mfp = NULL;
  2133.   }
  2134.  
  2135.   if (ftp->mtemp!=NULL)
  2136.   {
  2137.     remove(ftp->mtemp);
  2138.     free(ftp->mtemp);
  2139.     ftp->mtemp = NULL;
  2140.   }
  2141.  
  2142.   ftp->mpstate = FTPM_IDLE;
  2143. }
  2144.  
  2145.  
  2146. static int domget(int argc, char **argv)
  2147. {
  2148.   int i;
  2149.   struct ftp *ftp;
  2150.  
  2151.   if ((ftp = current_ftp->cb.ftp) == NULLFTP)
  2152.   {
  2153.     cwprintf(ftp->window, notsess);
  2154.     return 1;
  2155.   }
  2156.  
  2157.   ftp_mpcleanup(ftp);
  2158.  
  2159.   /* must have at least one file */
  2160.   if (argc<2)
  2161.   {
  2162.     cwprintf(ftp->window, "No files requested.\r\n");
  2163.     return 1;
  2164.   }
  2165.  
  2166.   /* alloc for temp name and file list */
  2167.   if ((ftp->mtemp = strdup(tmpnam(NULL)), ftp->mtemp==NULL) ||
  2168.       (ftp->margv = (char **)malloc(argc*sizeof(char *)), ftp->margv==NULL))
  2169.   {
  2170.     ftp_mpcleanup(ftp);
  2171.     cwprintf(ftp->window, nomemforop);
  2172.     return 1;
  2173.   }
  2174.  
  2175.   for (i=0; i<argc; i++)
  2176.     ftp->margv[i] = NULL;
  2177.  
  2178.   ftp->margp = 0;
  2179.  
  2180.   /* Copy file list */
  2181.   for (i=1; i<argc; i++)
  2182.   {
  2183.     if (ftp->margv[i-1] = strdup(argv[i]), ftp->margv[i-1]==NULL)
  2184.     {
  2185.       ftp_mpcleanup(ftp);
  2186.       cwprintf(ftp->window, nomemforop);
  2187.       return 1;
  2188.     }
  2189.   }  
  2190.  
  2191.   /* Initial open of temp list file */
  2192.   if (ftp->fp = fopen(ftp->mtemp, "w"), ftp->fp==NULL)
  2193.   {
  2194.     ftp_mpcleanup(ftp);
  2195.     cwprintf(ftp->window, "Oops - temp file wont open\r\n");
  2196.     return 1;
  2197.   }
  2198.  
  2199.   /* start the ball rolling... */
  2200.   ftp->mpstate = FTPM_LISTING;
  2201.   mprocess(ftp);
  2202.   return 0;
  2203. }
  2204.  
  2205. static int mprocess(struct ftp *ftp)
  2206. {
  2207.   char buf[256];
  2208.  
  2209.   if (ftp->mpstate==FTPM_IDLE)
  2210.     return 0;
  2211.  
  2212.   cwprintf(ftp->window, "mget> ");
  2213.  
  2214.   if (ftp->mpstate==FTPM_LISTING)
  2215.   {
  2216.     if (ftp->margv[ftp->margp]!=NULL)
  2217.     {
  2218.       if (ftp->fp==NULL)
  2219.         ftp->fp = fopen(ftp->mtemp, "a");
  2220.  
  2221.       if (ftp->fp==NULL)
  2222.       {
  2223.         ftp_mpcleanup(ftp);
  2224.         cwprintf(ftp->window, "Temp file wont open - mget aborted\r\n");
  2225.         return 0;
  2226.       }
  2227.  
  2228.       ftp->state = RECEIVING_STATE1;
  2229.       ftp->expected_size = 0;
  2230.       ftp->start_time = alarm_timenow () ;
  2231.       if ( ftp->type == IMAGE_TYPE )
  2232.       {
  2233.         ftp->type = IMAGE_PENDING ;
  2234.         sndftpmsg(ftp,"TYPE A\r\n");
  2235.       }
  2236.       ftpsetup(ftp,(void(*)())ftpdr,NULLVFP,(void(*)())ftpcds);
  2237.       /*
  2238.        * Generate the command to start the transfer
  2239.        * also send "NLST" on its own rather than "NLST " when the arg is empty
  2240.        */
  2241.       sndftpmsg(ftp,"NLST%s%s\r\n",(*ftp->margv[ftp->margp])?" ":"", ftp->margv[ftp->margp]);
  2242.       ++ftp->margp;
  2243.       return 1;
  2244.     }
  2245.     else
  2246.     {
  2247.       ftp->mpstate = FTPM_GETFILES;
  2248.       if (ftp->mfp = fopen(ftp->mtemp, "r"), ftp->mtemp==NULL)
  2249.       {
  2250.         ftp_mpcleanup(ftp);
  2251.         cwprintf(ftp->window, "Temp file wont open - mget aborted\r\n");
  2252.         return 0;
  2253.       }
  2254.     }
  2255.   }
  2256.  
  2257.   if (ftp->mpstate == FTPM_GETFILES)
  2258.   {
  2259.     strcpy(buf, "get ");
  2260.  
  2261.     if (fgets(buf+4, 256, ftp->mfp)!=NULL)
  2262.     {
  2263.       rip(buf);
  2264.       cwprintf(ftp->window, "%s\r\n", buf);
  2265.       ftpparse(ftp->session, buf, strlen(buf));
  2266.     }
  2267.     else
  2268.     {
  2269.       ftp_mpcleanup(ftp);
  2270.       cwprintf(ftp->window, "MGet completed.\r\n");
  2271.     }  
  2272.     return 0;
  2273.   }
  2274.  
  2275.   return 0;
  2276. }
  2277.  
  2278.  
  2279. /*
  2280.  * Options presetting commands
  2281.  */
  2282. /*
  2283.  * Set default path processing options
  2284.  * with which each FTP is initialised
  2285.  */
  2286. static int dooptpath(int argc, char **argv)
  2287. {
  2288.   int i, n;
  2289.   char opts[256];
  2290.   char *p;
  2291.   pathent_str *pe;
  2292.  
  2293.   if (argc>1)
  2294.   {
  2295.     if ((argc==2) && !strncmp(argv[1], "off", strlen(argv[1])))
  2296.     {
  2297.       delpathent(default_pathopts);
  2298.       default_pathopts = PATH_OFF;
  2299.     }
  2300.     else if ((argc==2) && !strncmp(argv[1], "auto", strlen(argv[1])))
  2301.     {
  2302.       delpathent(default_pathopts);
  2303.       default_pathopts = PATH_DEF;
  2304.     }
  2305.     else if ((argc==2) && !strncmp(argv[1], "type", strlen(argv[1])))
  2306.     {
  2307.       delpathent(default_pathopts);
  2308.       default_pathopts = PATH_TYPE;
  2309.     }
  2310.     else
  2311.     {
  2312.       /* Reconstrunct original path command */
  2313.       *opts = '$';
  2314.       p = opts+1;
  2315.       for (i=1; i<argc; i++)
  2316.       {
  2317.         n = sprintf(p, " %s", argv[i]);
  2318.         p += n;
  2319.       }
  2320.       if (pe = getpathent(opts), pe!=NULL)
  2321.       {
  2322.         delpathent(default_pathopts);
  2323.         default_pathopts = pe;
  2324.       }
  2325.       else
  2326.       {
  2327.         cwprintf(NULL, "Parse error in path options.\r\n");
  2328.         return 1;
  2329.       }
  2330.     }
  2331.   }
  2332.   else
  2333.   {
  2334.     if (default_pathopts == PATH_OFF)
  2335.       cwprintf(NULL, "Path processing off\r\n");
  2336.     else if (default_pathopts == PATH_DEF)
  2337.       cwprintf(NULL, "Auto path processing by extension\r\n");
  2338.     else
  2339.       cwprintf(NULL, "Preset path processing\r\n");
  2340.    /* show_pathent(ftp->pathopts); */
  2341.   }
  2342.  
  2343.   return 0;
  2344. }
  2345.  
  2346. static int dooptwinsz(int argc, char **argv)
  2347. {
  2348.   if (argc<2)
  2349.   {
  2350.     cwprintf(NULL, "FTP window %d x %d, (visible %d x %d)\r\n", 
  2351.             default_win_xv, default_win_yv, default_win_xe, default_win_ye);
  2352.     return 0;
  2353.   }
  2354.  
  2355.   if (argc==2)
  2356.   {
  2357.     /*
  2358.      * Changing YE only
  2359.      */
  2360.     default_win_ye = atoi(argv[1]);
  2361.   }
  2362.   else if (argc>2)
  2363.   {
  2364.     default_win_xe = atoi(argv[1]);
  2365.     default_win_ye = atoi(argv[2]);
  2366.     if (argc==4)
  2367.     {
  2368.       default_win_yv = atoi(argv[3]);
  2369.     }
  2370.     else if (argc>4)
  2371.     {
  2372.       default_win_xv = atoi(argv[3]);
  2373.       default_win_yv = atoi(argv[4]);
  2374.     }  
  2375.   }
  2376.  
  2377.   if (default_win_xe < 80)
  2378.     default_win_xe = 80;
  2379.  
  2380.   if (default_win_xv < 16)
  2381.     default_win_xv = 16;
  2382.  
  2383.   if (default_win_xv > default_win_xe)
  2384.     default_win_xv = default_win_xe;
  2385.     
  2386.   if (default_win_ye < 24)
  2387.     default_win_ye = 24;
  2388.  
  2389.   if (default_win_yv < 8)
  2390.     default_win_yv = 8;
  2391.  
  2392.   if (default_win_yv > default_win_ye)
  2393.     default_win_yv = default_win_ye;
  2394.  
  2395.   return 0;
  2396. }
  2397.  
  2398. /*
  2399.  * Set default trace level
  2400.  */
  2401. static int doopttrace(int argc, char **argv)
  2402. {
  2403.   if (argc<2)
  2404.     cwprintf(NULL, "FTP trace level is %d\r\n", default_trace);
  2405.   else
  2406.     default_trace = atoi(argv[1]);
  2407.  
  2408.   return 0;
  2409. }
  2410.  
  2411. /*
  2412.  * Set default lump size
  2413.  */
  2414. static int doopthash(int argc, char **argv)
  2415. {
  2416.   if (argc<2)
  2417.     cwprintf(NULL, "FTP lump size is %d\r\n", default_hash);
  2418.   else
  2419.     default_hash = atoi(argv[1]);
  2420.  
  2421.   return 0;
  2422. }
  2423.  
  2424. /*
  2425.  * Set default transfer type
  2426.  */
  2427. static int doopttype(int argc, char **argv)
  2428. {
  2429.   if (argc<2)
  2430.   {
  2431.     char *p;
  2432.     switch (default_type)
  2433.     {
  2434.       case IMAGE_TYPE:
  2435.         p = "Image";
  2436.         break;
  2437.       case ASCII_TYPE:
  2438.         p = "Ascii";
  2439.         break;
  2440.       default:
  2441.         p = "Ascii";
  2442.         default_type = ASCII_TYPE;
  2443.         break;
  2444.     }
  2445.     cwprintf(NULL, "FTP transfer type is %s\r\n", p);
  2446.   }
  2447.   else
  2448.   {
  2449.     switch (*argv[1])
  2450.     {
  2451.       case 'i':
  2452.       case 'b':
  2453.         default_type = IMAGE_TYPE;
  2454.         break;
  2455.       case 'a':
  2456.         default_type = ASCII_TYPE;
  2457.         break;
  2458.       default:
  2459.         cwprintf(NULL, "Invalid type %s\r\n",argv[1]);
  2460.         return 1;
  2461.     }
  2462.   }
  2463.   return 0;
  2464. }
  2465.  
  2466. /*
  2467.  * Set default prompt type
  2468.  */
  2469. static int dooptprompt(int argc, char **argv)
  2470. {
  2471.   if (argc<2)
  2472.   {
  2473.     char *type;
  2474.     switch (default_prompt)
  2475.     {
  2476.       case PROMPT_SHORTDIR:
  2477.         type = "Short directory";
  2478.         break;
  2479.       case PROMPT_LONGDIR:
  2480.         type = "Full directory";
  2481.         break;
  2482.       default:
  2483.         type = "No directory";
  2484.     }      
  2485.     cwprintf(NULL, "FTP prompt type: %s\r\n", type);
  2486.   }
  2487.   else
  2488.   {
  2489.     if (!strncmp(argv[1], "nodir", strlen(argv[1])))
  2490.       default_prompt = PROMPT_NODIR;
  2491.     else if (!strncmp(argv[1], "short", strlen(argv[1])))
  2492.       default_prompt = PROMPT_SHORTDIR;
  2493.     else if (!strncmp(argv[1], "long", strlen(argv[1])))
  2494.       default_prompt = PROMPT_LONGDIR;
  2495.     else
  2496.       cwprintf(NULL, "Unknown prompt type. Valid prompts are: nodir short long\r\n");
  2497.   }
  2498.  
  2499.   return 0;
  2500. }
  2501.  
  2502. int doftpopt(int argc, char **argv)
  2503. {
  2504.   return subcmd(ftpopts,argc,argv);
  2505. }
  2506.  
  2507. /*
  2508.  * Calls the pathname processor according to user options
  2509.  * and retains the old path name handling...
  2510.  */
  2511. static int create_fetchedfile(struct ftp *ftp, char *localname, char *remotename, char *givenname)
  2512. {
  2513.   int filetype = -1;
  2514.  
  2515.   if (givenname==NULL)
  2516.   {
  2517.     /*
  2518.      * Local name not specified
  2519.      */
  2520.     if (ftp->pathopts==PATH_OFF ||
  2521.         !pathmap(remotename, localname, &filetype, ftp->pathopts) ||
  2522.         ftp->pathopts==PATH_TYPE )
  2523.     {
  2524.       char *cp, *temp;
  2525.       temp = strdup(remotename);
  2526.       cp = temp;
  2527.       while (cp = strpbrk(cp, "\"[]|<>+=;,./\\"), cp != NULL)
  2528.       {
  2529.         if (*cp=='/' || *cp=='\\')
  2530.           *cp++ = '.';
  2531.         else if (*cp=='.')
  2532.           *cp++ = '/';
  2533.         else
  2534.           *cp++ = '_';
  2535.       }
  2536.       sprintf(localname, "<FTP$dir>.%s", (*temp=='.')?temp+1:temp);
  2537.       free(temp);
  2538.     }
  2539.   }
  2540.   else
  2541.   {
  2542.     /*
  2543.      * Local name specified
  2544.      */
  2545.     strcpy(localname, givenname);
  2546.  
  2547.     if (ftp->pathopts!=PATH_OFF)
  2548.       pathmap(remotename, localname, &filetype, PATH_TYPE);
  2549.   }
  2550.  
  2551.   if (filetype<0)
  2552.     filetype = (ftp->type == IMAGE_TYPE)?0xffd:0xfff;
  2553.  
  2554.   if (create_localfile(ftp, localname, filetype))
  2555.   {
  2556.     ftp->filetype = filetype;
  2557.     return 1;
  2558.   }
  2559.  
  2560.   return 0;
  2561. }
  2562.  
  2563. /*
  2564.  * Takes a filename, expands it into a canonicalised name
  2565.  * then recursively creates directories and finally the
  2566.  * file with the specifued filetype
  2567.  */
  2568. static int create_localfile(struct ftp *ftp, char *localname, int filetype)
  2569. {
  2570.   os_regset r;
  2571.   os_error *e;
  2572.  
  2573.   char tempname[256];
  2574.  
  2575.   if (strpbrk(localname, "@$&%<>:"))
  2576.     strcpy(tempname, localname);
  2577.   else
  2578.     sprintf(tempname, "<ftp$dir>.%s", (*localname=='.')?localname+1:localname);
  2579.  
  2580.   r.r[0] = 37;
  2581.   r.r[1] = (int)tempname;
  2582.   r.r[2] = (int)localname;
  2583.   r.r[3] = (int)NULL;
  2584.   r.r[4] = (int)NULL;
  2585.   r.r[5] = 256;
  2586.   
  2587.   if (e = os_swix(OS_FSControl, &r), e==NULL)
  2588.     e = create_path(localname, 0, (filetype<0)?0xfff:filetype);
  2589.  
  2590.   if (filetype>=0)
  2591.   {
  2592.     if (e!=NULL)
  2593.     {
  2594.       cwprintf(ftp->window,"Cant create local file - %s\n", e->errmess);
  2595.       return 0;
  2596.     }
  2597.     else
  2598.       cwprintf(ftp->window,"Opening %s (%s)\r\n", localname, fttoa(filetype));
  2599.   }
  2600.   return 1;
  2601. }
  2602.