home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / www / src / WWW / Library / Implementation / HTFTP.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-28  |  25.7 KB  |  926 lines

  1. /*            File Transfer Protocol (FTP) Client
  2. **            for a WorldWideWeb browser
  3. **            ===================================
  4. **
  5. **    A cache of control connections is kept.
  6. **
  7. ** Note: Port allocation
  8. **
  9. **    It is essential that the port is allocated by the system, rather
  10. **    than chosen in rotation by us (POLL_PORTS), or the following
  11. **    problem occurs.
  12. **
  13. **    It seems that an attempt by the server to connect to a port which has
  14. **    been used recently by a listen on the same socket, or by another
  15. **    socket this or another process causes a hangup of (almost exactly)
  16. **    one minute. Therefore, we have to use a rotating port number.
  17. **    The problem remains that if the application is run twice in quick
  18. **    succession, it will hang for what remains of a minute.
  19. **
  20. ** Authors
  21. **    TBL    Tim Berners-lee <timbl@info.cern.ch>
  22. **    DD    Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
  23. ** History:
  24. **     2 May 91    Written TBL, as a part of the WorldWideWeb project.
  25. **    15 Jan 92    Bug fix: close() was used for NETCLOSE for control soc
  26. **    10 Feb 92    Retry if cached connection times out or breaks
  27. **     8 Dec 92    Bug fix 921208 TBL after DD
  28. **    17 Dec 92    Anon FTP password now just WWWuser@ suggested by DD
  29. **            fails on princeton.edu!
  30. **
  31. ** Options:
  32. **    LISTEN        We listen, the other guy connects for data.
  33. **            Otherwise, other way round, but problem finding our
  34. **            internet address!
  35. **
  36. ** Bugs:
  37. **    No binary mode! Always uses ASCII! 
  38. */
  39.  
  40. #define LISTEN        /* @@@@ Test */
  41.  
  42. /*
  43. BUGS:    @@@      Limit connection cache size!
  44.         Error reporting to user.
  45.         400 & 500 errors are acked by user with windows.
  46.         Use configuration file for user names
  47.         Prompt user for password
  48.         
  49. **        Note for portablility this version does not use select() and
  50. **        so does not watch the control and data channels at the
  51. **        same time.
  52. */        
  53.  
  54. #include "HTFTP.h"    /* Implemented here */
  55.  
  56. #define CR   FROMASCII('\015')    /* Must be converted to ^M for transmission */
  57. #define LF   FROMASCII('\012')    /* Must be converted to ^J for transmission */
  58.  
  59. #define REPEAT_PORT    /* Give the port number for each file */
  60. #define REPEAT_LISTEN    /* Close each listen socket and open a new one */
  61.  
  62. /* define POLL_PORTS         If allocation does not work, poll ourselves.*/
  63. #define LISTEN_BACKLOG 2    /* Number of pending connect requests (TCP)*/
  64.  
  65. #define FIRST_TCP_PORT    1024    /* Region to try for a listening port */
  66. #define LAST_TCP_PORT    5999    
  67.  
  68. #define LINE_LENGTH 256
  69. #define COMMAND_LENGTH 256
  70.  
  71. #include "HTParse.h"
  72. #include "HTUtils.h"
  73. #include "tcp.h"
  74. #include "HTTCP.h"
  75. #include "HTAnchor.h"
  76. #include "HTFile.h"    /* For HTFileFormat() */
  77. #include "HTBTree.h"
  78. #include "HTChunk.h"
  79. #ifndef IPPORT_FTP
  80. #define IPPORT_FTP    21
  81. #endif
  82.  
  83. #ifdef REMOVED_CODE
  84. extern char *malloc();
  85. extern void free();
  86. extern char *strncpy();
  87. #endif
  88.  
  89. typedef struct _connection {
  90.     struct _connection *    next;    /* Link on list     */
  91.     u_long            addr;    /* IP address        */
  92.     int                socket;    /* Socket number for communication */
  93.     BOOL            binary; /* Binary mode? */
  94. } connection;
  95.  
  96. #ifndef NIL
  97. #define NIL 0
  98. #endif
  99.  
  100. /*        Hypertext object building machinery
  101. */
  102. #include "HTML.h"
  103.  
  104. #define PUTC(c) (*targetClass.put_character)(target, c)
  105. #define PUTS(s) (*targetClass.put_string)(target, s)
  106. #define START(e) (*targetClass.start_element)(target, e, 0, 0)
  107. #define END(e) (*targetClass.end_element)(target, e)
  108. #define END_TARGET (*targetClass.end_document)(target)
  109. #define FREE_TARGET (*targetClass.free)(target)
  110. struct _HTStructured {
  111.     CONST HTStructuredClass *    isa;
  112.     /* ... */
  113. };
  114.  
  115.  
  116. /*    Module-Wide Variables
  117. **    ---------------------
  118. */
  119. PRIVATE connection * connections =0;    /* Linked list of connections */
  120. PRIVATE char    response_text[LINE_LENGTH+1];/* Last response from NewsHost */
  121. PRIVATE connection * control;        /* Current connection */
  122. PRIVATE int    data_soc = -1;        /* Socket for data transfer =invalid */
  123.  
  124. #ifdef POLL_PORTS
  125. PRIVATE    unsigned short    port_number = FIRST_TCP_PORT;
  126. #endif
  127.  
  128. #ifdef LISTEN
  129. PRIVATE int     master_socket = -1;    /* Listening socket = invalid    */
  130. PRIVATE char    port_command[255];    /* Command for setting the port */
  131. PRIVATE fd_set    open_sockets;         /* Mask of active channels */
  132. PRIVATE int    num_sockets;          /* Number of sockets to scan */
  133. #else
  134. PRIVATE    unsigned short    passive_port;    /* Port server specified for data */
  135. #endif
  136.  
  137.  
  138. #define NEXT_CHAR HTGetChararcter()    /* Use function in HTFormat.c */
  139.  
  140. #define DATA_BUFFER_SIZE 2048
  141. PRIVATE char data_buffer[DATA_BUFFER_SIZE];        /* Input data buffer */
  142. PRIVATE char * data_read_pointer;
  143. PRIVATE char * data_write_pointer;
  144. #define NEXT_DATA_CHAR next_data_char()
  145.  
  146.  
  147. /*    Procedure: Read a character from the data connection
  148. **    ----------------------------------------------------
  149. */
  150. PRIVATE char next_data_char
  151. NOARGS
  152. {
  153.     int status;
  154.     if (data_read_pointer >= data_write_pointer) {
  155.     status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
  156.     /* Get some more data */
  157.     if (status <= 0) return (char)-1;
  158.     data_write_pointer = data_buffer + status;
  159.     data_read_pointer = data_buffer;
  160.     }
  161. #ifdef NOT_ASCII
  162.     {
  163.         char c = *data_read_pointer++;
  164.     return FROMASCII(c);
  165.     }
  166. #else
  167.     return *data_read_pointer++;
  168. #endif
  169. }
  170.  
  171.  
  172. /*    Close an individual connection
  173. **
  174. */
  175. #ifdef __STDC__
  176. PRIVATE int close_connection(connection * con)
  177. #else
  178. PRIVATE int close_connection(con)
  179.     connection *con;
  180. #endif
  181. {
  182.     connection * scan;
  183.     int status = NETCLOSE(con->socket);
  184.     if (TRACE) fprintf(stderr, "FTP: Closing control socket %d\n", con->socket);
  185.     if (connections==con) {
  186.         connections = con->next;
  187.     return status;
  188.     }
  189.     for(scan=connections; scan; scan=scan->next) {
  190.         if (scan->next == con) {
  191.         scan->next = con->next;    /* Unlink */
  192.         if (control==con) control = (connection*)0;
  193.         return status;
  194.     } /*if */
  195.     } /* for */
  196.     return -1;        /* very strange -- was not on list. */
  197. }
  198.  
  199.  
  200. /*    Execute Command and get Response
  201. **    --------------------------------
  202. **
  203. **    See the state machine illustrated in RFC959, p57. This implements
  204. **    one command/reply sequence.  It also interprets lines which are to
  205. **    be continued, which are marked with a "-" immediately after the
  206. **    status code.
  207. **
  208. **    Continuation then goes on until a line with a matching reply code
  209. **    an a space after it.
  210. **
  211. ** On entry,
  212. **    con    points to the connection which is established.
  213. **    cmd    points to a command, or is NIL to just get the response.
  214. **
  215. **    The command is terminated with the CRLF pair.
  216. **
  217. ** On exit,
  218. **    returns:  The first digit of the reply type,
  219. **          or negative for communication failure.
  220. */
  221. #ifdef __STDC__
  222. PRIVATE int response(char * cmd)
  223. #else
  224. PRIVATE int response(cmd)
  225.     char * cmd;
  226. #endif
  227. {
  228.     int result;                /* Three-digit decimal code */
  229.     int    continuation_response = -1;
  230.     int status;
  231.     
  232.     if (!control) {
  233.           if(TRACE) fprintf(stderr, "FTP: No control connection set up!!\n");
  234.       return -99;
  235.     }
  236.     
  237.     if (cmd) {
  238.     
  239.     if (TRACE) fprintf(stderr, "  Tx: %s", cmd);
  240.  
  241. #ifdef NOT_ASCII
  242.     {
  243.         char * p;
  244.         for(p=cmd; *p; p++) {
  245.             *p = TOASCII(*p);
  246.         }
  247.     }
  248. #endif 
  249.     status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
  250.     if (status<0) {
  251.         if (TRACE) fprintf(stderr, 
  252.             "FTP: Error %d sending command: closing socket %d\n",
  253.         status, control->socket);
  254.         close_connection(control);
  255.         return status;
  256.     }
  257.     }
  258.  
  259.     do {
  260.     char *p = response_text;
  261.     for(;;) {  
  262.         if (((*p++=NEXT_CHAR) == LF)
  263.             || (p == &response_text[LINE_LENGTH])) {
  264.         char continuation;
  265.         *p++=0;            /* Terminate the string */
  266.         if (TRACE) fprintf(stderr, "    Rx: %s", response_text);
  267.         sscanf(response_text, "%d%c", &result, &continuation);
  268.         if  (continuation_response == -1) {
  269.             if (continuation == '-')  /* start continuation */
  270.                 continuation_response = result;
  271.         } else {     /* continuing */
  272.             if (continuation_response == result
  273.                 && continuation == ' ')
  274.                 continuation_response = -1;    /* ended */
  275.         }    
  276.         break;        
  277.         } /* if end of line */
  278.         
  279.         if (*(p-1) < 0) {
  280.         if(TRACE) fprintf(stderr, "Error on rx: closing socket %d\n",
  281.             control->socket);
  282.         strcpy(response_text, "000 *** TCP read error on response\n");
  283.             close_connection(control);
  284.             return -1;    /* End of file on response */
  285.         }
  286.     } /* Loop over characters */
  287.  
  288.     } while (continuation_response != -1);
  289.     
  290.     if (result==421) {
  291.     if(TRACE) fprintf(stderr, "FTP: They close so we close socket %d\n",
  292.         control->socket);
  293.     close_connection(control);
  294.     return -1;
  295.     }
  296.     return result/100;
  297. }
  298.  
  299.  
  300. /*    Get a valid connection to the host
  301. **    ----------------------------------
  302. **
  303. ** On entry,
  304. **    arg    points to the name of the host in a hypertext address
  305. ** On exit,
  306. **    returns    <0 if error
  307. **        socket number if success
  308. **
  309. **    This routine takes care of managing timed-out connections, and
  310. **    limiting the number of connections in use at any one time.
  311. **
  312. **    It ensures that all connections are logged in if they exist.
  313. **    It ensures they have the port number transferred.
  314. */
  315. PRIVATE int get_connection ARGS1 (CONST char *,arg)
  316. {
  317.     struct sockaddr_in soc_address;    /* Binary network address */
  318.     struct sockaddr_in* sin = &soc_address;
  319.  
  320.     char * username=0;
  321.     char * password=0;
  322.     
  323.     if (!arg) return -1;        /* Bad if no name sepcified    */
  324.     if (!*arg) return -1;        /* Bad if name had zero length    */
  325.  
  326. /*  Set up defaults:
  327. */
  328.     sin->sin_family = AF_INET;                /* Family, host order  */
  329.     sin->sin_port = htons(IPPORT_FTP);            /* Well Known Number    */
  330.  
  331.     if (TRACE) fprintf(stderr, "FTP: Looking for %s\n", arg);
  332.  
  333. /* Get node name:
  334. */
  335.     {
  336.     char *p1 = HTParse(arg, "", PARSE_HOST);
  337.     char *p2 = strrchr(p1, '@');    /* user? */
  338.     char * pw;
  339.     if (p2) {
  340.         username = p1;
  341.         *p2=0;            /* terminate */
  342.         p1 = p2+1;            /* point to host */
  343.         pw = strchr(username, ':');
  344.         if (pw) {
  345.             *pw++ = 0;
  346.         password = pw;
  347.         }
  348.     }
  349.     if (HTParseInet(sin, p1)) { free(p1); return -1;} /* TBL 920622 */
  350.  
  351.         if (!username) free(p1);
  352.     } /* scope of p1 */
  353.  
  354.         
  355. /*    Now we check whether we already have a connection to that port.
  356. */
  357.  
  358.     {
  359.     connection * scan;
  360.     for (scan=connections; scan; scan=scan->next) {
  361.         if (sin->sin_addr.s_addr == scan->addr) {
  362.           if (TRACE) fprintf(stderr, 
  363.         "FTP: Already have connection for %d.%d.%d.%d.\n",
  364.             (int)*((unsigned char *)(&scan->addr)+0),
  365.             (int)*((unsigned char *)(&scan->addr)+1),
  366.             (int)*((unsigned char *)(&scan->addr)+2),
  367.             (int)*((unsigned char *)(&scan->addr)+3));
  368.         if (username) free(username);
  369.         return scan->socket;        /* Good return */
  370.         } else {
  371.           if (TRACE) fprintf(stderr, 
  372.         "FTP: Existing connection is %d.%d.%d.%d\n",
  373.             (int)*((unsigned char *)(&scan->addr)+0),
  374.             (int)*((unsigned char *)(&scan->addr)+1),
  375.             (int)*((unsigned char *)(&scan->addr)+2),
  376.             (int)*((unsigned char *)(&scan->addr)+3));
  377.         }
  378.     }
  379.     }
  380.  
  381.    
  382. /*    Now, let's get a socket set up from the server:
  383. */      
  384.     {
  385.         int status;
  386.     connection * con = (connection *)malloc(sizeof(*con));
  387.     if (con == NULL) outofmem(__FILE__, "get_connection");
  388.     con->addr = sin->sin_addr.s_addr;    /* save it */
  389.     con->binary = NO;
  390.     status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  391.     if (status<0) {
  392.         (void) HTInetStatus("socket");
  393.         free(con);
  394.         if (username) free(username);
  395.         return status;
  396.     }
  397.     con->socket = status;
  398.  
  399.         status = connect(con->socket, (struct sockaddr*)&soc_address,
  400.      sizeof(soc_address));
  401.         if (status<0){
  402.         (void) HTInetStatus("connect");
  403.         if (TRACE) fprintf(stderr, 
  404.             "FTP: Unable to connect to remote host for `%s'.\n",
  405.             arg);
  406.         NETCLOSE(con->socket);
  407.         free(con);
  408.         if (username) free(username);
  409.         return status;            /* Bad return */
  410.     }
  411.     
  412.     if (TRACE) fprintf(stderr, "FTP connected, socket %d\n", con->socket);
  413.     control = con;            /* Current control connection */
  414.     con->next = connections;    /* Link onto list of good ones */
  415.     connections = con;
  416.         HTInitInput(con->socket);/* Initialise buffering for contron connection */
  417.  
  418.  
  419. /*    Now we log in        Look up username, prompt for pw.
  420. */
  421.     {
  422.         int status = response(NIL);    /* Get greeting */
  423.         
  424.         if (status == 2) {        /* Send username */
  425.             char * command;
  426.         if (username) {
  427.             command = (char*)malloc(10+strlen(username)+2+1);
  428.             if (command == NULL) outofmem(__FILE__, "get_connection");
  429.             sprintf(command, "USER %s%c%c", username, CR, LF);
  430.         } else {
  431.             command = (char*)malloc(25);
  432.             if (command == NULL) outofmem(__FILE__, "get_connection");
  433.             sprintf(command, "USER anonymous%c%c", CR, LF);
  434.             }
  435.         status = response(command);
  436.         free(command);
  437.         }
  438.         if (status == 3) {        /* Send password */
  439.             char * command;
  440.         if (password) {
  441.             command = (char*)malloc(10+strlen(password)+2+1);
  442.             if (command == NULL) outofmem(__FILE__, "get_connection");
  443.             sprintf(command, "PASS %s%c%c", password, CR, LF);
  444.         } else {
  445.             char * user = getenv("USER");
  446.             CONST char *host = HTHostName();
  447.             if (!user) user = "WWWuser";
  448.             /* If not fully qualified, suppress it as ftp.uu.net
  449.                prefers a blank to a bad name */
  450.             if (!strchr(host, '.')) host = "";
  451.  
  452.             command = (char*)malloc(20+strlen(host)+2+1);
  453.             if (command == NULL) outofmem(__FILE__, "get_connection");
  454.             sprintf(command,
  455.             "PASS %s@%s%c%c", user ? user : "WWWuser",
  456.             host, CR, LF); /*@@*/
  457.             }
  458.         status = response(command);
  459.         free(command);
  460.         }
  461.             if (username) free(username);
  462.  
  463.         if (status == 3) {
  464.             char temp[80];
  465.         sprintf(temp, "ACCT noaccount%c%c", CR, LF);
  466.         status = response(temp);
  467.         }
  468.         if (status !=2) {
  469.             if (TRACE) fprintf(stderr, "FTP: Login fail: %s", response_text);
  470.             if (control) close_connection(control);
  471.             return -1;        /* Bad return */
  472.         }
  473.         if (TRACE) fprintf(stderr, "FTP: Logged in.\n");
  474.     }
  475.  
  476. /*    Now we inform the server of the port number we will listen on
  477. */
  478. #ifndef REPEAT_PORT
  479.     {
  480.         int status = response(port_command);
  481.         if (status !=2) {
  482.             if (control) close_connection(control);
  483.             return -status;        /* Bad return */
  484.         }
  485.         if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
  486.     }
  487. #endif
  488.     return con->socket;            /* Good return */
  489.     } /* Scope of con */
  490. }
  491.  
  492.  
  493. #ifdef LISTEN
  494.  
  495. /*    Close Master (listening) socket
  496. **    -------------------------------
  497. **
  498. **
  499. */
  500. #ifdef __STDC__
  501. PRIVATE int close_master_socket(void)
  502. #else
  503. PRIVATE int close_master_socket()
  504. #endif
  505. {
  506.     int status;
  507.     FD_CLR(master_socket, &open_sockets);
  508.     status = NETCLOSE(master_socket);
  509.     if (TRACE) fprintf(stderr, "FTP: Closed master socket %d\n", master_socket);
  510.     master_socket = -1;
  511.     if (status<0) return HTInetStatus("close master socket");
  512.     else return status;
  513. }
  514.  
  515.  
  516. /*    Open a master socket for listening on
  517. **    -------------------------------------
  518. **
  519. **    When data is transferred, we open a port, and wait for the server to
  520. **    connect with the data.
  521. **
  522. ** On entry,
  523. **    master_socket    Must be negative if not set up already.
  524. ** On exit,
  525. **    Returns        socket number if good
  526. **            less than zero if error.
  527. **    master_socket    is socket number if good, else negative.
  528. **    port_number    is valid if good.
  529. */
  530. #ifdef __STDC__
  531. PRIVATE int get_listen_socket(void)
  532. #else
  533. PRIVATE int get_listen_socket()
  534. #endif
  535. {
  536.     struct sockaddr_in soc_address;    /* Binary network address */
  537.     struct sockaddr_in* sin = &soc_address;
  538.     int new_socket;            /* Will be master_socket */
  539.     
  540.     
  541.     FD_ZERO(&open_sockets);    /* Clear our record of open sockets */
  542.     num_sockets = 0;
  543.     
  544. #ifndef REPEAT_LISTEN
  545.     if (master_socket>=0) return master_socket;  /* Done already */
  546. #endif
  547.  
  548. /*  Create internet socket
  549. */
  550.     new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  551.     
  552.     if (new_socket<0)
  553.     return HTInetStatus("socket for master socket");
  554.     
  555.     if (TRACE) fprintf(stderr, "FTP: Opened master socket number %d\n", new_socket);
  556.     
  557. /*  Search for a free port.
  558. */
  559.     sin->sin_family = AF_INET;        /* Family = internet, host order  */
  560.     sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
  561. #ifdef POLL_PORTS
  562.     {
  563.         unsigned short old_port_number = port_number;
  564.     for(port_number=old_port_number+1;;port_number++){ 
  565.         int status;
  566.         if (port_number > LAST_TCP_PORT)
  567.         port_number = FIRST_TCP_PORT;
  568.         if (port_number == old_port_number) {
  569.         return HTInetStatus("bind");
  570.         }
  571.         soc_address.sin_port = htons(port_number);
  572.         if ((status=bind(new_socket,
  573.             (struct sockaddr*)&soc_address,
  574.                 /* Cast to generic sockaddr */
  575.             sizeof(soc_address))) == 0)
  576.         break;
  577.         if (TRACE) fprintf(stderr, 
  578.             "TCP bind attempt to port %d yields %d, errno=%d\n",
  579.         port_number, status, errno);
  580.     } /* for */
  581.     }
  582. #else
  583.     {
  584.         int status;
  585.     int address_length = sizeof(soc_address);
  586.     status = getsockname(control->socket,
  587.             (struct sockaddr *)&soc_address,
  588.              &address_length);
  589.     if (status<0) return HTInetStatus("getsockname");
  590.     CTRACE(tfp, "FTP: This host is %s\n",
  591.         HTInetString(sin));
  592.     
  593.     soc_address.sin_port = 0;    /* Unspecified: please allocate */
  594.     status=bind(new_socket,
  595.         (struct sockaddr*)&soc_address,
  596.             /* Cast to generic sockaddr */
  597.         sizeof(soc_address));
  598.     if (status<0) return HTInetStatus("bind");
  599.     
  600.     address_length = sizeof(soc_address);
  601.     status = getsockname(new_socket,
  602.             (struct sockaddr*)&soc_address,
  603.             &address_length);
  604.     if (status<0) return HTInetStatus("getsockname");
  605.     }
  606. #endif    
  607.  
  608.     CTRACE(tfp, "FTP: bound to port %d on %s\n",
  609.             (int)ntohs(sin->sin_port),
  610.         HTInetString(sin));
  611.  
  612. #ifdef REPEAT_LISTEN
  613.     if (master_socket>=0)
  614.         (void) close_master_socket();
  615. #endif    
  616.     
  617.     master_socket = new_socket;
  618.     
  619. /*    Now we must find out who we are to tell the other guy
  620. */
  621.     (void)HTHostName();     /* Make address valid - doesn't work*/
  622.     sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
  623.             (int)*((unsigned char *)(&sin->sin_addr)+0),
  624.             (int)*((unsigned char *)(&sin->sin_addr)+1),
  625.             (int)*((unsigned char *)(&sin->sin_addr)+2),
  626.             (int)*((unsigned char *)(&sin->sin_addr)+3),
  627.             (int)*((unsigned char *)(&sin->sin_port)+0),
  628.             (int)*((unsigned char *)(&sin->sin_port)+1),
  629.             CR, LF);
  630.  
  631.  
  632. /*    Inform TCP that we will accept connections
  633. */
  634.     if (listen(master_socket, 1)<0) {
  635.     master_socket = -1;
  636.     return HTInetStatus("listen");
  637.     }
  638.     CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n");
  639.     FD_SET(master_socket, &open_sockets);
  640.     if ((master_socket+1) > num_sockets) num_sockets=master_socket+1;
  641.  
  642.     return master_socket;        /* Good */
  643.  
  644. } /* get_listen_socket */
  645. #endif
  646.  
  647.  
  648.  
  649. /*    Read a directory into an hypertext object from the data socket
  650. **    --------------------------------------------------------------
  651. **
  652. ** On entry,
  653. **    anchor        Parent anchor to link the this node to
  654. **    address        Address of the directory
  655. ** On exit,
  656. **    returns        HT_LOADED if OK
  657. **            <0 if error.
  658. */
  659. PRIVATE int read_directory
  660. ARGS4 (
  661.   HTParentAnchor *,        parent,
  662.   CONST char *,            address,
  663.   HTFormat,            format_out,
  664.   HTStream *,            sink )
  665. {
  666.     HTStructured* target = HTML_new(parent, format_out, sink);
  667.     HTStructuredClass targetClass;
  668.     char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
  669.  
  670.     char c = 0;
  671.  
  672.     char *lastpath;  /* prefix for link, either "" (for root) or xxx  */
  673.     char *entry;   /* pointer into lastpath to bit after last slash */
  674.  
  675.     targetClass = *(target->isa);
  676.  
  677.     HTDirTitles(target, (HTAnchor*)parent);
  678.   
  679.     data_read_pointer = data_write_pointer = data_buffer;
  680.  
  681.     if (*filename == 0)  /* Empty filename : use root */
  682.         strcpy (lastpath, "/");
  683.     else 
  684.     {
  685.         char * p = strrchr(filename, '/');  /* find lastslash */
  686.         lastpath = (char*)malloc(strlen(p));
  687.     if (!lastpath) outofmem(__FILE__, "read_directory");
  688.         strcpy(lastpath, p+1);    /* take slash off the beginning */
  689.     }
  690.     free (filename);
  691.  
  692.    
  693.     {
  694.         HTBTree * bt = HTBTree_new((HTComparer)strcasecomp);
  695.         char c;
  696.     HTChunk * chunk = HTChunkCreate(128);
  697.     START(HTML_DIR);
  698.     for (c=0; c!=(char)EOF;)   /* For each entry in the directory */
  699.     {
  700.         char * filename = NULL;
  701.         char * p = entry;
  702.         HTChunkClear(chunk);
  703.         /*   read directory entry
  704.          */
  705.         for(;;) {                 /* Read in one line as filename */
  706.         c = NEXT_DATA_CHAR;
  707.         if (c == '\r' || c == LF) {    /* Terminator? */ 
  708.             if (chunk->size != 0)   /* got some text */
  709.               break;                /* finish getting one entry */
  710.           } else if (c == (char)EOF) {
  711.             break;             /* End of file */
  712.           } else {
  713.             HTChunkPutc(chunk, c);
  714.           }
  715.             }
  716.         HTChunkTerminate(chunk);
  717.         if (c == (char) EOF && chunk->size == 1)  /* 1 means empty: includes terminating 0 */
  718.             break;
  719.             if(TRACE) fprintf(stderr, "HTFTP: file name in %s is %s\n", lastpath, chunk->data);
  720.         StrAllocCopy(filename, chunk->data);
  721.         HTBTree_add(bt,filename); /* sort filename in the tree bt */
  722.  
  723.     }  /* next entry */
  724.         HTChunkFree(chunk);
  725.  
  726.     /* Run through tree printing out in order 
  727.      */
  728.     {
  729.         HTBTElement * ele;
  730.         for (ele = HTBTree_next(bt, NULL);
  731.          ele != NULL;
  732.          ele = HTBTree_next(bt, ele))
  733.         {
  734.             START(HTML_LI);
  735.         HTDirEntry(target, lastpath, (char *)HTBTree_object(ele));
  736.         }
  737.     }
  738.     END(HTML_DIR);
  739.     END_TARGET;
  740.     FREE_TARGET;
  741.     HTBTreeAndObject_free(bt);
  742.     }
  743.  
  744.     if (lastpath) free(lastpath);
  745.     return response(NIL) == 2 ? HT_LOADED : -1;
  746. }
  747.  
  748.  
  749. /*    Retrieve File from Server
  750. **    -------------------------
  751. **
  752. ** On entry,
  753. **    name        WWW address of a file: document, including hostname
  754. ** On exit,
  755. **    returns        Socket number for file if good.
  756. **            <0 if bad.
  757. */
  758. PUBLIC int HTFTPLoad
  759. ARGS4 (
  760.   CONST char *,            name,
  761.   HTParentAnchor *,        anchor,
  762.   HTFormat,            format_out,
  763.   HTStream *,            sink
  764. )
  765. {
  766.     BOOL isDirectory = NO;
  767.     int status;
  768.     int retry;            /* How many times tried? */
  769.     HTFormat format;
  770.     
  771.     for (retry=0; retry<2; retry++) {    /* For timed out/broken connections */
  772.     
  773.     status = get_connection(name);
  774.     if (status<0) return status;
  775.  
  776. #ifdef LISTEN
  777.     status = get_listen_socket();
  778.     if (status<0) return status;
  779.     
  780. #ifdef REPEAT_PORT
  781. /*    Inform the server of the port number we will listen on
  782. */
  783.     {
  784.         status = response(port_command);
  785.         if (status !=2) {        /* Could have timed out */
  786.         if (status<0) continue;        /* try again - net error*/
  787.         return -status;            /* bad reply */
  788.         }
  789.         if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
  790.     }
  791. #endif
  792. #else    /* Use PASV */
  793. /*    Tell the server to be passive
  794. */
  795.     {
  796.         char *p;
  797.         int reply, h0, h1, h2, h3, p0, p1;    /* Parts of reply */
  798.         status = response("PASV%c%c", CR, LF);
  799.         if (status !=2) {
  800.         if (status<0) continue;        /* retry or Bad return */
  801.         return -status;            /* bad reply */
  802.         }
  803.         for(p=response_text; *p; p++)
  804.         if ((*p<'0')||(*p>'9')) *p = ' ';    /* Keep only digits */
  805.         status = sscanf(response_text, "%d%d%d%d%d%d%d",
  806.             &reply, &h0, &h1, &h2, &h3, &p0, &p1);
  807.         if (status<5) {
  808.         if (TRACE) fprintf(stderr, "FTP: PASV reply has no inet address!\n");
  809.         return -99;
  810.         }
  811.         passive_port = (p0<<8) + p1;
  812.         if (TRACE) fprintf(stderr, "FTP: Server is listening on port %d\n",
  813.             passive_port);
  814.     }
  815.  
  816. /*    Open connection for data:
  817. */
  818.     {
  819.         struct sockaddr_in soc_address;
  820.         int status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  821.         if (status<0) {
  822.         (void) HTInetStatus("socket for data socket");
  823.         return status;
  824.         }
  825.         data_soc = status;
  826.         
  827.         soc_address.sin_addr.s_addr = control->addr;
  828.         soc_address.sin_port = htons(passive_port);
  829.         soc_address.sin_family = AF_INET;        /* Family, host order  */
  830.         if (TRACE) fprintf(stderr,  
  831.         "FTP: Data remote address is port %d, inet %d.%d.%d.%d\n",
  832.             (unsigned int)ntohs(soc_address.sin_port),
  833.             (int)*((unsigned char *)(&soc_address.sin_addr)+0),
  834.             (int)*((unsigned char *)(&soc_address.sin_addr)+1),
  835.             (int)*((unsigned char *)(&soc_address.sin_addr)+2),
  836.             (int)*((unsigned char *)(&soc_address.sin_addr)+3));
  837.     
  838.         status = connect(data_soc, (struct sockaddr*)&soc_address,
  839.             sizeof(soc_address));
  840.         if (status<0){
  841.         (void) HTInetStatus("connect for data");
  842.         NETCLOSE(data_soc);
  843.         return status;            /* Bad return */
  844.         }
  845.         
  846.         if (TRACE) fprintf(stderr, "FTP data connected, socket %d\n", data_soc);
  847.     }
  848. #endif /* use PASV */
  849.     status = 0;
  850.         break;    /* No more retries */
  851.  
  852.     } /* for retries */
  853.     if (status<0) return status;    /* Failed with this code */
  854.     
  855. /*    Ask for the file:
  856. */    
  857.     {
  858.         char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
  859.     char command[LINE_LENGTH+1];
  860.     BOOL binary;
  861.     HTAtom * encoding;
  862.     if (!*filename) StrAllocCopy(filename, "/");
  863.     format = HTFileFormat(filename, &encoding);
  864.     binary = (encoding != HTAtom_for("8bit")
  865.           && encoding != HTAtom_for("7bit"));
  866.         if (binary != control->binary) {
  867.         char * mode = binary ? "I" : "A";
  868.         sprintf(command, "TYPE %s%c%c", mode, CR, LF);
  869.         status = response(command);
  870.         if (status != 2) return -status;
  871.         control->binary = binary;
  872.     }
  873.     sprintf(command, "RETR %s%c%c", filename, CR, LF);
  874.     status = response(command);
  875.     if (status != 1) {  /* Failed : try to CWD to it */
  876.       sprintf(command, "CWD %s%c%c", filename, CR, LF);
  877.       status = response(command);
  878.       if (status == 2) {  /* Successed : let's NAME LIST it */
  879.         isDirectory = YES;
  880.         sprintf(command, "NLST%c%c", CR, LF);
  881.         status = response (command);
  882.       }
  883.     }
  884.     free(filename);
  885.     if (status != 1) return -status;        /* Action not started */
  886.     }
  887.  
  888. #ifdef LISTEN
  889. /*    Wait for the connection
  890. */
  891.     {
  892.     struct sockaddr_in soc_address;
  893.         int    soc_addrlen=sizeof(soc_address);
  894.     status = accept(master_socket,
  895.             (struct sockaddr *)&soc_address,
  896.             &soc_addrlen);
  897.     if (status<0)
  898.         return HTInetStatus("accept");
  899.     CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
  900.     data_soc = status;
  901.     }
  902. #else
  903. /* @@ */
  904. #endif
  905.     if (isDirectory) {
  906.     return read_directory (anchor, name, format_out, sink);
  907.       /* returns HT_LOADED or error */
  908.     } else {
  909.     HTParseSocket(format, format_out,
  910.         anchor, data_soc, sink);
  911.         
  912.     HTInitInput(control->socket);
  913.     /* Reset buffering to control connection DD 921208 */
  914.     
  915.     status = NETCLOSE(data_soc);
  916.     if (TRACE) fprintf(stderr, "FTP: Closing data socket %d\n", data_soc);
  917.     if (status<0) (void) HTInetStatus("close");    /* Comment only */
  918.     data_soc = -1;    /* invalidate it */
  919.     
  920.     status = response(NIL);        /* Pick up final reply */
  921.     if (status!=2) return HTLoadError(sink, 500, response_text);
  922.  
  923.     return HT_LOADED;
  924.     }       
  925. } /* open_file_read */
  926.