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