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