home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Misc / FTPMOUNT.LZX / FTPMount-0.7 / Source / connect.c next >
Encoding:
C/C++ Source or Header  |  1995-09-06  |  51.2 KB  |  2,447 lines

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