home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Amiga Shareware Floppies / ma64.dms / ma64.adf / FTPMount-1.0 / Source / connect.c next >
C/C++ Source or Header  |  1996-04-12  |  58KB  |  2,617 lines

  1. /*
  2.  * This source file is Copyright 1995 and 1996 by Evan Scott, apart from those sections which are
  3.  * explicitly described as otherwise.
  4.  * All rights reserved.
  5.  * Permission is granted to distribute this file provided no
  6.  * fees beyond distribution costs are levied.
  7.  */
  8.  
  9. #include <exec/types.h>
  10. #include <exec/memory.h>
  11. #include <exec/alerts.h>
  12.  
  13. #include <devices/timer.h>
  14.  
  15. #include <dos/dos.h>
  16. #include <dos/dosextens.h>
  17. #include <dos/dostags.h>
  18.  
  19. #include <proto/exec.h>
  20. #include <proto/dos.h>
  21. #include <proto/intuition.h>
  22. #include <proto/gadtools.h>
  23.  
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27.  
  28. #include <ctype.h>                     // for isdigit()  (RJF)
  29. #include "evtypes.h"
  30. #include "verify.h"
  31. #include "tcp.h"
  32.  
  33. #include "site.h"
  34. #include "ftp.h"
  35. #include "split.h"
  36. #include "ftpinfo.h"
  37. #include "connect.h"
  38. #include "request.h"
  39.  
  40. #include "globals.h"
  41. #include "strings.h"
  42.  
  43. #define ERROR_GARBAGE_RECEIVED 15
  44.  
  45. extern ftpinfo *get_info(site *, b8 *);
  46.  
  47. b8 *grow_info(b8 *a, b8 *b, int n)
  48. /*
  49.  * concatenates a and b (not null terminated of length n) and returns
  50.  * an allocated string with the result.  a is freed.
  51.  *     a may be nil
  52.  *     b may not
  53.  */
  54. {
  55.     b8 *c, *d;
  56.     int len;
  57.     
  58.     if (a) len = strlen(a);
  59.     else len = 0;
  60.     
  61.     len += n + 1;
  62.     
  63.     c = (b8 *)allocate(len, V_cstr);
  64.     if (!c) {
  65.         if (a) deallocate(a, V_cstr);
  66.         return nil;
  67.     }
  68.     
  69.     if (a) {
  70.         strcpy(c, a);
  71.         d = c + strlen(c);
  72.     } else {
  73.         d = c;
  74.     }
  75.     
  76.     if (n)
  77.         memcpy(d, b, n);
  78.     d[n] = 0;
  79.     
  80.     if (a) {
  81.         deallocate(a, V_cstr);
  82.     }
  83.     
  84.     a = c;
  85.     while (*a) {
  86.         if (*a == '\t') *a = ' ';    /* hmmm */
  87.         if (*a == '\r') *a = '\n';
  88.         a++;
  89.     }
  90.     
  91.     return c;
  92. }
  93.  
  94. boolean substr(b8 *s, b8 *ss)
  95. /*
  96.  * returns true if s contains ss (non-case-sensitive)
  97.  * s may be nil, in which case false is returned
  98.  */
  99. {
  100.     int len;
  101.     
  102.     if (s == nil) return false;
  103.     
  104.     len = strlen(ss);
  105.     
  106.     while (*s) {
  107.         if (strnicmp(s, ss, len) == 0) return true;
  108.         s++;
  109.     }
  110.     
  111.     return false;
  112. }
  113.  
  114. void inform(struct IntuitionBase *IntuitionBase, b8 *title, b8 *text, b8 *site, b32 errno)
  115. {
  116.     struct EasyStruct es;
  117.     
  118.     es.es_StructSize = sizeof(struct EasyStruct);
  119.     es.es_Flags = 0;
  120.     es.es_Title = title;
  121.     es.es_GadgetFormat = strings[MSG_OK];
  122.     es.es_TextFormat = text;
  123.     
  124.     EasyRequest(nil, &es, nil, site, errno);
  125. }
  126.  
  127. tcpmessage *new_message(site *sp)
  128. /*
  129.  * get a new tcpmessage
  130.  */
  131. {
  132.     tcpmessage *intr;
  133.     struct MsgPort *sync;
  134.     
  135.     verify(sp, V_site);
  136.     
  137.     intr = sp->intr;
  138.     verify(intr, V_tcpmessage);
  139.     
  140.     sync = sp->sync;
  141.     
  142.     /* "re-use" intr to get a new tcpmessage */
  143.     intr->command = TCP_NEWMESSAGE;
  144.     intr->header.mn_ReplyPort = sync;
  145.     
  146.     PutMsg(tcp, &intr->header);
  147.     WaitPort(sync); GetMsg(sync);    /* this _should_ never block */
  148.     
  149.     return (tcpmessage *)intr->data;
  150. }
  151.  
  152. void interrupt_message(site *sp, tcpmessage *tm)
  153. /*
  154.  * interrupt tm
  155.  */
  156. {
  157.     tcpmessage *intr;
  158.     struct MsgPort *sync;
  159.     
  160.     verify(sp, V_site);
  161.     verify(tm, V_tcpmessage);
  162.     
  163.     intr = sp->intr;
  164.     verify(intr, V_tcpmessage);
  165.     
  166.     sync = sp->sync;
  167.     
  168.     intr->command = TCP_INTERRUPT;
  169.     intr->header.mn_ReplyPort = sync;
  170.     intr->interrupt = tm;
  171.     
  172.     PutMsg(tcp, &intr->header);
  173.     
  174.     /*
  175.      * NB: I could probably assume tm->header.mn_ReplyPort == sync safely, but
  176.      * this seems a bit more "correctly generic" (although potentially more buggy)
  177.      */
  178.     WaitPort(tm->header.mn_ReplyPort); GetMsg(tm->header.mn_ReplyPort);    /* aborted tm coming back */
  179.     WaitPort(sync); GetMsg(sync);    /* intr coming back successful */
  180.     
  181.     return;
  182. }
  183.  
  184. b32 control_write(site *sp, b8 *command, b32 csig)
  185. /*
  186.  * writes the string command to the control connection
  187.  * Inputs:
  188.  *     sp    : site pointer
  189.  *    command : null terminated command string
  190.  *    csig    : additional cancel signals, 0 is ok
  191.  *
  192.  * returns the tcp error
  193.  */
  194. {
  195.     tcpmessage *tm;
  196.     struct MsgPort *sync;
  197.     b32 signals, rsigs;
  198.     
  199.     verify(sp, V_site);
  200.     truth(command != nil);
  201.     
  202.     // truth(sp->connected);
  203.     
  204.     tm = sp->control;
  205.     verify(tm, V_tcpmessage);
  206.     
  207.     sync = sp->sync;
  208.     
  209.     tm->command = TCP_WRITE;
  210.     tm->length = strlen(command);
  211.     tm->flags = 0;
  212.     tm->data = command;
  213.     tm->header.mn_ReplyPort = sync;
  214.     
  215.     csig |= sp->abort_signals | sp->disconnect_signals;
  216.     signals = (1 << sync->mp_SigBit) | csig;
  217.     
  218.     PutMsg(tcp, &tm->header);
  219.     do {
  220.         rsigs = Wait(signals);
  221.         if (rsigs & csig) {
  222.             interrupt_message(sp, tm);
  223.             
  224.             if (rsigs & sp->disconnect_signals) {
  225.                 disconnect(sp);
  226.             }
  227.             
  228.             return ERROR_INTERRUPTED;
  229.         }
  230.     } while (!GetMsg(sync));
  231.     
  232.     return tm->error;
  233. }
  234.  
  235. b32 make_connection(site *sp, tcpmessage *tm, b8 *addr, b16 port, b32 csig)
  236. /*
  237.  * make a connection to a remote host
  238.  * Inputs:
  239.  *    sp    : site pointer
  240.  *    tm    : an unused tcpmessage
  241.  *    addr    : null terminated string address
  242.  *    port    : port number to connect to
  243.  *    csig    : additional cancel signals (may be 0)
  244.  *
  245.  * Returns:
  246.  *     standard tcp error
  247.  */
  248. {
  249.     struct MsgPort *sync;
  250.     b32 signals, rsigs, asigs;
  251.     
  252.     verify(sp, V_site);
  253.     verify(tm, V_tcpmessage);
  254.     truth(addr != nil);
  255.     
  256.     sync = sp->sync;
  257.     
  258.     tm->header.mn_ReplyPort = sync;
  259.     tm->command = TCP_CONNECT;
  260.     tm->data = addr;
  261.     tm->port.w = port;
  262.     tm->flags = 0;
  263.     
  264.     asigs = csig | sp->abort_signals | sp->disconnect_signals;
  265.     signals = asigs | (1 << sync->mp_SigBit);
  266.     
  267.     PutMsg(tcp, &tm->header);
  268.     do {
  269.         rsigs = Wait(signals);
  270.         if (rsigs & asigs) {
  271.             interrupt_message(sp, tm);
  272.             
  273.             if (rsigs & sp->disconnect_signals) {
  274.                 disconnect(sp);
  275.             }
  276.             
  277.             return ERROR_INTERRUPTED;
  278.         }
  279.     } while (!GetMsg(sync));
  280.     
  281.     return tm->error;
  282. }
  283.  
  284. void break_connection(site *sp, tcpmessage *tm)
  285. /*
  286.  * do a TCP_CLOSE on tm
  287.  */
  288. {
  289.     struct MsgPort *sync;
  290.     
  291.     verify(sp, V_site);
  292.     verify(tm, V_tcpmessage);
  293.     
  294.     sync = sp->sync;
  295.     
  296.     tm->command = TCP_CLOSE;
  297.     tm->header.mn_ReplyPort = sync;
  298.     
  299.     PutMsg(tcp, &tm->header);
  300.     WaitPort(sync); GetMsg(sync);
  301.     
  302.     return;
  303. }
  304.  
  305. boolean passive_response(b8 *s, b8 *addr, b16 *portp)
  306. /*
  307.  * parse the response to a PASV command
  308.  * Inputs:
  309.  *    s    : the response to the PASV (null terminated)
  310.  *    addr    : a buffer to hold the address (should be as long as s)
  311.  *    portp    : where to put the port number
  312.  *
  313.  * Returns:
  314.  *    true if it was a valid PASV response
  315.  */
  316. {
  317.     b8 *t;
  318.     b16 ncommas, portn;
  319.     
  320.     truth(s != nil);
  321.     truth(addr != nil);
  322.     truth(portp != nil);
  323.     
  324.     while (*s && *s != '(') s++;
  325.     if (!*s) return false;
  326.     
  327.     /* first calculate port number ... skip the first 4 commas */
  328.     
  329.     ncommas = 0;
  330.     t = s;
  331.     while (*t && *t != ')' && ncommas < 4) {
  332.         if (*t == ',') ncommas++;
  333.         t++;
  334.     }
  335.     
  336.     portn = atoi(t) * 256;    /* possibly a more thorough check of whether these are legit numbers */
  337.     while (*t && *t != ',' && *t != ')') t++;
  338.     if (*t == ',') portn += atoi(t+1);
  339.     
  340.     /*
  341.      * now copy the first 4 fields to addr, changing commas to periods
  342.      * (hopefully making a legitimate ip address)
  343.      */
  344.     
  345.     ncommas = 0;
  346.     s++;        /* move s past the '(' */
  347.     while (*s && ncommas < 4) {
  348.         if (*s == ',') {
  349.             ncommas++;
  350.             if (ncommas == 4) *addr = 0;
  351.             else *addr++ = '.';
  352.             s++;
  353.         } else {
  354.             *addr++ = *s++;
  355.         }
  356.     }
  357.     
  358.     *portp = portn;
  359.     
  360.     return true;
  361. }
  362.  
  363. b32 response(site *sp, b32 csig, b8 **infop, b8 *code)
  364. /*
  365.  * reads response from remote server on sp->control
  366.  * Inputs:
  367.  *    sp    : site pointer
  368.  *    csig    : cancel signals (in addition to standard sp->abort etc ... usually a window) 0 is ok.
  369.  *    infop    : pointer to a string pointer to store the servers response message
  370.  *    code    : 3 byte array for the response code (eg 257 "/usr/dm/pathname" directory created)
  371.  *
  372.  * Returns:
  373.  *    standard tcp error code with the additional error ERROR_GARBAGE_RECEIVED
  374.  */
  375. {
  376.     tcpmessage *tm;
  377.     struct MsgPort *sync;
  378.     b32 signals, rsigs, asigs;
  379.     b8 *info, *z;
  380.     
  381.     verify(sp, V_site);
  382.     
  383.     truth(code != nil);
  384.     truth(infop != nil);
  385.     
  386.     *infop = nil;
  387.  
  388.     tm = sp->control;
  389.     sync = sp->sync;
  390.     
  391.     verify(tm, V_tcpmessage);
  392.     
  393.     asigs = csig | sp->disconnect_signals | sp->abort_signals;    /* abort signals */
  394.     signals = asigs | (1 << sync->mp_SigBit);
  395.  
  396.     tm->command = TCP_READ;
  397.     tm->flags = FLAG_READLINE;
  398.     tm->data = sp->read_buffer;
  399.     tm->length = READ_BUFFER_LENGTH;
  400.     tm->header.mn_ReplyPort = sync;
  401.     
  402.     PutMsg(tcp, &tm->header);
  403.     do {
  404.         rsigs = Wait(signals);
  405.         if (rsigs & asigs) {
  406.             state_change(sp, SS_ABORTING);
  407.  
  408.             interrupt_message(sp, tm);
  409.             
  410.             /* cancelling the read of the response is guaranteed fatal anyway ... but ... */
  411.             
  412.             if (rsigs & sp->disconnect_signals) {
  413.                 disconnect(sp);
  414.             }
  415.             
  416.             return ERROR_INTERRUPTED;
  417.         }
  418.     } while (!GetMsg(sync));
  419.     
  420.     if (tm->error != NO_ERROR) {
  421.         show_int(tm->error);
  422.         return tm->error;
  423.     }
  424.     
  425.     z = sp->read_buffer;
  426.     
  427.     if (tm->result < 4 ||
  428.             z[0] < '0' || z[0] > '9' ||
  429.             z[1] < '0' || z[1] > '9' ||
  430.             z[2] < '0' || z[2] > '9') {
  431.         show_int(tm->result);
  432.         return ERROR_GARBAGE_RECEIVED;
  433.     }
  434.  
  435.     code[0] = z[0];
  436.     code[1] = z[1];
  437.     code[2] = z[2];
  438.  
  439.     info = grow_info(nil, &z[4], tm->result - 4);
  440.     if (z[3] == '-') {    /* we have a continuation message */
  441.         while (1) {
  442.             PutMsg(tcp, &tm->header);
  443.             do {
  444.                 rsigs = Wait(signals);
  445.                 if (rsigs & asigs) {
  446.                     state_change(sp, SS_ABORTING);
  447.                     
  448.                     if (info)
  449.                         deallocate(info, V_cstr);
  450.                     
  451.                     interrupt_message(sp, tm);
  452.                     
  453.                     if (rsigs & sp->disconnect_signals) {
  454.                         disconnect(sp);
  455.                     }
  456.         
  457.                     return ERROR_INTERRUPTED;
  458.                 }
  459.             } while (!GetMsg(sync));
  460.             
  461.             if (tm->error != NO_ERROR) {    /* tell them about the error */
  462.                 if (info)
  463.                     deallocate(info, V_cstr);
  464.                 
  465.                 return tm->error;
  466.             }
  467.             
  468.             if (tm->result < 4) {        /* not enough to even check if codes are equal */
  469.                 info = grow_info(info, z, tm->result);
  470.                 continue;
  471.             }
  472.             
  473.             if (z[0] == code[0] &&
  474.                     z[1] == code[1] &&
  475.                     z[2] == code[2]) {
  476.                 info = grow_info(info, &z[4], tm->result - 4);
  477.                 if (z[3] == ' ') break;        /* end of continuation */
  478.             } else {
  479.                 info = grow_info(info, z, tm->result);
  480.             }
  481.         }
  482.     }
  483.     
  484.     *infop = info;
  485.     return NO_ERROR;
  486. }
  487.  
  488. b16 numeric_reply(b8 *s)
  489. {
  490.     return (b16)((s[0] - '0') * 100 + (s[1] - '0') * 10 + (s[2] - '0'));
  491. }
  492.  
  493. boolean retry_cancel(struct IntuitionBase *IntuitionBase, b8 *title, b8 *info)
  494. /*
  495.  * paged information with retry/cancel buttons
  496.  * returns true for retry
  497.  *     info may be nil (well, sortof)
  498.  */
  499. {
  500.     b8 *z, *s, tmp;
  501.     struct EasyStruct es;
  502.     int nlines;
  503.     
  504.     es.es_StructSize = sizeof(struct EasyStruct);
  505.     es.es_Flags = 0;
  506.     es.es_Title = title;
  507.     es.es_TextFormat = "%s";
  508.     
  509.     if (info)
  510.         z = info;
  511.     else
  512.         z = strings[MSG_UNKNOWN];
  513.     
  514. more:
  515.     s = z;
  516.     nlines = 0;
  517.  
  518.     while (*z && nlines < MORE_LINES) {
  519.         if (*z == '\n') nlines++;
  520.         z++;
  521.     }
  522.     
  523.     if (*z) {
  524.         es.es_GadgetFormat = strings[MSG_RETRY_MORE_CANCEL];
  525.     } else {
  526.         es.es_GadgetFormat = strings[MSG_RETRY_CANCEL];
  527.     }
  528.     
  529.     tmp = *z;
  530.     *z = 0;
  531.     
  532.     switch (EasyRequest(nil, &es, nil, s)) {
  533.     case 0:    /* cancel */
  534.         *z = tmp;
  535.         return false;
  536.     case 1: /* retry */
  537.         *z = tmp;
  538.         return true;
  539.     case 2: /* more */
  540.         *z = tmp;
  541.         if (!*z) return true;
  542.         goto more;
  543.     }
  544. }
  545.  
  546. void ok(struct IntuitionBase *IntuitionBase, b8 *title, b8 *info)
  547. /*
  548.  * paged information with ok button
  549.  *     info may be nil (sortof)
  550.  */
  551. {
  552.     b8 *z, *s, tmp;
  553.     struct EasyStruct es;
  554.     int nlines;
  555.     
  556.     es.es_StructSize = sizeof(struct EasyStruct);
  557.     es.es_Flags = 0;
  558.     es.es_Title = title;
  559.     es.es_TextFormat = "%s";
  560.     
  561.     if (info)
  562.         z = info;
  563.     else
  564.         z = strings[MSG_UNKNOWN];
  565.     
  566. more:
  567.     s = z;
  568.  
  569.     nlines = 0;
  570.     while (*z && nlines < MORE_LINES) {
  571.         if (*z == '\n') nlines++;
  572.         z++;
  573.     }
  574.     
  575.     if (*z) {
  576.         es.es_GadgetFormat = strings[MSG_MORE_OK];
  577.     } else {
  578.         es.es_GadgetFormat = strings[MSG_OK];
  579.     }
  580.     
  581.     tmp = *z;
  582.     *z = 0;
  583.     
  584.     if (EasyRequest(nil, &es, nil, s) && tmp) {
  585.         *z = tmp;
  586.         goto more;
  587.     }
  588.     
  589.     *z = tmp;
  590.     return;
  591. }
  592.  
  593. void disconnect(site *sp)
  594. /*
  595.  * rudely close control connection and clean up state information on site sp
  596.  */
  597. {
  598.     tcpmessage *tm;
  599.     struct MsgPort *sync;
  600.     
  601.     verify(sp, V_site);
  602.     
  603.     if (!sp->connected) return;
  604.     
  605.     sync = sp->sync;
  606.     
  607.     state_change(sp, SS_DISCONNECTING);
  608.  
  609.     tm = sp->cfile;        /* file open, have to close it */
  610.     if (tm) {
  611.         verify(tm, V_tcpmessage);
  612.         verify(sp->file_list, V_file_info);
  613.         
  614.         tm->command = TCP_CLOSE;
  615.         tm->header.mn_ReplyPort = sync;
  616.         
  617.         PutMsg(tcp, &tm->header);    /* send CLOSE on file tm */
  618.         WaitPort(sync); GetMsg(sync);
  619.         
  620.         tm->command = TCP_DISPOSE;
  621.         PutMsg(tcp, &tm->header);
  622.         
  623.         sp->cfile = nil;
  624.         
  625.         deallocate(sp->file_list, V_file_info);
  626.         sp->file_list = nil;
  627.     }
  628.  
  629.     tm = sp->control;
  630.     verify(tm, V_tcpmessage);
  631.     
  632.     tm->command = TCP_CLOSE;
  633.     tm->header.mn_ReplyPort = sync;
  634.     
  635.     PutMsg(tcp, &tm->header);    /* send CLOSE */
  636.     
  637.     sp->connected = false;
  638.     
  639.     if (sp->cwd) {
  640.         deallocate(sp->cwd, V_cstr);
  641.         sp->cwd = nil;
  642.     }
  643.     
  644.     while (sp->infos) free_info_header(sp->infos);
  645.     
  646.     WaitPort(sync); GetMsg(sync);    /* wait for CLOSE to come back */
  647.     
  648.     /* it shouldn't really fail ... not sure if we can do anything if it has */
  649.  
  650.     state_change(sp, SS_DISCONNECTED);
  651.     
  652.     return;
  653. }
  654.  
  655. b32 read_file(site *sp, b8 *s, b32 *length)
  656. /*
  657.  * read *length bytes from open file
  658.  * Inputs:
  659.  *    sp    : site pointer
  660.  *    s    : data buffer
  661.  *    length    : pointer to length to read, changed to length actually read
  662.  *
  663.  *    Result:
  664.  *    tcp error is returned, *length is modified to be actual length read
  665.  */
  666. {
  667.     tcpmessage *tm;
  668.     struct MsgPort *sync;
  669.     b32 signals, asigs, rsigs;
  670.  
  671.     verify(sp, V_site);
  672.     truth(s != nil);
  673.     truth(length != nil);
  674.  
  675.     tm = sp->cfile;
  676.     verify(tm, V_tcpmessage);
  677.     
  678.     sync = sp->sync;
  679.     
  680.     tm->command = TCP_READ;
  681.     tm->flags = 0;
  682.     tm->data = s;
  683.     tm->length = *length;
  684.     tm->header.mn_ReplyPort = sync;
  685.     
  686.     asigs = sp->abort_signals | sp->disconnect_signals;
  687.     signals = asigs | (1 << sync->mp_SigBit);
  688.     
  689.     PutMsg(tcp, &tm->header);
  690.     do {
  691.         rsigs = Wait(signals);
  692.         if (rsigs & asigs) {
  693.             state_change(sp, SS_ABORTING);
  694.             
  695.             interrupt_message(sp, tm);
  696.             
  697.             if (rsigs & sp->disconnect_signals) {
  698.                 disconnect(sp);
  699.             } else {
  700.                 close_file(sp, false);
  701.             }
  702.             
  703.             *length = 0;
  704.             return ERROR_INTERRUPTED;
  705.         }
  706.     } while (!GetMsg(sync));
  707.     
  708.     if (tm->result > 0) {
  709.         *length = tm->result;
  710.         return NO_ERROR;
  711.     } else {
  712.         *length = 0;
  713.         return tm->error;
  714.     }
  715. }
  716.  
  717. b32 write_file(site *sp, b8 *s, b32 *length)
  718. /*
  719.  * write *length bytes to an open file (almost identical copy to read_file above)
  720.  * Inputs:
  721.  *    sp    : site pointer
  722.  *    s    : data buffer
  723.  *    length    : pointer to length to write, changed to length actually written
  724.  *
  725.  *    Result:
  726.  *    tcp error is returned, *length is modified to be actual length written
  727.  */
  728. {
  729.     tcpmessage *tm;
  730.     struct MsgPort *sync;
  731.     b32 signals, asigs, rsigs;
  732.  
  733.     verify(sp, V_site);
  734.     truth(s != nil);
  735.     truth(length != nil);
  736.  
  737.     tm = sp->cfile;
  738.     verify(tm, V_tcpmessage);
  739.     
  740.     sync = sp->sync;
  741.     
  742.     tm->command = TCP_WRITE;
  743.     tm->flags = 0;
  744.     tm->data = s;
  745.     tm->length = *length;
  746.     tm->header.mn_ReplyPort = sync;
  747.     
  748.     asigs = sp->abort_signals | sp->disconnect_signals;
  749.     signals = asigs | (1 << sync->mp_SigBit);
  750.     
  751.     PutMsg(tcp, &tm->header);
  752.     do {
  753.         rsigs = Wait(signals);
  754.         if (rsigs & asigs) {
  755.             state_change(sp, SS_ABORTING);
  756.             
  757.             interrupt_message(sp, tm);
  758.             
  759.             if (rsigs & sp->disconnect_signals) {
  760.                 disconnect(sp);
  761.             } else {
  762.                 close_file(sp, false);
  763.             }
  764.             
  765.             *length = 0;
  766.             return ERROR_INTERRUPTED;
  767.         }
  768.     } while (!GetMsg(sync));
  769.     
  770.     if (tm->result > 0) {
  771.         *length = tm->result;
  772.         return NO_ERROR;
  773.     } else {
  774.         *length = 0;
  775.         return tm->error;
  776.     }
  777. }
  778.  
  779. b32 open_file(site *sp, b8 *s, boolean writing, b8 *leaf_name)
  780. /*
  781.  * open file with name in s
  782.  * Inputs:
  783.  *    sp    : site pointer
  784.  *    s    : file name
  785.  *    writing    : true if opened for writing, false if for reading
  786.  *
  787.  * Returns:
  788.  *    0    : no error
  789.  *    non-0    : standard file system errors (ERROR_OBJECT_NOT_FOUND etc)
  790.  */
  791. {
  792.     tcpmessage *tm, *newtm;
  793.     struct MsgPort *sync;
  794.     b8 reply[4], *info;
  795.     b8 *leaf;
  796.     b32 error;
  797.     b16 port_number;
  798.     file_info *fi;
  799.     
  800.     verify(sp, V_site);
  801.     truth(s != nil);
  802.     
  803.     /* a few conditions we are assuming */
  804.     truth(sp->connected);
  805.     truth(sp->cfile == nil);
  806.     truth(sp->file_list == nil);
  807.     
  808.     if (s[0] == 0) {
  809.         /* they are trying to open the root of this site as a file ... */
  810.         return ERROR_OBJECT_WRONG_TYPE;
  811.     }
  812.     
  813.     tm = sp->control;
  814.     verify(tm, V_tcpmessage);
  815.     
  816.     sync = sp->sync;
  817.     
  818.     tm->header.mn_ReplyPort = sync;
  819.     
  820.     leaf = cd_parent(sp, s);
  821.     if (!leaf) {
  822.         /* there are other possible reasons here, but I'm being lazy ... */
  823.         return ERROR_DIR_NOT_FOUND;
  824.     }
  825.     
  826.     if (leaf_name) leaf = leaf_name;
  827.     
  828.     state_change(sp, SS_OPENING);
  829.     
  830.     newtm = new_message(sp);
  831.     if (newtm) {
  832.         fi = (file_info *)allocate(sizeof(*fi) + strlen(s) + 1, V_file_info);
  833.         if (fi) {
  834.             strcpy(fi->fname, s);
  835.             
  836.             if (control_write(sp, "PASV\r\n", 0) == NO_ERROR) {
  837.                 if (writing) {    /* yes, I do this twice :( */
  838.                     sprintf(sp->read_buffer, "STOR %s\r\n", leaf);
  839.                 } else {
  840.                     sprintf(sp->read_buffer, "RETR %s\r\n", leaf);
  841.                 }
  842.                 if (!sp->quick || control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
  843.                     if (response(sp, 0, &info, reply) == NO_ERROR) {
  844.                         if (reply[0] == '2') {
  845.                             if (info) {
  846.                                 if (passive_response(info, sp->read_buffer, &port_number)) {
  847.                                     deallocate(info, V_cstr);
  848.                                     
  849.                                     if (make_connection(sp, newtm, sp->read_buffer, port_number, 0) == NO_ERROR) {
  850.                                         if (writing) {    /* and again */
  851.                                             sprintf(sp->read_buffer, "STOR %s\r\n", leaf);
  852.                                         } else {
  853.                                             sprintf(sp->read_buffer, "RETR %s\r\n", leaf);
  854.                                         }
  855.                                         if (sp->quick || control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
  856.                                             /* this next response will be to the RETR/STOR */
  857.                                             if (response(sp, 0, &info, reply) == NO_ERROR) {
  858.                                                 if (info) {
  859. #ifdef VERIFY
  860.                                                     if (reply[0] != '1') {
  861.                                                         reply[3] = 0;
  862.                                                         show_string(reply);
  863.                                                         show_string(info);
  864.                                                     }
  865. #endif
  866.                                                     deallocate(info, V_cstr);
  867.                                                 }
  868.                                                 if (reply[0] == '1') {
  869.                                                     ensure(fi, V_file_info);
  870.                                                     
  871.                                                     fi->rfarg = 0;
  872.                                                     fi->rpos = 0;
  873.                                                     fi->vpos = 0;
  874.                                                     fi->end = 0;
  875.                                                     fi->closed = false;
  876.                                                     fi->seek_end = false;
  877.                                                     fi->eof = false;
  878.                                                     fi->port = nil;
  879.                                                     fi->type = (writing) ? ACTION_FINDOUTPUT : ACTION_FINDINPUT;
  880.                                                     
  881.                                                     sp->cfile = newtm;
  882.                                                     sp->file_list = fi;
  883.                                                     fi->next = nil;
  884.                                                     
  885.                                                     return 0;
  886.                                                 } else {
  887.                                                     switch (numeric_reply(reply)) {
  888.                                                     case 450:
  889.                                                     case 520:
  890.                                                     case 550:
  891.                                                         if (writing) {
  892.                                                             error = ERROR_INVALID_COMPONENT_NAME;
  893.                                                         } else {
  894.                                                             error = ERROR_OBJECT_NOT_FOUND;
  895.                                                         }
  896.                                                         break;
  897.                                                     case 521:
  898.                                                     case 532:
  899.                                                     case 533:
  900.                                                         if (writing) {
  901.                                                             error = ERROR_WRITE_PROTECTED;
  902.                                                         } else {
  903.                                                             error = ERROR_READ_PROTECTED;
  904.                                                         }
  905.                                                         break;
  906.                                                     case 452:
  907.                                                     case 552:
  908.                                                         error = ERROR_DISK_FULL;
  909.                                                         break;
  910.                                                     case 553:
  911.                                                         if (writing) {
  912.                                                             error = ERROR_WRITE_PROTECTED;
  913.                                                         } else {
  914.                                                             error = ERROR_INVALID_COMPONENT_NAME;
  915.                                                         }
  916.                                                         break;
  917.                                                     default:
  918.                                                         error = ERROR_OBJECT_NOT_FOUND;
  919.                                                         break;
  920.                                                     }
  921.                                                 }
  922.                                             
  923.                                                 /* no need to disconnect sp */
  924.                                                 
  925.                                                 deallocate(fi, V_file_info);
  926.                                                 
  927.                                                 break_connection(sp, newtm);
  928.                                                 
  929.                                                 newtm->command = TCP_DISPOSE;
  930.                                                 PutMsg(tcp, &newtm->header);
  931.                                                 
  932.                                                 return error;
  933.                                             } else {
  934.                                                 show_string("Error reading response to RETR/STOR");
  935.                                                 error = ERROR_OBJECT_NOT_FOUND;
  936.                                             }
  937.                                             break_connection(sp, newtm);
  938.                                         } else {
  939.                                             show_string("error writing RETR/STOR");
  940.                                             error = ERROR_OBJECT_NOT_FOUND;
  941.                                         }
  942.                                     } else {
  943.                                         show_string("Error making connection");
  944.                                         error = ERROR_OBJECT_NOT_FOUND;
  945.                                     }
  946.                                 } else {
  947.                                     show_string("Bad PASV response");
  948.                                     deallocate(info, V_cstr);
  949.                                     error = ERROR_OBJECT_NOT_FOUND;
  950.                                 }
  951.                             } else {
  952.                                 show_string("no info");
  953.                                 error = ERROR_NO_FREE_STORE;
  954.                             }
  955.                         } else {
  956.                             show_string("non-'2' response to PASV");
  957.                             error = ERROR_OBJECT_NOT_FOUND;
  958.                         }
  959.                     } else {
  960.                         show_string("error reading response to PASV");
  961.                         error = ERROR_OBJECT_NOT_FOUND;
  962.                     }
  963.                 } else {
  964.                     show_string("error writing RETR/STOR");
  965.                     error = ERROR_OBJECT_NOT_FOUND;
  966.                 }
  967.             } else {
  968.                 show_string("error writing PASV");
  969.                 error = ERROR_OBJECT_NOT_FOUND;
  970.             }
  971.             
  972.             deallocate(fi, V_file_info);
  973.         } else error = ERROR_NO_FREE_STORE;
  974.         
  975.         newtm->command = TCP_DISPOSE;
  976.         PutMsg(tcp, &newtm->header);
  977.  
  978.         disconnect(sp);
  979.     } else error = ERROR_NO_FREE_STORE;
  980.     
  981.     return error;
  982. }
  983.  
  984. /* this is how large our flushing buffer is when attempting an abort */
  985. #define FLUSH_SIZE 100
  986.  
  987. void close_file(site *sp, boolean normal_close)
  988. /*
  989.  * close currently open file for site
  990.  * Inputs:
  991.  *    sp    : site pointer
  992.  *    normal_close : true if closed normally, false if closed by async abort
  993.  */
  994. {
  995.     tcpmessage *tm, *filetm, *ret;
  996.     file_info *fi;
  997.     struct MsgPort *sync;
  998.     b8 *info, reply[4], flush[FLUSH_SIZE];
  999.     b32 signals, asigs, rsigs;
  1000.     
  1001.     verify(sp, V_site);
  1002.     
  1003.     tm = sp->control;
  1004.     filetm = sp->cfile;
  1005.     fi = sp->file_list;
  1006.     
  1007.     verify(tm, V_tcpmessage);
  1008.     verify(filetm, V_tcpmessage);
  1009.     verify(fi, V_file_info);
  1010.     
  1011.     if (normal_close) {
  1012.         sp->cfile = nil;
  1013.         sp->cfile_type = 0;
  1014.         sp->file_list = 0;
  1015.     }
  1016.  
  1017.     state_change(sp, SS_CLOSING);
  1018.  
  1019.     sync = sp->sync;
  1020.     
  1021.     signals = (1 << sync->mp_SigBit) | sp->disconnect_signals | sp->abort_signals;
  1022.     asigs = sp->disconnect_signals | sp->abort_signals;
  1023.  
  1024. #ifdef VERIFY
  1025.     if (fi->eof && fi->rpos < fi->end) {
  1026.         show_string("Closing : EOF before fi->end");
  1027.         show_int(fi->rpos);
  1028.         show_int(fi->end);
  1029.     }
  1030. #endif
  1031.  
  1032.     if (fi->type == ACTION_FINDINPUT && fi->rpos < fi->end && !fi->eof) {
  1033.         if (normal_close) {
  1034.             deallocate(fi, V_file_info);
  1035.         } else {
  1036.             fi->eof = true;
  1037.         }
  1038.         
  1039.         /* have to ABOR :( */
  1040.         show_string("Attempting ABOR");
  1041.         
  1042.         if (control_write(sp, "ABOR\r\n", 0) != NO_ERROR) {
  1043.             show_string("close file failed X1");
  1044.  
  1045.             break_connection(sp, filetm);
  1046.  
  1047.             disconnect(sp);
  1048.     
  1049.             filetm->command = TCP_DISPOSE;
  1050.             PutMsg(tcp, &filetm->header);
  1051.             
  1052.             return;
  1053.         }
  1054.         
  1055.         /* can't use response because we need to flush filetm at the same time */
  1056.         
  1057.         filetm->command = TCP_READ;
  1058.         filetm->flags = 0;
  1059.         filetm->data = flush;
  1060.         filetm->length = FLUSH_SIZE;
  1061.         filetm->header.mn_ReplyPort = sync;
  1062.         
  1063.         tm->command = TCP_READ;
  1064.         tm->flags = FLAG_READLINE;
  1065.         tm->data = sp->read_buffer;
  1066.         tm->length = READ_BUFFER_LENGTH;
  1067.         tm->header.mn_ReplyPort = sync;
  1068.         
  1069.         PutMsg(tcp, &tm->header);
  1070.         PutMsg(tcp, &filetm->header);
  1071.         
  1072.         while (1) {
  1073.             rsigs = Wait(signals);
  1074.             if (rsigs & asigs) {
  1075.                 state_change(sp, SS_ABORTING);
  1076.                 
  1077.                 interrupt_message(sp, tm);
  1078.                 interrupt_message(sp, filetm);
  1079.                 
  1080.                 break;
  1081.             }
  1082.             
  1083.             ret = (tcpmessage *)GetMsg(sync);
  1084.             if (ret == tm) {
  1085.                 /* wait for filetm */
  1086.                 WaitPort(sync); GetMsg(sync);
  1087.                 break;
  1088.             }
  1089.             if (ret->error != NO_ERROR) {    /* filetm is done */
  1090.                 /* wait for tm */
  1091.                 WaitPort(sync); GetMsg(sync);
  1092.                 break;
  1093.             }
  1094.             PutMsg(tcp, &filetm->header);
  1095.         }
  1096.         
  1097.         break_connection(sp, filetm);
  1098.     
  1099.         if (normal_close) {
  1100.             filetm->command = TCP_DISPOSE;
  1101.             PutMsg(tcp, &filetm->header);
  1102.         }
  1103.         
  1104.         if (tm->error != NO_ERROR) {
  1105.             show_string("close file failed X2");
  1106.             disconnect(sp);
  1107.  
  1108.             return;
  1109.         }
  1110.         
  1111.         show_string("First ABOR response");
  1112. #ifdef VERIFY
  1113.         if (sp->read_buffer[3] == '-') show_string("continuation reply on ABOR");
  1114. #endif
  1115.         if (!normal_close) {
  1116.             /* leave the close response until we do the real close later */
  1117.             return;
  1118.         }
  1119.         
  1120.         switch (response(sp, 0, &info, reply)) {
  1121.         case NO_ERROR:
  1122.             break;
  1123.         default:
  1124.             show_string("close file failed X3");
  1125.             disconnect(sp);
  1126.  
  1127.             return;
  1128.         }
  1129.             
  1130.         show_string("Second ABOR response");
  1131.         
  1132. #ifdef VERIFY
  1133.         if (reply[0] != '2') {
  1134.             reply[3] = 0;
  1135.             show_string(reply);
  1136.             show_string(info);
  1137.         }
  1138. #endif
  1139.  
  1140.         if (info) deallocate(info, V_cstr);
  1141.         
  1142.         show_string("ABOR completed");
  1143.         return;
  1144.     }
  1145.  
  1146.     if (normal_close) {
  1147.         deallocate(fi, V_file_info);
  1148.     } else {
  1149.         fi->eof = true;
  1150.     }
  1151.     
  1152.     break_connection(sp, filetm);
  1153.  
  1154.     if (!normal_close) {
  1155.         /* leave the final close response until we do the real close later */
  1156.         return;
  1157.     }
  1158.  
  1159.     filetm->command = TCP_DISPOSE;
  1160.     PutMsg(tcp, &filetm->header);
  1161.     
  1162.     switch (response(sp, 0, &info, reply)) {
  1163.     case NO_ERROR:
  1164.         break;
  1165.     default:
  1166.         show_string("close failed 1");
  1167.         
  1168.         disconnect(sp);
  1169.         return;
  1170.     }
  1171.     
  1172.     if (info) deallocate(info, V_cstr);
  1173.     
  1174.     /* we don't really care what they returned here */
  1175.     
  1176. #ifdef VERIFY
  1177.     if (reply[0] != '2') {
  1178.         show_string("Non '2' close of file");
  1179.         sp->read_buffer[0] = reply[0];
  1180.         sp->read_buffer[1] = reply[1];
  1181.         sp->read_buffer[2] = reply[2];
  1182.         sp->read_buffer[3] = 0;
  1183.         show_string(sp->read_buffer);
  1184.     }
  1185. #endif
  1186.     
  1187.     return;
  1188. }
  1189.  
  1190. b32 delete_file(site *sp, b8 *s)
  1191. /*
  1192.  * delete file with name in s
  1193.  * Inputs:
  1194.  *    sp    : site pointer
  1195.  *    s    : full path name
  1196.  *
  1197.  * Returns:
  1198.  *    standard file system errors
  1199.  */
  1200. {
  1201.     b8 *leaf;
  1202.     b32 error;
  1203.     boolean perm;
  1204.     b8 *info, reply[3];
  1205.     ftpinfo *fi;
  1206.     
  1207.     verify(sp, V_site);
  1208.     truth(s != nil);
  1209.     
  1210.     if (s[0] == 0) {
  1211.         /* trying to delete root */
  1212.         return ERROR_OBJECT_WRONG_TYPE;
  1213.     }
  1214.     
  1215.     leaf = cd_parent(sp, s);
  1216.     if (!leaf) {
  1217.         /* again, being lazy here */
  1218.         return ERROR_DIR_NOT_FOUND;
  1219.     }
  1220.     
  1221.     state_change(sp, SS_DELETING);
  1222.     
  1223.     fi = get_info(sp, s);
  1224.     if (fi) {
  1225.         leaf = fi->name;
  1226.     }
  1227.     
  1228.     sprintf(sp->read_buffer, "DELE %s\r\n", leaf);
  1229.  
  1230.     if (control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
  1231.         if (response(sp, 0, &info, reply) == NO_ERROR) {
  1232.             perm = substr(info, "perm");
  1233.             if (info) deallocate(info, V_cstr);
  1234.             
  1235.             switch (reply[0]) {
  1236.             case '2':
  1237.                 return 0;    /* success */
  1238.             case '4':
  1239.                 /* temp failure ... */
  1240.                 /* most likely reason */
  1241.                 return ERROR_OBJECT_IN_USE;
  1242.             default:
  1243.                 if (numeric_reply(reply) == 502) return ERROR_ACTION_NOT_KNOWN;
  1244.                 if (perm)
  1245.                     return ERROR_DELETE_PROTECTED;
  1246.                 else
  1247.                     return ERROR_OBJECT_NOT_FOUND;
  1248.             }
  1249.         } else {
  1250.             disconnect(sp);
  1251.             error = ERROR_OBJECT_NOT_FOUND;
  1252.         }
  1253.     } else {
  1254.         disconnect(sp);
  1255.         error = ERROR_OBJECT_NOT_FOUND;
  1256.     }
  1257.     
  1258.     return error;
  1259. }
  1260.  
  1261. b32 delete_directory(site *sp, b8 *s)
  1262. /*
  1263.  * delete directory with name in s
  1264.  * Inputs:
  1265.  *    sp    : site pointer
  1266.  *    s    : full path name
  1267.  *
  1268.  * Returns:
  1269.  *    standard file system errors
  1270.  */
  1271. {
  1272.     b8 *leaf;
  1273.     b32 error;
  1274.     boolean perm, no_such;
  1275.     b8 *info, reply[3];
  1276.     ftpinfo *fi;
  1277.     
  1278.     verify(sp, V_site);
  1279.     truth(s != nil);
  1280.     
  1281.     if (s[0] == 0) {
  1282.         /* trying to delete root */
  1283.         return ERROR_OBJECT_WRONG_TYPE;
  1284.     }
  1285.     
  1286.     leaf = cd_parent(sp, s);
  1287.     if (!leaf) {
  1288.         /* again, being lazy here */
  1289.         return ERROR_DIR_NOT_FOUND;
  1290.     }
  1291.     
  1292.     state_change(sp, SS_DELETING);
  1293.     
  1294.     fi = get_info(sp, s);
  1295.     if (fi) {
  1296.         leaf = fi->name;
  1297.     }
  1298.  
  1299.     sprintf(sp->read_buffer, "RMD %s\r\n", leaf);
  1300.     if (control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
  1301.         if (response(sp, 0, &info, reply) == NO_ERROR) {
  1302.             perm = substr(info, "perm");
  1303.             no_such = substr(info, "no such");
  1304.             if (info) deallocate(info, V_cstr);
  1305.             
  1306.             switch (reply[0]) {
  1307.             case '2':
  1308.                 return 0;    /* success */
  1309.             case '4':
  1310.                 /* temp failure ... */
  1311.                 /* most likely reason */
  1312.                 return ERROR_OBJECT_IN_USE;
  1313.             default:
  1314.                 if (numeric_reply(reply) == 502) return ERROR_ACTION_NOT_KNOWN;
  1315.                 if (perm) {
  1316.                     return ERROR_DELETE_PROTECTED;
  1317.                 } else if (no_such) {
  1318.                     return ERROR_OBJECT_NOT_FOUND;
  1319.                 } else {
  1320.                     return ERROR_DIRECTORY_NOT_EMPTY;
  1321.                 }
  1322.             }
  1323.         } else {
  1324.             disconnect(sp);
  1325.             error = ERROR_OBJECT_NOT_FOUND;
  1326.         }
  1327.     } else {
  1328.         disconnect(sp);
  1329.         error = ERROR_OBJECT_NOT_FOUND;
  1330.     }
  1331.     
  1332.     return error;
  1333. }
  1334.  
  1335. b32 make_directory(site *sp, b8 *s)
  1336. /*
  1337.  * make directory with name in s
  1338.  * Inputs:
  1339.  *    sp    : site pointer
  1340.  *    s    : full path name
  1341.  *
  1342.  * Returns:
  1343.  *    standard file system errors
  1344.  */
  1345. {
  1346.     b8 *leaf;
  1347.     b32 error;
  1348.     boolean exists;
  1349.     b8 *info, reply[3];
  1350.     
  1351.     verify(sp, V_site);
  1352.     truth(s != nil);
  1353.     
  1354.     if (s[0] == 0) {
  1355.         /* trying to mkd root */
  1356.         return ERROR_OBJECT_WRONG_TYPE;
  1357.     }
  1358.     
  1359.     leaf = cd_parent(sp, s);
  1360.     if (!leaf) {
  1361.         /* again, being lazy here */
  1362.         return ERROR_DIR_NOT_FOUND;
  1363.     }
  1364.     
  1365.     state_change(sp, SS_MAKEDIR);
  1366.     
  1367.     sprintf(sp->read_buffer, "MKD %s\r\n", leaf);
  1368.     if (control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
  1369.         if (response(sp, 0, &info, reply) == NO_ERROR) {
  1370.             exists = substr(info, "exist");
  1371.             
  1372.             if (info) deallocate(info, V_cstr);
  1373.             
  1374.             switch (reply[0]) {
  1375.             case '2':
  1376.                 return 0;    /* success */
  1377.             case '4':
  1378.                 /* temp failure ... */
  1379.                 /* most likely reason */
  1380.                 return ERROR_OBJECT_IN_USE;
  1381.             default:
  1382.                 if (numeric_reply(reply) == 502) return ERROR_ACTION_NOT_KNOWN;
  1383.                 if (exists)
  1384.                     return ERROR_OBJECT_EXISTS;
  1385.                 else
  1386.                     return ERROR_WRITE_PROTECTED;
  1387.             }
  1388.         } else {
  1389.             disconnect(sp);
  1390.             error = ERROR_OBJECT_NOT_FOUND;
  1391.         }
  1392.     } else {
  1393.         disconnect(sp);
  1394.         error = ERROR_OBJECT_NOT_FOUND;
  1395.     }
  1396.     
  1397.     return error;
  1398. }
  1399.  
  1400. b32 rename_object(site *sp, b8 *from, b8 *to)
  1401. /*
  1402.  * renames file 'from' to 'to'
  1403.  * Inputs:
  1404.  *    sp    : site pointer
  1405.  *    from, to: null terminated file names
  1406.  *
  1407.  * Returns:
  1408.  *    file system error, or 0 indicating success
  1409.  */
  1410. {
  1411.     b8 *leaf1, *leaf2;
  1412.     b8 *info, reply[3];
  1413.     boolean perm, exist;
  1414.     
  1415.     if (sp->unix_paths) {
  1416.         if (!change_dir(sp, "")) {
  1417.             return ERROR_DIR_NOT_FOUND;
  1418.         }
  1419.         
  1420.         leaf1 = from;
  1421.         leaf2 = to;
  1422.     } else {
  1423.         leaf1 = cd_parent(sp, from);
  1424.         if (!leaf1) {
  1425.             return ERROR_DIR_NOT_FOUND;
  1426.         }
  1427.         
  1428.         leaf2 = to + strlen(to) - 1;
  1429.         while (leaf2 > to && *leaf2 != '/') leaf2--;
  1430.         
  1431.         if (leaf2 > to) *leaf2 = 0;
  1432.         
  1433.         if (strcmp(to, sp->cwd) == 0) {    /* they are in the same directory, we can do it */
  1434.             if (leaf2 > to) *leaf2 = '/';
  1435.         } else {
  1436.             return ERROR_ACTION_NOT_KNOWN;
  1437.         }
  1438.     }
  1439.     
  1440.     state_change(sp, SS_RENAMING);
  1441.     
  1442.     sprintf(sp->read_buffer, "RNFR %s\r\n", leaf1);
  1443.     if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
  1444.         disconnect(sp);
  1445.         return ERROR_OBJECT_NOT_FOUND;
  1446.     }
  1447.     
  1448.     if (sp->quick) {
  1449.         sprintf(sp->read_buffer, "RNTO %s\r\n", leaf2);
  1450.         if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
  1451.             disconnect(sp);
  1452.             return ERROR_OBJECT_NOT_FOUND;
  1453.         }
  1454.     }
  1455.     
  1456.     /* response to RNFR */
  1457.     if (response(sp, 0, &info, reply) != NO_ERROR) {
  1458.         disconnect(sp);
  1459.         return ERROR_OBJECT_NOT_FOUND;
  1460.     }
  1461.     
  1462.     perm = substr(info, "perm");
  1463.         
  1464.     if (info) deallocate(info, V_cstr);
  1465.     
  1466.     if (reply[0] != '3') {
  1467.         if (perm) {
  1468.             return ERROR_WRITE_PROTECTED;
  1469.         }
  1470.         return ERROR_OBJECT_NOT_FOUND;
  1471.     }
  1472.     
  1473.     if (!sp->quick) {
  1474.         sprintf(sp->read_buffer, "RNTO %s\r\n", leaf2);
  1475.         if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
  1476.             disconnect(sp);
  1477.             return ERROR_OBJECT_NOT_FOUND;
  1478.         }
  1479.     }
  1480.     
  1481.     /* response to RNTO */
  1482.     if (response(sp, 0, &info, reply) != NO_ERROR) {
  1483.         disconnect(sp);
  1484.         return ERROR_OBJECT_NOT_FOUND;
  1485.     }
  1486.     
  1487.     perm = substr(info, "perm");
  1488.     exist = substr(info, "exist");
  1489.     
  1490.     if (info) deallocate(info, V_cstr);
  1491.     
  1492.     if (reply[0] != '2') {
  1493.         if (perm) {
  1494.             return ERROR_WRITE_PROTECTED;
  1495.         }
  1496.         if (exist) {
  1497.             return ERROR_OBJECT_EXISTS;
  1498.         }
  1499.         return ERROR_INVALID_COMPONENT_NAME;
  1500.     }
  1501.     
  1502.     return 0;
  1503. }
  1504.  
  1505. boolean change_dir(site *sp, b8 *new_dir)
  1506. /*
  1507.  * change directory to new_dir
  1508.  * Inputs:
  1509.  *    sp    : site pointer
  1510.  *    new_dir    : null terminated path name
  1511.  *
  1512.  * Returns:
  1513.  *    true if change_dir was successful
  1514.  */
  1515. {
  1516.     tcpmessage *tm;
  1517.     struct MsgPort *sync;
  1518.     b8 *info, reply[4];
  1519.     b8 *z, *s;
  1520.     
  1521.     verify(sp, V_site);
  1522.     truth(new_dir != nil);
  1523.     
  1524.     tm = sp->control;
  1525.     sync = sp->sync;
  1526.     
  1527.     verify(tm, V_tcpmessage);
  1528.     
  1529.     /* check to see if we are already there */
  1530.  
  1531.     if (!sp->cwd && new_dir[0] == 0) return true;
  1532.     if (sp->cwd && strcmp(sp->cwd, new_dir) == 0) return true;
  1533.     
  1534.     /* have to explicitly change there */
  1535.     
  1536.     if (sp->cfile) {    /* can't do _anything_ while we have a file opened */
  1537.         if (sp->file_list->closed) {
  1538.             close_file(sp, true);
  1539.         } else {
  1540.             return false;
  1541.         }
  1542.     }
  1543.     
  1544.     if (!sp->connected) {
  1545.         return false;
  1546.     }
  1547.  
  1548.     state_change(sp, SS_CWD);
  1549.  
  1550.     if (sp->cwd) {
  1551.         deallocate(sp->cwd, V_cstr);
  1552.         sp->cwd = nil;
  1553.     }
  1554.     
  1555.     /* first we change to the root */
  1556.     
  1557.     if (sp->root) {
  1558.         sprintf(sp->read_buffer, "CWD %s\r\n", sp->root);
  1559.     } else {
  1560.         strcpy(sp->read_buffer, "CWD\r\n");
  1561.     }
  1562.     
  1563.     if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
  1564.         show_string("change dir failed 1");
  1565.         
  1566.         disconnect(sp);
  1567.         
  1568.         return false;
  1569.     }
  1570.     
  1571.     /* this CWD is vital */
  1572.     
  1573.     if (response(sp, 0, &info, reply) != NO_ERROR) {
  1574.         show_string("change dir failed 2");
  1575.         
  1576.         disconnect(sp);
  1577.         
  1578.         return false;
  1579.     }
  1580.     
  1581.     if (info) deallocate(info, V_cstr);
  1582.     
  1583.     if (reply[0] != '2') {
  1584.         show_string("change dir failed 3");
  1585.         
  1586.         disconnect(sp);
  1587.         
  1588.         return false;
  1589.     }
  1590.     
  1591.     /*
  1592.      * ok, we are at (should be at :) the root (or at least what
  1593.      * we consider to be the root) of the FS
  1594.      */
  1595.     
  1596.     if (new_dir[0] == 0) {
  1597.         /* they wanted to change to the root ... so nothing further need be done */
  1598.         return true;
  1599.     }
  1600.  
  1601.     if (sp->unix_paths) {
  1602.         sprintf(sp->read_buffer, "CWD %s\r\n", new_dir);
  1603.         
  1604.         if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
  1605.             show_string("change dir failed 4");
  1606.             
  1607.             disconnect(sp);
  1608.             
  1609.             return false;
  1610.         }
  1611.         
  1612.         if (response(sp, 0, &info, reply) != NO_ERROR) {
  1613.             show_string("change dir failed 5");
  1614.             
  1615.             disconnect(sp);
  1616.             
  1617.             return false;
  1618.         }
  1619.         
  1620.         if (info) deallocate(info, V_cstr);
  1621.         
  1622.         if (reply[0] == '2') {
  1623.             sp->cwd = (b8 *)allocate(strlen(new_dir) + 1, V_cstr);
  1624.             if (sp->cwd) {
  1625.                 strcpy(sp->cwd, new_dir);
  1626.                 return true;
  1627.             }
  1628.             goto fail_to_root;
  1629.         }
  1630.         
  1631.         /* ok, our clumped cwd didn't work, lets try it the slow way */
  1632.     }
  1633.  
  1634.     s = z = new_dir;
  1635.     while (*z) {
  1636.         while (*s && *s != '/') s++;
  1637.         
  1638.         if (*s == '/') {
  1639.             *s = 0;
  1640.             sprintf(sp->read_buffer, "CWD %s\r\n", z);
  1641.             *s++ = '/';
  1642.         } else {
  1643.             sprintf(sp->read_buffer, "CWD %s\r\n", z);
  1644.         }
  1645.         
  1646.         if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
  1647.             show_string("change dir failed 6");
  1648.             
  1649.             disconnect(sp);
  1650.             
  1651.             return false;
  1652.         }
  1653.         
  1654.         if (response(sp, 0, &info, reply) != NO_ERROR) {
  1655.             show_string("change dir failed 7");
  1656.             
  1657.             disconnect(sp);
  1658.             
  1659.             return false;
  1660.         }
  1661.         
  1662.         if (info) deallocate(info, V_cstr);
  1663.         
  1664.         if (reply[0] != '2') {
  1665.             goto fail_to_root;
  1666.         }
  1667.         
  1668.         z = s;
  1669.     }
  1670.     
  1671.     /* we've succeeded where unix_paths failed, so ... */
  1672.     
  1673.     sp->unix_paths = false;
  1674.     
  1675.     sp->cwd = (b8 *)allocate(strlen(new_dir) + 1, V_cstr);
  1676.     if (sp->cwd) {
  1677.         strcpy(sp->cwd, new_dir);
  1678.         return true;
  1679.     }
  1680.     
  1681. fail_to_root:
  1682.     /* something went wrong ... who knows where we are? ... go back to the root */
  1683.     
  1684.     if (sp->root) {
  1685.         sprintf(sp->read_buffer, "CWD %s\r\n", sp->root);
  1686.     } else {
  1687.         strcpy(sp->read_buffer, "CWD\r\n");
  1688.     }
  1689.     
  1690.     if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
  1691.         show_string("change dir failed 8");
  1692.  
  1693.         disconnect(sp);
  1694.         
  1695.         return false;
  1696.     }
  1697.     
  1698.     if (response(sp, 0, &info, reply) != NO_ERROR) {
  1699.         show_string("change dir failed 9");
  1700.         
  1701.         disconnect(sp);
  1702.         
  1703.         return false;
  1704.     }
  1705.     
  1706.     if (info) deallocate(info, V_cstr);
  1707.     
  1708.     if (reply[0] == '2') {
  1709.         return false;
  1710.     }
  1711.     
  1712.     show_string("change dir failed 10");
  1713.  
  1714.     disconnect(sp);
  1715.     
  1716.     return false;
  1717. }
  1718.  
  1719. b8 *cd_parent(site *sp, b8 *path)
  1720. /*
  1721.  * change to the parent dir of the object described by path
  1722.  * Inputs:
  1723.  *    sp    : site pointer
  1724.  *    path    : string describing object
  1725.  *
  1726.  * Returns:
  1727.  *    pointer to leaf name (last component of path)
  1728.  *    or nil ... generally indicates gross error or dir not found
  1729.  */
  1730. {
  1731.     b8 *leaf;
  1732.     boolean cd;
  1733.     
  1734.     verify(sp, V_site);
  1735.     truth(path != nil);
  1736.  
  1737.     /* start at end of pathname and work back til we find a / */
  1738.     
  1739.     leaf = path + strlen(path) - 1;
  1740.     while (leaf > path && *leaf != '/') leaf--;
  1741.     
  1742.     if (leaf == path) {
  1743.         /* no /, so we are talking about an object in the root dir */
  1744.         
  1745.         cd = change_dir(sp, "");
  1746.     } else {
  1747.         /* temporarily knock out / to get parent path */
  1748.         
  1749.         *leaf = 0;
  1750.         cd = change_dir(sp, path);
  1751.         
  1752.         /* then restore the / and move over it */
  1753.         *leaf++ = '/';
  1754.     }
  1755.     
  1756.     if (cd) return leaf;
  1757.     else return nil;
  1758. }
  1759.  
  1760. void convert_nt_entry(b8 *is)          // Input String from add_info()
  1761. /*
  1762.  *  if this is a WinNT entry, convert (in place) to a
  1763.  *  *nix style record. (Unix, Linux, Xenix...)
  1764.  *
  1765.  *  03/09/96 Ron Flory (RJF)
  1766.  *
  1767.  *  This function kindly donated by Ron Flory (), and remain Copyright 1996,
  1768.  *  all rights reserved by him.
  1769.  */
  1770. {
  1771.     static b8      *nt_buf = 0;            // alloc once, but NEVER free (??)
  1772.     static char    *months[] = {    "Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ",
  1773.                     "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec " };
  1774.     b8        *c1, *c2, tbuf[32], c;
  1775.     short i;
  1776.  
  1777.     if (!nt_buf)                        // if our conversion buf not alloc'd
  1778.         nt_buf = (b8*) malloc(READ_BUFFER_LENGTH);
  1779.  
  1780.     // ***** verify inbuffer viable, MsDos-like, conversion buf alloc'd *****
  1781.  
  1782.     if ( (!is) || (!nt_buf) || (!isdigit(*is)) || (strlen(is) < 41) )
  1783.         return;
  1784.  
  1785.     *nt_buf = 0;                        // term NT->Unix conversion buffer
  1786.     c1 = strchr(is, 0x0d);              // look for MsDos CR
  1787.     if (c1)
  1788.         *c1 = 0;                         // then, remove CR
  1789.  
  1790.     // ***** avoid altering 'is' unless this really is an MsDos entry *****
  1791.  
  1792.  
  1793.     strcpy(nt_buf, "-rwxrwxrwx 9 x x ");    // assume this is a file
  1794.                         // plug dummy fields (ignored)
  1795.                         // Note: execute flags set
  1796.  
  1797.     if ( (strstr(is, "<DIR>")) || (strstr(is, "<dir>")) ) // directory ?
  1798.     {
  1799.         *nt_buf = 'd';                   // force directory flag in return buf
  1800.         strcat(nt_buf, "size ");         // dummy 'size' (ignored on dirs)
  1801.     }
  1802.     else
  1803.     {
  1804.         // ***** extract filesize *****
  1805.  
  1806.         c1 = is +30;                     // start of length field
  1807.         c2 = nt_buf + strlen(nt_buf);
  1808.  
  1809.         while (*c1 == ' ')               // skip leading spaces
  1810.             c1++;
  1811.  
  1812.         while ( ((c = *(c1++)) != 0) && (c <= '9') && (c > ' ') )
  1813.             if (c != ',')
  1814.                 *(c2++) = c;               // copy filesize digits (skip commas)
  1815.  
  1816.         *(c2++) = ' ';                   // padd
  1817.         *c2     = 0;                     // term
  1818.     }
  1819.  
  1820.     // ***** extract creation date *****
  1821.  
  1822.     memcpy(tbuf, is, 2);                // month digits (01..12)
  1823.     tbuf[2] = 0;
  1824.  
  1825.     i = atol(tbuf);                     // convert digits to 1..12 month index
  1826.  
  1827.     if ( (i) && (i < 13) )              // valid month index ??
  1828.         strcat(nt_buf, months[i -1]);    // insert month string
  1829.     memcpy(tbuf, is +3, 2);             // day digits (01..31)
  1830.     tbuf[2] = 0;
  1831.  
  1832.     if (*tbuf == '0')                   // leading space on 'day' ??
  1833.         *tbuf = ' ';                     // need leading space, not zero
  1834.  
  1835.     strcat(nt_buf, tbuf);               // append day of month
  1836.     strcat(nt_buf, " ");
  1837.  
  1838.     // ***** get creation time *****
  1839.  
  1840.     c1 = strchr(is, ':');               // find creation time field
  1841.     if (!c1)
  1842.         return;                          // abort, not an MsDos entry
  1843.  
  1844.     memcpy(tbuf, c1 -2, 2);             // extract hour
  1845.     tbuf[2] = 0;                        // term
  1846.  
  1847.     i = atoi(tbuf);                     // month digits to index (01..12)
  1848.  
  1849.     if (toupper(*(c1 +3)) == 'P')       // PM ???
  1850.         i += 12;
  1851.  
  1852.     stci_d(tbuf, i);                    // itoa()
  1853.     strcat(nt_buf, tbuf);               // hour
  1854.     strcat(nt_buf, ":");
  1855.  
  1856.     memcpy(tbuf, c1 +1, 2);             // minutes
  1857.     tbuf[2] = 0;
  1858.     strcat(nt_buf, tbuf);               // minutes
  1859.     strcat(nt_buf, " ");                // padd
  1860.  
  1861.     // **** extract directory/file name *****
  1862.  
  1863.     c1 = is +39;                        // start of fid (I hate hard offsets)
  1864.     c2 = nt_buf + strlen(nt_buf);
  1865.  
  1866.     while ((c = *c1) > ' ')             // (might want to skip leading spaces)
  1867.         *(c2++) = *(c1++);               // copy entire dir/filename
  1868.  
  1869.     *c2 = 0;                            // term buffer
  1870.  
  1871.     // ***** copy translated MsDos entry over orig Unix entry *****
  1872.  
  1873.     strcpy(is, nt_buf);                 // if we get here, should be OK (?!*)
  1874. }
  1875. #define LAST_FIELDS 5
  1876.  
  1877. void add_info(struct info_header *ih, b8 *s)
  1878. /*
  1879.  * parses s and adds the information to header ih
  1880.  * Inputs:
  1881.  *    ih    : info_header
  1882.  *    s    : line returned from LIST
  1883.  */
  1884. {
  1885.     b32 perm;    /* permission bits */
  1886.     b32 size;
  1887.     b8 *fields[LAST_FIELDS], *z;    /* want the last 5 fields */
  1888.     b8 tempd[15], tempt[10];
  1889.     int i, num_fields;
  1890.     struct DateTime dtime;
  1891.  
  1892.     convert_nt_entry(s);          // if NT entry, convert to Unix format (RJF)
  1893.     
  1894.     if (s[0] <= ' ') return;
  1895.     
  1896.     for (i = 0; i < LAST_FIELDS; i++) fields[i] = s;    /* safety */
  1897.     
  1898.     perm = 0;
  1899.     if (*s == 'd') perm |= MYFLAG_DIR;
  1900.     if (*s == 'l') {    /* throw away yucky unix soft links */
  1901.         perm |= MYFLAG_DIR;        /* assume its a directory ... it _may_ be a file ... */
  1902.         z = s + strlen(s) - 1;
  1903.         while (z > s && !(z[0] == '-' && z[1] == '>')) z--;
  1904.         if (z > s) {
  1905.             *z-- = 0;
  1906.             /* and any space that they've put in for looks */
  1907.             if (z > s && *z == ' ')
  1908.             {
  1909.                 *z = 0;
  1910.             }
  1911.         }
  1912.     }
  1913.     s++;
  1914.     if (*s < 'A') perm |= FIBF_READ;
  1915.     s++;
  1916.     if (*s < 'A') perm |= FIBF_WRITE | FIBF_DELETE;
  1917.     s++;
  1918.     if (*s < 'A') perm |= FIBF_EXECUTE;
  1919.     s++;
  1920.     
  1921.     while (*s > ' ') s++;
  1922.     
  1923.     num_fields = 1;
  1924.     
  1925.     do {
  1926.         while (*s > 0 && *s <= ' ') s++;
  1927.         
  1928.         if (!*s) break;
  1929.         
  1930.         for (i = 0; i < LAST_FIELDS - 1; i++) {
  1931.             fields[i] = fields[i+1];
  1932.         }
  1933.         num_fields++;
  1934.         fields[LAST_FIELDS - 1] = s;
  1935.         if (num_fields == 9) break;
  1936.         if (num_fields == 8)
  1937.         {
  1938.             if (s[0] >= '0' && s[0] <= '9' && s[3] >= '0' && s[4] <= '9')
  1939.             {
  1940.                 // go around another time
  1941.             }
  1942.             else
  1943.             {
  1944.                 break;
  1945.             }
  1946.         }
  1947.         while (*s > ' ') s++;
  1948.     } while (*s);
  1949.     
  1950.     /* ok, we now have the last 5 fields, so process them  */
  1951.     
  1952.     size = atoi(fields[0]);
  1953.     
  1954.     s = fields[4];
  1955.     while (*s >= ' ') s++;
  1956.     *s = 0;
  1957.     
  1958.     if (num_fields > 4) {
  1959.     
  1960.         /* throw away . & .. */
  1961.         if (fields[4][0] == '.') {
  1962.             if (fields[4][1] == '\0') return;
  1963.             if (fields[4][1] == '.' && fields[4][2] == '\0') return;
  1964.         }
  1965.         
  1966.         dtime.dat_Format = FORMAT_INT;
  1967.         dtime.dat_Flags = 0;
  1968.         dtime.dat_StrDay = nil;
  1969.         dtime.dat_StrDate = tempd;
  1970.         dtime.dat_StrTime = tempt;
  1971.         
  1972.         s = fields[3];
  1973.  
  1974.         if (s[1] == ':' || s[2] == ':') {
  1975.             tempd[0] = (year / 10) % 10 + '0';
  1976.             tempd[1] = year % 10 + '0';
  1977.             tempd[2] = '-';
  1978.             tempd[3] = fields[1][0];
  1979.             tempd[4] = fields[1][1];
  1980.             tempd[5] = fields[1][2];
  1981.             tempd[6] = '-';
  1982.             tempd[7] = fields[2][0];
  1983.             tempd[8] = fields[2][1];
  1984.             if (tempd[8] < '0') tempd[8] = 0;
  1985.             else tempd[9] = 0;
  1986.             
  1987.             while (*s > ' ') s++;
  1988.             *s = 0;
  1989.             
  1990.             strcpy(tempt, fields[3]);
  1991.             strcat(tempt, ":00");
  1992.         } else {
  1993.             tempd[0] = fields[3][2];
  1994.             tempd[1] = fields[3][3];
  1995.             tempd[2] = '-';
  1996.             tempd[3] = fields[1][0];
  1997.             tempd[4] = fields[1][1];
  1998.             tempd[5] = fields[1][2];
  1999.             tempd[6] = '-';
  2000.             tempd[7] = fields[2][0];
  2001.             tempd[8] = fields[2][1];
  2002.             
  2003.             if (tempd[8] < '0') tempd[8] = 0;
  2004.             else tempd[9] = 0;
  2005.             
  2006.             strcpy(tempt, "12:00:00");
  2007.         }
  2008.         
  2009.         StrToDate(&dtime);
  2010.         
  2011.         add_ftpinfo(ih, fields[4], dtime.dat_Stamp, size, (size + 1023) / 1024, perm);
  2012.     }
  2013. }
  2014.  
  2015. boolean get_list(site *sp, struct info_header *ih)
  2016. /*
  2017.  * gets LIST in cwd and puts it in ih
  2018.  * Inputs:
  2019.  *    sp    : site pointer
  2020.  *    ih    : info_header to hold list information
  2021.  *
  2022.  * Returns:
  2023.  *    true if LIST was successful
  2024.  */
  2025. {
  2026.     tcpmessage *tm, *listm;
  2027.     struct MsgPort *sync;
  2028.     b8 reply[3], *info;
  2029.     b16 portn;
  2030.     b32 signals, asigs, rsigs;
  2031.     
  2032.     verify(sp, V_site);
  2033.     verify(ih, V_info_header);
  2034.     
  2035.     truth(sp->connected);
  2036.     truth(sp->cfile == nil);
  2037.     
  2038.     state_change(sp, SS_LISTING);
  2039.     
  2040.     tm = sp->control;
  2041.     verify(tm, V_tcpmessage);
  2042.     
  2043.     sync = sp->sync;
  2044.     
  2045.     asigs = sp->disconnect_signals | sp->abort_signals;
  2046.     signals = (1 << sync->mp_SigBit) | asigs;
  2047.     
  2048.     listm = new_message(sp);
  2049.     if (!listm) return false;
  2050.     
  2051.     if (control_write(sp, "PASV\r\n", 0) == NO_ERROR) {
  2052.         if (!sp->quick || control_write(sp, "LIST\r\n", 0) == NO_ERROR) {
  2053.             if (response(sp, 0, &info, reply) == NO_ERROR) {
  2054.                 if (reply[0] == '2' && info) {
  2055.                     if (passive_response(info, sp->read_buffer, &portn)) {
  2056.                         
  2057.                         deallocate(info, V_cstr);
  2058.                         
  2059.                         if (make_connection(sp, listm, sp->read_buffer, portn, 0) == NO_ERROR) {
  2060.                             if (sp->quick || control_write(sp, "LIST\r\n", 0) == NO_ERROR) {
  2061.                                 /* this next response will be to the LIST */
  2062.                                 if (response(sp, 0, &info, reply) == NO_ERROR) {
  2063.                                     if (info) deallocate(info, V_cstr);
  2064.                                 
  2065.                                     if (reply[0] == '1') {
  2066.                                         /* list should be coming through listm now */
  2067.                                         
  2068.                                         goto read_list;
  2069.                                     }
  2070.                                 }
  2071.                             }
  2072.                             break_connection(sp, listm);
  2073.                         }
  2074.                     } else {
  2075.                         if (info) deallocate(info, V_cstr);
  2076.                     }
  2077.                 } else {
  2078.                     if (info) deallocate(info, V_cstr);
  2079.                 }
  2080.             }
  2081.         }
  2082.     }
  2083.     
  2084.     listm->command = TCP_DISPOSE;
  2085.     PutMsg(tcp, &listm->header);
  2086.     
  2087.     disconnect(sp);
  2088.     
  2089.     return false;
  2090.     
  2091. read_list:
  2092.     listm->command = TCP_READ;
  2093.     listm->data = sp->read_buffer;
  2094.     listm->flags = FLAG_READLINE;
  2095.     listm->length = READ_BUFFER_LENGTH;
  2096.     listm->header.mn_ReplyPort = sync;
  2097.     
  2098.     do {
  2099.         PutMsg(tcp, &listm->header);
  2100.         rsigs = Wait(signals);
  2101.         if (rsigs & asigs) {
  2102.             state_change(sp, SS_ABORTING);
  2103.             
  2104.             interrupt_message(sp, listm);
  2105.             
  2106.             if (rsigs & sp->disconnect_signals) {
  2107.                 break_connection(sp, listm);
  2108.                 
  2109.                 listm->command = TCP_DISPOSE;
  2110.                 PutMsg(tcp, &listm->header);
  2111.                 
  2112.                 disconnect(sp);
  2113.                 return false;
  2114.             }
  2115.         } else {
  2116.             GetMsg(sync);
  2117.         }
  2118.         
  2119.         if (listm->result > 0) {
  2120.             sp->read_buffer[listm->result] = 0;
  2121.             add_info(ih, sp->read_buffer);
  2122.         }
  2123.     } while (listm->error == NO_ERROR);
  2124.     
  2125.     break_connection(sp, listm);
  2126.     
  2127.     listm->command = TCP_DISPOSE;
  2128.     PutMsg(tcp, &listm->header);
  2129.     
  2130.     if (response(sp, 0, &info, reply) != NO_ERROR) {
  2131.         show_string("get list failed 8");
  2132.         
  2133.         disconnect(sp);
  2134.         
  2135.         return true;
  2136.     }
  2137.     
  2138.     if (info) deallocate(info, V_cstr);
  2139.     
  2140. #ifdef VERIFY
  2141.     if (reply[0] != '2') {
  2142.         show_string("received non-2 for end of LIST");
  2143.     }
  2144. #endif
  2145.     return true;
  2146. }
  2147.  
  2148. boolean prelim(site *sp, struct Window *w)
  2149. /*
  2150.  * once logged in, does preliminary setup stuff ... for now
  2151.  * sets TYPE I and figures out where the root of the fs is
  2152.  * Inputs:
  2153.  *     sp    : site pointer
  2154.  *    w    : the connection cancel window
  2155.  *
  2156.  * Returns:
  2157.  *    true if setup was successful
  2158.  */
  2159. {
  2160.     b32 csig;
  2161.     b8 *info, reply[3];
  2162.     b8 *s, *z;
  2163.     
  2164.     csig = (1 << w->UserPort->mp_SigBit);
  2165.     
  2166.     if (control_write(sp, "TYPE I\r\n", csig) == NO_ERROR) {
  2167.         /* we either need to change to root, or work out where root is */
  2168.         if (sp->root) {
  2169.             sprintf(sp->read_buffer, "CWD %s\r\n", sp->root);
  2170.         } else {
  2171.             strcpy(sp->read_buffer, "PWD\r\n");
  2172.         }
  2173.         if (!sp->quick || control_write(sp, sp->read_buffer, csig) == NO_ERROR) {
  2174.             /* first response is to TYPE I */
  2175.             if (response(sp, csig, &info, reply) == NO_ERROR) {
  2176.                 /* we don't really care what they replied */
  2177.                 if (info) deallocate(info, V_cstr);
  2178.                 if (sp->root) {
  2179.                     sprintf(sp->read_buffer, "CWD %s\r\n", sp->root);
  2180.                 } else {
  2181.                     strcpy(sp->read_buffer, "PWD\r\n");
  2182.                 }
  2183.                 if (sp->quick || control_write(sp, sp->read_buffer, csig) == NO_ERROR) {
  2184.                     /* ... next response is to CWD/PWD */
  2185.                     if (response(sp, csig, &info, reply) == NO_ERROR) {
  2186.                         if (reply[0] == '2') {
  2187.                             if (sp->root) {
  2188.                                 /* was the CWD ... was successful */
  2189.                                 if (info) deallocate(info, V_cstr);
  2190.                                 
  2191.                                 return true;
  2192.                             } else if (info) {
  2193.                                 /* was the PWD ... have to extract the root path */
  2194.                                 s = info;
  2195.                                 while (*s && *s != '"') s++;
  2196.                                 if (*s) {
  2197.                                     s++;
  2198.                                     z = s;
  2199.                                     while (*z && *z != '"') z++;
  2200.                                     if (*z) {
  2201.                                         sp->root = (b8 *)allocate(z - s + 1, V_cstr);
  2202.                                         if (sp->root) {
  2203.                                             if (z != s)
  2204.                                                 memcpy(sp->root, s, z - s);
  2205.                                             
  2206.                                             sp->root[z - s] = 0;
  2207.                                             
  2208.                                             deallocate(info, V_cstr);
  2209.                                             return true;
  2210.                                         } else if (sp->error_messages)
  2211.                                             inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_OOM_ROOT], nil, 0);
  2212.                                     } else if (sp->error_messages)
  2213.                                         inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_PWD_GARBAGE], nil, 0);
  2214.                                 } else if (sp->error_messages)
  2215.                                     inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_PWD_GARBAGE], nil, 0);
  2216.                                 deallocate(info, V_cstr);
  2217.                             } else {
  2218.                                 if (sp->error_messages)
  2219.                                     inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_FAILED_PWD], nil, 0);
  2220.                             }
  2221.                         } else {
  2222.                             if (sp->error_messages)
  2223.                                 ok(sp->IBase, strings[MSG_OPERATIONAL_ERROR], info);
  2224.                             if (info) deallocate(info, V_cstr);
  2225.                         }
  2226.                     } else if (sp->error_messages)
  2227.                         inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_READING_PWD], nil, 0);
  2228.                 } else if (sp->error_messages)
  2229.                     inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_REQUESTING_PWD], nil, 0);
  2230.             } else if (sp->error_messages)
  2231.                 inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_READING_TYPE], nil, 0);
  2232.         } else if (sp->error_messages)
  2233.             inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_REQUESTING_PWD], nil, 0);
  2234.     } else if (sp->error_messages)
  2235.         inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_SETTING_TYPE], nil, 0);
  2236.     
  2237.     return false;
  2238. }
  2239.  
  2240. void login(site *sp, struct Window *w)
  2241. /*
  2242.  * goes through the login sequence once a successful connection has been established
  2243.  * Inputs:
  2244.  *    sp    : site pointer
  2245.  *    w    : the connection cancel window
  2246.  */
  2247. {
  2248.     tcpmessage *tm;
  2249.     struct MsgPort *sync;
  2250.     b8 reply[4], *info;
  2251.     b32 csig;
  2252.     boolean early_success = false;
  2253.     
  2254.     tm = sp->control;
  2255.     sync = sp->sync;
  2256.     
  2257.     state_change(sp, SS_LOGIN);
  2258.     
  2259.     csig = 1 << w->UserPort->mp_SigBit;
  2260.     
  2261. retry_login:
  2262.     if (sp->needs_user || sp->needs_password) {
  2263.         if (!sp->error_messages || !user_pass_request(sp, w)) {
  2264.             tm->command = TCP_CLOSE;
  2265.             PutMsg(tcp, &tm->header);
  2266.             WaitPort(sync); GetMsg(sync);
  2267.     
  2268.             close_req(sp, w);
  2269.     
  2270.             state_change(sp, SS_DISCONNECTED);
  2271.             return;
  2272.         }
  2273.     }
  2274.     
  2275.     if (sp->user) {
  2276.         sprintf(sp->read_buffer, "USER %s\r\n", sp->user);
  2277.     } else {
  2278.         strcpy(sp->read_buffer, "USER ftp\r\n");
  2279.     }
  2280.     
  2281.     if (control_write(sp, sp->read_buffer, csig) == NO_ERROR) {
  2282.         if (sp->password) {
  2283.             sprintf(sp->read_buffer, "PASS %s\r\n", sp->password);
  2284.         } else {
  2285.             sprintf(sp->read_buffer, "PASS %s\r\n", anon_login);
  2286.         }
  2287.         if (control_write(sp, sp->read_buffer, csig) == NO_ERROR) {
  2288.             /* first response should be to the USER */
  2289.             
  2290.             switch (response(sp, csig, &info, reply)) {
  2291.             case NO_ERROR:
  2292.                 switch (reply[0]) {
  2293.                 case '2':
  2294.                     early_success = true;
  2295.                     /* the welcome banner will come here I guess */
  2296.                     if (sp->all_messages && !sp->read_banners) {
  2297.                         ok(sp->IBase, strings[MSG_LOGIN_SUCCEEDED_NO_PASS], info);
  2298.                         sp->read_banners = true;
  2299.                     }
  2300.                     /* fall through */
  2301.                 case '3':
  2302.                     /* ignore the banner here ... usually its just "Anonymous login ok, send ident ..." */
  2303.                     if (info) deallocate(info, V_cstr);
  2304.         
  2305.                     /* now read pass response */
  2306.                     switch (response(sp, csig, &info, reply)) {
  2307.                     case NO_ERROR:
  2308.                         /* if we succeeded early, we don't care what they tell us */
  2309.                         if (!early_success) {
  2310.                             if (reply[0] == '2') {
  2311.                                 if (sp->all_messages && !sp->read_banners) {
  2312.                                     ok(sp->IBase, strings[MSG_LOGIN_SUCCEEDED], info);
  2313.                                     sp->read_banners = true;
  2314.                                 }
  2315.                             } else if (reply[0] == '3') {
  2316.                                 /* they want an ACCT ... fuck 'em */
  2317.  
  2318.                                 if (sp->error_messages)
  2319.                                     inform(sp->IBase, strings[MSG_LOGIN_FAILED], strings[MSG_ACCT_REQUESTED], nil, 0);
  2320.                                 if (info) deallocate(info, V_cstr);
  2321.                                 break;
  2322.                             } else {
  2323.                                 if (reply[0] == '5' && reply[1] == '3' && reply[2] == '0') {
  2324.                                     /* this is login incorrect */
  2325.                                     if (sp->error_messages && retry_cancel(sp->IBase, strings[MSG_LOGIN_INCORRECT], info)) {
  2326.                                         if (info) deallocate(info, V_cstr);
  2327.                                         sp->needs_password = true;
  2328.                                         if (sp->password) deallocate(sp->password, V_cstr);
  2329.                                         sp->password = nil;
  2330.                                         goto retry_login;
  2331.                                     }
  2332.                                     if (info) deallocate(info, V_cstr);
  2333.                                     break;
  2334.                                 }
  2335.                                 
  2336.                                 if (sp->error_messages)
  2337.                                     ok(sp->IBase, strings[MSG_LOGIN_FAILED_PASS], info);
  2338.                                 if (info) deallocate(info, V_cstr);
  2339.                                 break;
  2340.                             }
  2341.                         }
  2342.                         
  2343.                         if (info) deallocate(info, V_cstr);
  2344.                         
  2345.                         if (prelim(sp, w)) {
  2346.                             close_req(sp, w);
  2347.                             sp->connected = true;
  2348.             
  2349.                             state_change(sp, SS_IDLE);
  2350.                             return;
  2351.                         }
  2352.                         
  2353.                         break;
  2354.                     case ERROR_INTERRUPTED:
  2355.                         break;
  2356.                     case ERROR_LOST_CONNECTION:
  2357.                     case ERROR_EOF:
  2358.                     case ERROR_UNREACHABLE:
  2359.                         if (sp->error_messages)
  2360.                             inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_LOST_CONN_DURING_LOGIN_PASS], nil, 0);
  2361.                         break;
  2362.                     case ERROR_GARBAGE_RECEIVED:
  2363.                         if (sp->error_messages)
  2364.                             inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_GARBAGE_RECEIVED_PASS], nil, 0);
  2365.                         break;
  2366.                     default:
  2367.                         if (sp->error_messages)
  2368.                             inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_ERROR_RESPONSE_PASS], nil, 0);
  2369.                         break;
  2370.                     }
  2371.                     break;
  2372.                 case '4':
  2373.                     if (sp->error_messages && retry_cancel(sp->IBase, strings[MSG_TEMP_LOGIN_FAILURE_USER], info)) {
  2374.                         if (info) deallocate(info, V_cstr);
  2375.                         goto retry_login;
  2376.                     }
  2377.                     if (info) deallocate(info, V_cstr);
  2378.                     break;
  2379.                 default:
  2380.                     if (sp->error_messages)
  2381.                         ok(sp->IBase, strings[MSG_LOGIN_FAILED_USER], info);
  2382.                     if (info) deallocate(info, V_cstr);
  2383.                     break;
  2384.                 }
  2385.                 break;
  2386.             case ERROR_INTERRUPTED:
  2387.                 break;
  2388.             case ERROR_LOST_CONNECTION:
  2389.             case ERROR_EOF:
  2390.             case ERROR_UNREACHABLE:
  2391.                 if (sp->error_messages)
  2392.                     inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_LOST_CONN_DURING_LOGIN], nil, 0);
  2393.                 break;
  2394.             case ERROR_GARBAGE_RECEIVED:
  2395.                 if (sp->error_messages)
  2396.                     inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_GARBAGE_RECEIVED_USER], nil, 0);
  2397.                 break;
  2398.             default:
  2399.                 if (sp->error_messages)
  2400.                     inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_ERROR_USER_RESPONSE], nil, 0);
  2401.                 break;
  2402.             }
  2403.         } else if (sp->error_messages)
  2404.             inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_ERROR_WRITING_PASS], nil, 0);
  2405.     } else if (sp->error_messages)
  2406.         inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_ERROR_WRITING_USER], nil, 0);
  2407.     
  2408.     tm->command = TCP_CLOSE;
  2409.     PutMsg(tcp, &tm->header);
  2410.     WaitPort(sync); GetMsg(sync);
  2411.     
  2412.     close_req(sp, w);
  2413.     
  2414.     state_change(sp, SS_DISCONNECTED);
  2415.     
  2416.     return;
  2417. }
  2418.  
  2419. void init_connect(site *sp)
  2420. {
  2421.     struct Window *w;
  2422.     b8 *z;
  2423.     tcpmessage *tm, *intr;
  2424.     struct MsgPort *sync;
  2425.     b8 reply[3], *info;
  2426.     b32 signals, csig;
  2427.     
  2428.     verify(sp, V_site);
  2429.  
  2430.     z = sp->host;
  2431.     
  2432.     while (sp->infos) free_info_header(sp->infos);
  2433.     
  2434.     w = connect_req(sp, z);
  2435.     if (!w) {
  2436.         show_string("connect req failed");
  2437.         return;
  2438.     }
  2439.     
  2440.     state_change(sp, SS_CONNECTING);
  2441.     
  2442.     tm = sp->control;
  2443.     sync = sp->sync;
  2444.     intr = sp->intr;
  2445.     
  2446.     csig = (1 << w->UserPort->mp_SigBit) | sp->abort_signals | sp->disconnect_signals;
  2447.     signals = (1 << sync->mp_SigBit) | csig;
  2448.  
  2449.     if (sp->port_number == 0) {
  2450.         tm->command = TCP_SERVICE;
  2451.         tm->data = strings[MSG_SERVICE];
  2452.         tm->header.mn_ReplyPort = sync;
  2453.         
  2454.         PutMsg(tcp, &tm->header);
  2455.         WaitPort(sync); GetMsg(sync);
  2456.         
  2457.         if (tm->result) {
  2458.             sp->port_number = ftp_port_number = tm->port.w;
  2459.         } else if (tm->error == ERROR_NO_CONNECTION) {
  2460.             close_req(sp, w);
  2461.             
  2462.             if (sp->error_messages)
  2463.                 inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_AMITCP_NOT_RUNNING], nil, 0);
  2464.             
  2465.             state_change(sp, SS_DISCONNECTED);
  2466.             
  2467.             return;
  2468.         } else {
  2469.             sp->port_number = 21;
  2470.         }
  2471.     }
  2472.     
  2473.     tm->command = TCP_CONNECT;
  2474.     tm->header.mn_ReplyPort = sync;
  2475.     tm->data = z;
  2476.     tm->port.w = sp->port_number;
  2477.     
  2478.     PutMsg(tcp, &tm->header);
  2479.  
  2480.     do {
  2481.         if (Wait(signals) & csig) {
  2482.             intr->interrupt = tm;
  2483.             PutMsg(tcp, &intr->header);
  2484.             WaitPort(sync); GetMsg(sync);
  2485.             WaitPort(sync); GetMsg(sync);
  2486.         
  2487.             if (tm->result) {    /* it succeeded in connecting */
  2488.                 tm->command = TCP_CLOSE;
  2489.                 PutMsg(tcp, &tm->header);
  2490.                 WaitPort(sync); GetMsg(sync);
  2491.             }
  2492.         
  2493.             close_req(sp, w);
  2494.             
  2495.             state_change(sp, SS_DISCONNECTED);
  2496.         
  2497.             return;
  2498.         }
  2499.     } while (!GetMsg(sync));
  2500.     
  2501.     if (!tm->result) {    /* the connect failed ... tell the user why */
  2502.         close_req(sp, w);
  2503.         
  2504.         switch (tm->error) {
  2505.         case ERROR_NO_CONNECTION:
  2506.             if (sp->error_messages)
  2507.                 inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_AMITCP_NOT_RUNNING], nil, 0);
  2508.             break;
  2509.         case ERROR_UNKNOWN_HOST:
  2510.             if (sp->error_messages)
  2511.                 inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_HOST_UNKNOWN], z, 0);
  2512.             break;
  2513.         case ERROR_UNREACHABLE:
  2514.             if (sp->error_messages)
  2515.                 inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_HOST_UNREACHABLE], z, 0);
  2516.             break;
  2517.         case ERROR_CONNECT_REFUSED:
  2518.             if (sp->error_messages)
  2519.                 inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_FTP_REFUSED], z, 0);
  2520.             break;
  2521.         default:
  2522.             if (sp->error_messages)
  2523.                 inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_CANT_CONNECT], z, tm->error);
  2524.             break;
  2525.         }
  2526.         
  2527.         state_change(sp, SS_DISCONNECTED);
  2528.         
  2529.         return;
  2530.     }
  2531.     
  2532.     /* ok, we've connected ... look at the greeting */
  2533.     
  2534. retry_intro:
  2535.     switch (response(sp, csig, &info, reply)) {
  2536.     case NO_ERROR:
  2537.         break;
  2538.     case ERROR_INTERRUPTED:
  2539.         close_req(sp, w);
  2540.         goto close_and_exit;
  2541.     case ERROR_LOST_CONNECTION:
  2542.     case ERROR_EOF:
  2543.     case ERROR_UNREACHABLE:
  2544.         close_req(sp, w);
  2545.         if (sp->error_messages)
  2546.             inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_LOST_CONN_DURING_INTRO], nil, 0);
  2547.         goto close_and_exit;
  2548.     case ERROR_GARBAGE_RECEIVED:
  2549.         close_req(sp, w);
  2550.         if (sp->error_messages)
  2551.             inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_GARBAGE_DURING_INTRO], z, 0);
  2552.         goto close_and_exit;
  2553.     default:
  2554.         close_req(sp, w);
  2555.         if (sp->error_messages)
  2556.             inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_ERROR_DURING_INTRO], nil, 0);
  2557.         goto close_and_exit;
  2558.     }
  2559.     
  2560.     switch (reply[0]) {
  2561.     case '1':
  2562.         if (sp->error_messages && retry_cancel(sp->IBase, strings[MSG_CONN_DELAY], info)) {
  2563.             goto retry_intro;
  2564.         }
  2565.         close_req(sp, w);
  2566.         if (info)
  2567.             deallocate(info, V_cstr);
  2568.         
  2569.         goto close_and_exit;
  2570.     case '2':
  2571.     case '3':
  2572.         /* This banner appears to be generally pretty dull, but if
  2573.          * you really want to see it then remove the comments ...
  2574.          * if (!sp->read_banners) {
  2575.          *     ok(sp->IBase, "Connected", info);
  2576.          * }
  2577.          */
  2578.         
  2579.         if (info)
  2580.             deallocate(info, V_cstr);
  2581.         
  2582.         login(sp, w);
  2583.         return;
  2584.     case '4':
  2585.         if (retry_cancel(sp->IBase, strings[MSG_TEMP_CONN_FAILURE], info)) {
  2586.             goto retry_intro;
  2587.         }
  2588.         close_req(sp, w);
  2589.         if (info)
  2590.             deallocate(info, V_cstr);
  2591.         
  2592.         goto close_and_exit;
  2593.     case '5':
  2594.     default:
  2595.         close_req(sp, w);
  2596.         
  2597.         if (sp->error_messages)
  2598.             ok(sp->IBase, strings[MSG_CONN_FAILED], info);
  2599.  
  2600.         if (info)
  2601.             deallocate(info, V_cstr);
  2602.         
  2603.         break;
  2604.     }
  2605.     
  2606. close_and_exit:
  2607.     tm->command = TCP_CLOSE;
  2608.     PutMsg(tcp, &tm->header);
  2609.     WaitPort(sync); GetMsg(sync);
  2610.     
  2611.     state_change(sp, SS_DISCONNECTED);
  2612.     
  2613.     return;
  2614. }
  2615.  
  2616.  
  2617.