~ubuntu-branches/ubuntu/warty/lynx/warty-security

« back to all changes in this revision

Viewing changes to WWW/Library/Implementation/HTFTP.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2004-09-16 12:14:10 UTC
  • Revision ID: james.westby@ubuntu.com-20040916121410-cz1gu92c4nqfeyrg
Tags: upstream-2.8.5
ImportĀ upstreamĀ versionĀ 2.8.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
**      LM      Lou Montulli <montulli@ukanaix.cc.ukans.edu>
 
24
**      FM      Foteos Macrides <macrides@sci.wfeb.edu>
 
25
** History:
 
26
**       2 May 91       Written TBL, as a part of the WorldWideWeb project.
 
27
**      15 Jan 92       Bug fix: close() was used for NETCLOSE for control soc
 
28
**      10 Feb 92       Retry if cached connection times out or breaks
 
29
**       8 Dec 92       Bug fix 921208 TBL after DD
 
30
**      17 Dec 92       Anon FTP password now just WWWuser@ suggested by DD
 
31
**                      fails on princeton.edu!
 
32
**      27 Dec 93 (FM)  Fixed up so FTP now works with VMS hosts.  Path
 
33
**                      must be Unix-style and cannot include the device
 
34
**                      or top directory.
 
35
**      ?? ??? ?? (LM)  Added code to prompt and send passwords for non
 
36
**                      anonymous FTP
 
37
**      25 Mar 94 (LM)  Added code to recognize different ftp server types
 
38
**                      and code to parse dates and sizes on most hosts.
 
39
**      27 Mar 93 (FM)  Added code for getting dates and sizes on VMS hosts.
 
40
**
 
41
** Notes:
 
42
**                      Portions Copyright 1994 Trustees of Dartmouth College
 
43
**                      Code for recognizing different FTP servers and
 
44
**                      parsing "ls -l" output taken from Macintosh Fetch
 
45
**                      program with permission from Jim Matthews,
 
46
**                      Dartmouth Software Development Team.
 
47
*/
 
48
 
 
49
/*
 
50
BUGS:   @@@     Limit connection cache size!
 
51
                Error reporting to user.
 
52
                400 & 500 errors are ack'ed by user with windows.
 
53
                Use configuration file for user names
 
54
 
 
55
**              Note for portability this version does not use select() and
 
56
**              so does not watch the control and data channels at the
 
57
**              same time.
 
58
*/
 
59
 
 
60
#include <HTUtils.h>
 
61
 
 
62
#include <HTAlert.h>
 
63
 
 
64
#include <HTFTP.h>      /* Implemented here */
 
65
#include <HTTCP.h>
 
66
#include <HTTP.h>
 
67
#include <HTFont.h>
 
68
 
 
69
#define REPEAT_PORT     /* Give the port number for each file */
 
70
#define REPEAT_LISTEN   /* Close each listen socket and open a new one */
 
71
 
 
72
/* define POLL_PORTS             If allocation does not work, poll ourselves.*/
 
73
#define LISTEN_BACKLOG 2        /* Number of pending connect requests (TCP)*/
 
74
 
 
75
#define FIRST_TCP_PORT  1024    /* Region to try for a listening port */
 
76
#define LAST_TCP_PORT   5999
 
77
 
 
78
#define LINE_LENGTH 256
 
79
 
 
80
#include <HTParse.h>
 
81
#include <HTAnchor.h>
 
82
#include <HTFile.h>     /* For HTFileFormat() */
 
83
#include <HTBTree.h>
 
84
#include <HTChunk.h>
 
85
#ifndef IPPORT_FTP
 
86
#define IPPORT_FTP      21
 
87
#endif /* !IPORT_FTP */
 
88
 
 
89
#include <LYUtils.h>
 
90
#include <LYGlobalDefs.h>
 
91
#include <LYStrings.h>
 
92
#include <LYLeaks.h>
 
93
 
 
94
typedef struct _connection {
 
95
    struct _connection *        next;   /* Link on list         */
 
96
    unsigned long               addr;   /* IP address           */
 
97
    int                         socket; /* Socket number for communication */
 
98
    BOOL                        binary; /* Binary mode? */
 
99
} connection;
 
100
 
 
101
/*              Hypertext object building machinery
 
102
*/
 
103
#include <HTML.h>
 
104
 
 
105
#define PUTC(c)      (*targetClass.put_character) (target, c)
 
106
#define PUTS(s)      (*targetClass.put_string)    (target, s)
 
107
#define START(e)     (*targetClass.start_element) (target, e, 0, 0, -1, 0)
 
108
#define END(e)       (*targetClass.end_element)   (target, e, 0)
 
109
#define FREE_TARGET  (*targetClass._free)         (target)
 
110
#define ABORT_TARGET (*targetClass._free)         (target)
 
111
 
 
112
struct _HTStructured {
 
113
        CONST HTStructuredClass *       isa;
 
114
        /* ... */
 
115
};
 
116
 
 
117
/*      Global Variables
 
118
**      ---------------------
 
119
*/
 
120
PUBLIC int HTfileSortMethod = FILE_BY_NAME;
 
121
 
 
122
#ifndef DISABLE_FTP /*This disables everything to end-of-file */
 
123
PRIVATE char ThisYear[8];
 
124
PRIVATE char LastYear[8];
 
125
PRIVATE int TheDate;
 
126
PRIVATE BOOLEAN HaveYears = FALSE;
 
127
 
 
128
/*      Module-Wide Variables
 
129
**      ---------------------
 
130
*/
 
131
PRIVATE connection * connections = NULL;/* Linked list of connections */
 
132
PRIVATE char response_text[LINE_LENGTH+1];/* Last response from ftp host */
 
133
PRIVATE connection * control = NULL;    /* Current connection */
 
134
PRIVATE int data_soc = -1;              /* Socket for data transfer =invalid */
 
135
PRIVATE char *user_entered_password = NULL;
 
136
PRIVATE char *last_username_and_host = NULL;
 
137
 
 
138
/*
 
139
 * ProFTPD 1.2.5rc1 is known to have a broken implementation of RETR.  If asked
 
140
 * to retrieve a directory, it gets confused and fails subsequent commands such
 
141
 * as CWD and LIST.  Since this is an unusual bug, we should remove this ifdef
 
142
 * at some point - TD 2004/1/1.
 
143
 */
 
144
#define BROKEN_PROFTPD 1
 
145
PRIVATE int ProFTPD_bugs = FALSE;
 
146
 
 
147
typedef enum {
 
148
        GENERIC_SERVER
 
149
        , MACHTEN_SERVER
 
150
        , UNIX_SERVER
 
151
        , VMS_SERVER
 
152
        , CMS_SERVER
 
153
        , DCTS_SERVER
 
154
        , TCPC_SERVER
 
155
        , PETER_LEWIS_SERVER
 
156
        , NCSA_SERVER
 
157
        , WINDOWS_NT_SERVER
 
158
        , WINDOWS_2K_SERVER
 
159
        , MS_WINDOWS_SERVER
 
160
        , MSDOS_SERVER
 
161
        , APPLESHARE_SERVER
 
162
        , NETPRESENZ_SERVER
 
163
        , DLS_SERVER
 
164
} eServerType;
 
165
 
 
166
PRIVATE eServerType server_type = GENERIC_SERVER; /* the type of ftp host */
 
167
PRIVATE int     unsure_type = FALSE;            /* sure about the type? */
 
168
PRIVATE BOOLEAN use_list = FALSE;               /* use the LIST command? */
 
169
 
 
170
PRIVATE int     interrupted_in_next_data_char = FALSE;
 
171
 
 
172
#ifdef POLL_PORTS
 
173
PRIVATE PortNumber      port_number = FIRST_TCP_PORT;
 
174
#endif /* POLL_PORTS */
 
175
 
 
176
PRIVATE int     master_socket = -1;     /* Listening socket = invalid   */
 
177
PRIVATE char    port_command[255];      /* Command for setting the port */
 
178
PRIVATE fd_set  open_sockets;           /* Mask of active channels */
 
179
PRIVATE int     num_sockets;            /* Number of sockets to scan */
 
180
PRIVATE PortNumber      passive_port;   /* Port server specified for data */
 
181
 
 
182
 
 
183
#define NEXT_CHAR HTGetCharacter()      /* Use function in HTFormat.c */
 
184
 
 
185
#define DATA_BUFFER_SIZE 2048
 
186
PRIVATE char data_buffer[DATA_BUFFER_SIZE];             /* Input data buffer */
 
187
PRIVATE char * data_read_pointer;
 
188
PRIVATE char * data_write_pointer;
 
189
#define NEXT_DATA_CHAR next_data_char()
 
190
PRIVATE int close_connection PARAMS((
 
191
        connection *    con));
 
192
 
 
193
 
 
194
#ifdef LY_FIND_LEAKS
 
195
/*
 
196
**  This function frees module globals. - FM
 
197
*/
 
198
PRIVATE void free_FTPGlobals NOARGS
 
199
{
 
200
    FREE(user_entered_password);
 
201
    FREE(last_username_and_host);
 
202
    if (control) {
 
203
        if (control->socket != -1)
 
204
            close_connection(control);
 
205
        FREE(control);
 
206
    }
 
207
}
 
208
#endif /* LY_FIND_LEAKS */
 
209
 
 
210
/* PUBLIC                                               HTVMS_name()
 
211
**              CONVERTS WWW name into a VMS name
 
212
** ON ENTRY:
 
213
**      nn              Node Name (optional)
 
214
**      fn              WWW file name
 
215
**
 
216
** ON EXIT:
 
217
**      returns         vms file specification
 
218
**
 
219
** Bug: Returns pointer to static -- non-reentrant
 
220
*/
 
221
PUBLIC char * HTVMS_name ARGS2(
 
222
        CONST char *,   nn,
 
223
        CONST char *,   fn)
 
224
{
 
225
 
 
226
/*      We try converting the filename into Files-11 syntax.  That is, we assume
 
227
**      first that the file is, like us, on a VMS node.  We try remote
 
228
**      (or local) DECnet access.  Files-11, VMS, VAX and DECnet
 
229
**      are trademarks of Digital Equipment Corporation.
 
230
**      The node is assumed to be local if the hostname WITHOUT DOMAIN
 
231
**      matches the local one. @@@
 
232
*/
 
233
    static char *vmsname;
 
234
    char * filename = (char*)malloc(strlen(fn)+1);
 
235
    char * nodename = (char*)malloc(strlen(nn)+2+1);    /* Copies to hack */
 
236
    char *second;               /* 2nd slash */
 
237
    char *last;                 /* last slash */
 
238
 
 
239
    CONST char * hostname = HTHostName();
 
240
 
 
241
    if (!filename || !nodename)
 
242
        outofmem(__FILE__, "HTVMSname");
 
243
    strcpy(filename, fn);
 
244
    strcpy(nodename, "");       /* On same node?  Yes if node names match */
 
245
    if (strncmp(nn, "localhost", 9)) {
 
246
        CONST char *p;
 
247
        CONST char *q;
 
248
        for (p = hostname, q = nn;
 
249
             *p && *p != '.' && *q && *q != '.'; p++, q++){
 
250
            if (TOUPPER(*p) != TOUPPER(*q)) {
 
251
                char *r;
 
252
                strcpy(nodename, nn);
 
253
                r = strchr(nodename, '.');      /* Mismatch */
 
254
                if (r)
 
255
                    *r = '\0';                  /* Chop domain */
 
256
                strcat(nodename, "::");         /* Try decnet anyway */
 
257
                break;
 
258
            }
 
259
        }
 
260
    }
 
261
 
 
262
    second = strchr(filename+1, '/');           /* 2nd slash */
 
263
    last = strrchr(filename, '/');      /* last slash */
 
264
 
 
265
    if (!second) {                              /* Only one slash */
 
266
        HTSprintf0(&vmsname, "%s%s", nodename, filename + 1);
 
267
    } else if (second == last) {                /* Exactly two slashes */
 
268
        *second = '\0';         /* Split filename from disk */
 
269
        HTSprintf0(&vmsname, "%s%s:%s", nodename, filename+1, second+1);
 
270
        *second = '/';  /* restore */
 
271
    } else {                            /* More than two slashes */
 
272
        char * p;
 
273
        *second = '\0';         /* Split disk from directories */
 
274
        *last = '\0';           /* Split dir from filename */
 
275
        HTSprintf0(&vmsname, "%s%s:[%s]%s",
 
276
                nodename, filename+1, second+1, last+1);
 
277
        *second = *last = '/';  /* restore filename */
 
278
        for (p = strchr(vmsname, '['); *p!=']'; p++)
 
279
            if (*p == '/')
 
280
                *p = '.';               /* Convert dir sep.  to dots */
 
281
    }
 
282
    FREE(nodename);
 
283
    FREE(filename);
 
284
    return vmsname;
 
285
}
 
286
 
 
287
/*      Procedure: Read a character from the data connection
 
288
**      ----------------------------------------------------
 
289
*/
 
290
PRIVATE int next_data_char NOARGS
 
291
{
 
292
    int status;
 
293
    if (data_read_pointer >= data_write_pointer) {
 
294
        status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
 
295
      if (status == HT_INTERRUPTED)
 
296
        interrupted_in_next_data_char = 1;
 
297
      if (status <= 0)
 
298
        return -1;
 
299
      data_write_pointer = data_buffer + status;
 
300
      data_read_pointer = data_buffer;
 
301
    }
 
302
#ifdef NOT_ASCII
 
303
    {
 
304
        char c = *data_read_pointer++;
 
305
        return FROMASCII(c);
 
306
    }
 
307
#else
 
308
    return UCH(*data_read_pointer++);
 
309
#endif /* NOT_ASCII */
 
310
}
 
311
 
 
312
 
 
313
/*      Close an individual connection
 
314
**
 
315
*/
 
316
PRIVATE int close_connection ARGS1(
 
317
        connection *,   con)
 
318
{
 
319
    connection * scan;
 
320
    int status;
 
321
    CTRACE((tfp, "HTFTP: Closing control socket %d\n", con->socket));
 
322
    status = NETCLOSE(con->socket);
 
323
    if (TRACE && status != 0) {
 
324
#ifdef UNIX
 
325
        CTRACE((tfp, "HTFTP:close_connection: %s", LYStrerror(errno)));
 
326
#else
 
327
        if (con->socket != INVSOC)
 
328
            HTInetStatus("HTFTP:close_connection");
 
329
#endif
 
330
    }
 
331
    con->socket = -1;
 
332
    if (connections == con) {
 
333
        connections = con->next;
 
334
        return status;
 
335
    }
 
336
    for (scan = connections; scan; scan = scan->next) {
 
337
        if (scan->next == con) {
 
338
            scan->next = con->next;     /* Unlink */
 
339
            if (control == con)
 
340
                control = (connection*)0;
 
341
            return status;
 
342
        } /*if */
 
343
    } /* for */
 
344
    return -1;          /* very strange -- was not on list. */
 
345
}
 
346
 
 
347
PRIVATE char *help_message_buffer = NULL;  /* global :( */
 
348
 
 
349
PRIVATE void init_help_message_cache NOARGS
 
350
{
 
351
    FREE(help_message_buffer);
 
352
}
 
353
 
 
354
PRIVATE void help_message_cache_add ARGS1(
 
355
        char *,         string)
 
356
{
 
357
    if (help_message_buffer)
 
358
        StrAllocCat(help_message_buffer, string);
 
359
    else
 
360
        StrAllocCopy(help_message_buffer, string);
 
361
 
 
362
    CTRACE((tfp,"Adding message to help cache: %s\n",string));
 
363
}
 
364
 
 
365
PRIVATE char *help_message_cache_non_empty NOARGS
 
366
{
 
367
  return(help_message_buffer);
 
368
}
 
369
 
 
370
PRIVATE char *help_message_cache_contents NOARGS
 
371
{
 
372
   return(help_message_buffer);
 
373
}
 
374
 
 
375
/*      Send One Command
 
376
**      ----------------
 
377
**
 
378
**      This function checks whether we have a control connection, and sends
 
379
**      one command if given.
 
380
**
 
381
** On entry,
 
382
**      control points to the connection which is established.
 
383
**      cmd     points to a command, or is zero to just get the response.
 
384
**
 
385
**      The command should already be terminated with the CRLF pair.
 
386
**
 
387
** On exit,
 
388
**      returns:  1 for success,
 
389
**                or negative for communication failure (in which case
 
390
**                the control connection will be closed).
 
391
*/
 
392
PRIVATE int write_cmd ARGS1(
 
393
        char *,         cmd)
 
394
{
 
395
    int status;
 
396
 
 
397
    if (!control) {
 
398
        CTRACE((tfp, "HTFTP: No control connection set up!!\n"));
 
399
        return -99;
 
400
    }
 
401
 
 
402
    if (cmd) {
 
403
        CTRACE((tfp, "  Tx: %s", cmd));
 
404
#ifdef NOT_ASCII
 
405
        {
 
406
            char * p;
 
407
            for (p = cmd; *p; p++) {
 
408
                *p = TOASCII(*p);
 
409
            }
 
410
        }
 
411
#endif /* NOT_ASCII */
 
412
        status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
 
413
        if (status < 0) {
 
414
            CTRACE((tfp, "HTFTP: Error %d sending command: closing socket %d\n",
 
415
                        status, control->socket));
 
416
            close_connection(control);
 
417
            return status;
 
418
        }
 
419
    }
 
420
    return 1;
 
421
}
 
422
 
 
423
/*      Execute Command and get Response
 
424
**      --------------------------------
 
425
**
 
426
**      See the state machine illustrated in RFC959, p57. This implements
 
427
**      one command/reply sequence.  It also interprets lines which are to
 
428
**      be continued, which are marked with a "-" immediately after the
 
429
**      status code.
 
430
**
 
431
**      Continuation then goes on until a line with a matching reply code
 
432
**      an a space after it.
 
433
**
 
434
** On entry,
 
435
**      control points to the connection which is established.
 
436
**      cmd     points to a command, or is zero to just get the response.
 
437
**
 
438
**      The command must already be terminated with the CRLF pair.
 
439
**
 
440
** On exit,
 
441
**      returns:  The first digit of the reply type,
 
442
**                or negative for communication failure.
 
443
*/
 
444
PRIVATE int response ARGS1(
 
445
        char *,         cmd)
 
446
{
 
447
    int result;                         /* Three-digit decimal code */
 
448
    int continuation_response = -1;
 
449
    int status;
 
450
 
 
451
    if ((status = write_cmd(cmd)) < 0)
 
452
        return status;
 
453
 
 
454
    do {
 
455
        char *p = response_text;
 
456
        for (;;) {
 
457
            int ich = NEXT_CHAR;
 
458
            if (((*p++ = (char) ich) == LF)
 
459
                        || (p == &response_text[LINE_LENGTH])) {
 
460
 
 
461
                char continuation;
 
462
 
 
463
                if (interrupted_in_htgetcharacter) {
 
464
                    CTRACE((tfp, "HTFTP: Interrupted in HTGetCharacter, apparently.\n"));
 
465
                    NETCLOSE (control->socket);
 
466
                    control->socket = -1;
 
467
                    return HT_INTERRUPTED;
 
468
                }
 
469
 
 
470
                *p = '\0';                      /* Terminate the string */
 
471
                CTRACE((tfp, "    Rx: %s", response_text));
 
472
 
 
473
                /* Check for login or help messages */
 
474
                if (!strncmp(response_text,"230-",4) ||
 
475
                    !strncmp(response_text,"250-",4) ||
 
476
                    !strncmp(response_text,"220-",4))
 
477
                    help_message_cache_add(response_text+4);
 
478
 
 
479
                sscanf(response_text, "%d%c", &result, &continuation);
 
480
                if  (continuation_response == -1) {
 
481
                        if (continuation == '-')  /* start continuation */
 
482
                            continuation_response = result;
 
483
                } else {        /* continuing */
 
484
                        if (continuation_response == result &&
 
485
                            continuation == ' ')
 
486
                            continuation_response = -1; /* ended */
 
487
                }
 
488
#ifdef BROKEN_PROFTPD
 
489
                if (result == 220 && LYstrstr(response_text, "ProFTPD 1.2.5")) {
 
490
                    ProFTPD_bugs = TRUE;
 
491
                    CTRACE((tfp, "This server is broken (RETR)\n"));
 
492
                }
 
493
#endif
 
494
                break;
 
495
            } /* if end of line */
 
496
 
 
497
            if (interrupted_in_htgetcharacter) {
 
498
                CTRACE((tfp, "HTFTP: Interrupted in HTGetCharacter, apparently.\n"));
 
499
                NETCLOSE (control->socket);
 
500
                control->socket = -1;
 
501
                return HT_INTERRUPTED;
 
502
            }
 
503
 
 
504
            if (ich == EOF) {
 
505
                CTRACE((tfp, "Error on rx: closing socket %d\n",
 
506
                            control->socket));
 
507
                strcpy(response_text, "000 *** TCP read error on response\n");
 
508
                close_connection(control);
 
509
                return -1;      /* End of file on response */
 
510
            }
 
511
        } /* Loop over characters */
 
512
 
 
513
    } while (continuation_response != -1);
 
514
 
 
515
    if (result == 421) {
 
516
        CTRACE((tfp, "HTFTP: They close so we close socket %d\n",
 
517
                    control->socket));
 
518
        close_connection(control);
 
519
        return -1;
 
520
    }
 
521
    if ((result == 255 && server_type == CMS_SERVER) &&
 
522
        (0 == strncasecomp(cmd, "CWD", 3) ||
 
523
         0 == strcasecomp(cmd, "CDUP"))) {
 
524
        /*
 
525
        **  Alas, CMS returns 255 on failure to CWD to parent of root. - PG
 
526
        */
 
527
        result = 555;
 
528
    }
 
529
    return result/100;
 
530
}
 
531
 
 
532
PRIVATE int send_cmd_1 ARGS1(char *, verb)
 
533
{
 
534
    char command[80];
 
535
 
 
536
    sprintf(command, "%.*s%c%c", (int) sizeof(command)-4, verb, CR, LF);
 
537
    return response (command);
 
538
}
 
539
 
 
540
PRIVATE int send_cmd_2 ARGS2(char *, verb, char *, param)
 
541
{
 
542
    char *command = 0;
 
543
    int status;
 
544
 
 
545
    HTSprintf0(&command, "%s %s%c%c", verb, param, CR, LF);
 
546
    status = response (command);
 
547
    FREE(command);
 
548
 
 
549
    return status;
 
550
}
 
551
 
 
552
#define send_cwd(path) send_cmd_2("CWD", path)
 
553
 
 
554
/*
 
555
 *  This function should try to set the macintosh server into binary mode.
 
556
 *  Some servers need an additional letter after the MACB command.
 
557
 */
 
558
PRIVATE int set_mac_binary ARGS1(
 
559
        eServerType,    ServerType)
 
560
{
 
561
    /* try to set mac binary mode */
 
562
    if (ServerType == APPLESHARE_SERVER ||
 
563
        ServerType == NETPRESENZ_SERVER) {
 
564
        /*
 
565
         *  Presumably E means "Enable".  - KW
 
566
         */
 
567
        return(2 == response("MACB E\r\n"));
 
568
    } else {
 
569
        return(2 == response("MACB\r\n"));
 
570
    }
 
571
}
 
572
 
 
573
/* This function gets the current working directory to help
 
574
 * determine what kind of host it is
 
575
 */
 
576
 
 
577
PRIVATE void get_ftp_pwd ARGS2(
 
578
        eServerType *,  ServerType,
 
579
        BOOLEAN *,      UseList)
 
580
{
 
581
 
 
582
    char *cp;
 
583
    /* get the working directory (to see what it looks like) */
 
584
    int status = response("PWD\r\n");
 
585
    if (status < 0) {
 
586
        return;
 
587
    } else {
 
588
        cp = strchr(response_text+5,'"');
 
589
        if (cp)
 
590
            *cp = '\0';
 
591
        if (*ServerType == TCPC_SERVER) {
 
592
            *ServerType = ((response_text[5] == '/') ?
 
593
                                          NCSA_SERVER : TCPC_SERVER);
 
594
            CTRACE((tfp, "HTFTP: Treating as %s server.\n",
 
595
                         ((*ServerType == NCSA_SERVER) ?
 
596
                                                 "NCSA" : "TCPC")));
 
597
        } else if (response_text[5] == '/') {
 
598
            /* path names beginning with / imply Unix,
 
599
             * right?
 
600
             */
 
601
            if (set_mac_binary(*ServerType)) {
 
602
                *ServerType = NCSA_SERVER;
 
603
                CTRACE((tfp, "HTFTP: Treating as NCSA server.\n"));
 
604
            } else {
 
605
                 *ServerType = UNIX_SERVER;
 
606
                 *UseList = TRUE;
 
607
                 CTRACE((tfp, "HTFTP: Treating as Unix server.\n"));
 
608
            }
 
609
            return;
 
610
        } else if (response_text[strlen(response_text)-1] == ']') {
 
611
            /* path names ending with ] imply VMS, right? */
 
612
            *ServerType = VMS_SERVER;
 
613
            *UseList = TRUE;
 
614
            CTRACE((tfp, "HTFTP: Treating as VMS server.\n"));
 
615
        } else {
 
616
            *ServerType = GENERIC_SERVER;
 
617
            CTRACE((tfp, "HTFTP: Treating as Generic server.\n"));
 
618
        }
 
619
 
 
620
        if ((*ServerType == NCSA_SERVER) ||
 
621
            (*ServerType == TCPC_SERVER) ||
 
622
            (*ServerType == PETER_LEWIS_SERVER) ||
 
623
            (*ServerType == NETPRESENZ_SERVER))
 
624
            set_mac_binary(*ServerType);
 
625
    }
 
626
}
 
627
 
 
628
/* This function turns MSDOS-like directory output off for
 
629
 * Windows NT servers.
 
630
 */
 
631
 
 
632
PRIVATE void set_unix_dirstyle ARGS2(
 
633
        eServerType *,  ServerType,
 
634
        BOOLEAN *,      UseList)
 
635
{
 
636
 
 
637
    char *cp;
 
638
    /* This is a toggle.  It seems we have to toggle in order to see
 
639
     * the current state (after toggling), so we may end up toggling
 
640
     * twice.  - kw
 
641
     */
 
642
    int status = response("SITE DIRSTYLE\r\n");
 
643
    if (status != 2) {
 
644
        *ServerType = GENERIC_SERVER;
 
645
        CTRACE((tfp, "HTFTP: DIRSTYLE failed, treating as Generic server.\n"));
 
646
        return;
 
647
    } else {
 
648
        *UseList = TRUE;
 
649
        /* Expecting one of:
 
650
         * 200 MSDOS-like directory output is off
 
651
         * 200 MSDOS-like directory output is on
 
652
         * The following code doesn't look for the full exact string -
 
653
         * who knows how the wording may change in some future version.
 
654
         * If the first response isn't recognized, we toggle again
 
655
         * anyway, under the assumption that it's more likely that
 
656
         * the MSDOS setting was "off" originally. - kw
 
657
         */
 
658
        cp = strstr(response_text+4, "MSDOS");
 
659
        if (cp && strstr(cp, " off")) {
 
660
            return;             /* already off now. */
 
661
        } else {
 
662
            response("SITE DIRSTYLE\r\n");
 
663
        }
 
664
    }
 
665
}
 
666
 
 
667
/*      Get a valid connection to the host
 
668
**      ----------------------------------
 
669
**
 
670
** On entry,
 
671
**      arg     points to the name of the host in a hypertext address
 
672
** On exit,
 
673
**      returns <0 if error
 
674
**              socket number if success
 
675
**
 
676
**      This routine takes care of managing timed-out connections, and
 
677
**      limiting the number of connections in use at any one time.
 
678
**
 
679
**      It ensures that all connections are logged in if they exist.
 
680
**      It ensures they have the port number transferred.
 
681
*/
 
682
PRIVATE int get_connection ARGS2(
 
683
        CONST char *,           arg,
 
684
        HTParentAnchor *,       anchor)
 
685
{
 
686
    int status;
 
687
    char * command = 0;
 
688
    connection * con;
 
689
    char * username = NULL;
 
690
    char * password = NULL;
 
691
    static BOOLEAN firstuse = TRUE;
 
692
 
 
693
    if (firstuse) {
 
694
        /*
 
695
        **  Set up freeing at exit. - FM
 
696
        */
 
697
#ifdef LY_FIND_LEAKS
 
698
        atexit(free_FTPGlobals);
 
699
#endif
 
700
        firstuse = FALSE;
 
701
    }
 
702
 
 
703
    if (control) {
 
704
        /*
 
705
        **  Reuse this object - KW, DW & FM
 
706
        */
 
707
        if (control->socket != -1) {
 
708
            NETCLOSE(control->socket);
 
709
        }
 
710
        con = control;
 
711
        con->addr = 0;
 
712
        con->binary = FALSE;
 
713
    } else {
 
714
        /*
 
715
        **  Allocate and init control struct.
 
716
        */
 
717
        con = typecalloc(connection);
 
718
        if (con == NULL)
 
719
            outofmem(__FILE__, "get_connection");
 
720
    }
 
721
    con->socket = -1;
 
722
 
 
723
    if (!arg) return -1;                /* Bad if no name specified     */
 
724
    if (!*arg) return -1;               /* Bad if name had zero length  */
 
725
 
 
726
/* Get node name:
 
727
*/
 
728
    CTRACE((tfp, "get_connection(%s)\n", arg));
 
729
    {
 
730
        char *p1 = HTParse(arg, "", PARSE_HOST);
 
731
        char *p2 = strrchr(p1, '@');    /* user? */
 
732
        char * pw = NULL;
 
733
 
 
734
        if (p2 != NULL) {
 
735
            username = p1;
 
736
            *p2 = '\0';                 /* terminate */
 
737
            p1 = p2+1;                  /* point to host */
 
738
            pw = strchr(username, ':');
 
739
            if (pw != NULL) {
 
740
                *pw++ = '\0';
 
741
                password = HTUnEscape(pw);
 
742
            }
 
743
            if (*username)
 
744
                HTUnEscape(username);
 
745
 
 
746
            /*
 
747
             *  If the password doesn't exist then we are going to have
 
748
             *  to ask the user for it.  The only problem is that we
 
749
             *  don't want to ask for it every time, so we will store
 
750
             *  away in a primitive fashion.
 
751
             */
 
752
            if (!password) {
 
753
                char *tmp = NULL;
 
754
 
 
755
                HTSprintf0(&tmp, "%s@%s", username, p1);
 
756
                /*
 
757
                 *  If the user@host is not equal to the last time through
 
758
                 *  or user_entered_password has no data then we need
 
759
                 *  to ask the user for the password.
 
760
                 */
 
761
                if (!last_username_and_host ||
 
762
                    strcmp(tmp, last_username_and_host) ||
 
763
                    !user_entered_password) {
 
764
 
 
765
                    StrAllocCopy(last_username_and_host, tmp);
 
766
                    HTSprintf0(&tmp, gettext("Enter password for user %s@%s:"),
 
767
                                     username, p1);
 
768
                    FREE(user_entered_password);
 
769
                    user_entered_password = HTPromptPassword(tmp);
 
770
 
 
771
                } /* else we already know the password */
 
772
                password = user_entered_password;
 
773
                FREE(tmp);
 
774
            }
 
775
        }
 
776
 
 
777
        if (!username)
 
778
            FREE(p1);
 
779
    } /* scope of p1 */
 
780
 
 
781
    status = HTDoConnect (arg, "FTP", IPPORT_FTP, (int *)&con->socket);
 
782
 
 
783
    if (status < 0) {
 
784
        if (status == HT_INTERRUPTED) {
 
785
            CTRACE((tfp, "HTFTP: Interrupted on connect\n"));
 
786
        } else {
 
787
            CTRACE((tfp, "HTFTP: Unable to connect to remote host for `%s'.\n",
 
788
                        arg));
 
789
        }
 
790
        if (status == HT_INTERRUPTED) {
 
791
            _HTProgress (CONNECTION_INTERRUPTED);
 
792
            status = HT_NOT_LOADED;
 
793
        } else {
 
794
            HTAlert(gettext("Unable to connect to FTP host."));
 
795
        }
 
796
        if (con->socket != -1)
 
797
        {
 
798
          NETCLOSE(con->socket);
 
799
        }
 
800
 
 
801
        FREE(username);
 
802
        if (control == con)
 
803
            control = NULL;
 
804
        FREE(con);
 
805
        return status;                  /* Bad return */
 
806
    }
 
807
 
 
808
    CTRACE((tfp, "FTP connected, socket %d  control %p\n",
 
809
                con->socket, con));
 
810
    control = con;              /* Current control connection */
 
811
 
 
812
    /* Initialise buffering for control connection */
 
813
    HTInitInput(control->socket);
 
814
    init_help_message_cache();  /* Clear the login message buffer. */
 
815
 
 
816
 
 
817
/*      Now we log in           Look up username, prompt for pw.
 
818
*/
 
819
    status = response((char *)0);       /* Get greeting */
 
820
 
 
821
    if (status == HT_INTERRUPTED) {
 
822
        CTRACE((tfp, "HTFTP: Interrupted at beginning of login.\n"));
 
823
        _HTProgress (CONNECTION_INTERRUPTED);
 
824
        NETCLOSE(control->socket);
 
825
        control->socket = -1;
 
826
        return HT_INTERRUPTED;
 
827
    }
 
828
    server_type = GENERIC_SERVER;       /* reset */
 
829
    if (status == 2) {          /* Send username */
 
830
        char *cp;               /* look at greeting text */
 
831
 
 
832
        /* don't gettext() this -- incoming text: */
 
833
        if (strlen(response_text) > 4) {
 
834
            if ((cp = strstr(response_text, " awaits your command")) ||
 
835
                (cp = strstr(response_text, " ready."))) {
 
836
                *cp = '\0';
 
837
            }
 
838
            cp = response_text + 4;
 
839
            if (!strncasecomp(cp, "NetPresenz", 10))
 
840
                server_type = NETPRESENZ_SERVER;
 
841
        } else {
 
842
            cp = response_text;
 
843
        }
 
844
        StrAllocCopy(anchor->server, cp);
 
845
 
 
846
        status = send_cmd_2("USER", (username && *username)
 
847
                            ? username
 
848
                            : "anonymous");
 
849
 
 
850
        if (status == HT_INTERRUPTED) {
 
851
            CTRACE((tfp, "HTFTP: Interrupted while sending username.\n"));
 
852
            _HTProgress (CONNECTION_INTERRUPTED);
 
853
            NETCLOSE(control->socket);
 
854
            control->socket = -1;
 
855
            return HT_INTERRUPTED;
 
856
        }
 
857
    }
 
858
    if (status == 3) {          /* Send password */
 
859
        if (password) {
 
860
            /*
 
861
             * We have non-zero length password, so send it. - FM
 
862
             */
 
863
            HTSprintf0(&command, "PASS %s%c%c", password, CR, LF);
 
864
        } else {
 
865
            /*
 
866
             * Create and send a mail address as the password. - FM
 
867
             */
 
868
            char *user = NULL;
 
869
            CONST char *host = NULL;
 
870
            char * cp;
 
871
 
 
872
            if (personal_mail_address && *personal_mail_address) {
 
873
                /*
 
874
                 * We have a non-zero length personal
 
875
                 * mail address, so use that. - FM
 
876
                 */
 
877
                StrAllocCopy(user, personal_mail_address);
 
878
                if ((cp=strchr(user, '@')) != NULL) {
 
879
                    *cp++ = '\0';
 
880
                    host = cp;
 
881
                } else {
 
882
                    host = HTHostName();
 
883
                }
 
884
            } else {
 
885
                /*
 
886
                 * Use an environment variable and the host global. - FM
 
887
                 */
 
888
                if ((cp=LYGetEnv("USER")) != NULL)
 
889
                    StrAllocCopy(user, cp);
 
890
                else
 
891
                    StrAllocCopy(user, "WWWuser");
 
892
                host = HTHostName();
 
893
            }
 
894
 
 
895
            /*
 
896
             * If host is not fully qualified, suppress it
 
897
             * as ftp.uu.net prefers a blank to a bad name
 
898
             */
 
899
            if (!(host) || strchr(host, '.') == NULL)
 
900
                host = "";
 
901
 
 
902
            HTSprintf0(&command, "PASS %s@%s%c%c", user, host, CR, LF);
 
903
            FREE(user);
 
904
        }
 
905
        status = response(command);
 
906
        FREE(command);
 
907
        if (status == HT_INTERRUPTED) {
 
908
            CTRACE((tfp, "HTFTP: Interrupted while sending password.\n"));
 
909
            _HTProgress (CONNECTION_INTERRUPTED);
 
910
            NETCLOSE(control->socket);
 
911
            control->socket = -1;
 
912
            return HT_INTERRUPTED;
 
913
        }
 
914
    }
 
915
    FREE(username);
 
916
 
 
917
    if (status == 3) {
 
918
        status = send_cmd_1("ACCT noaccount");
 
919
        if (status == HT_INTERRUPTED) {
 
920
            CTRACE((tfp, "HTFTP: Interrupted while sending password.\n"));
 
921
            _HTProgress (CONNECTION_INTERRUPTED);
 
922
            NETCLOSE(control->socket);
 
923
            control->socket = -1;
 
924
            return HT_INTERRUPTED;
 
925
        }
 
926
 
 
927
    }
 
928
    if (status != 2) {
 
929
        CTRACE((tfp, "HTFTP: Login fail: %s", response_text));
 
930
        /* if (control->socket > 0) close_connection(control->socket); */
 
931
        return -1;              /* Bad return */
 
932
    }
 
933
    CTRACE((tfp, "HTFTP: Logged in.\n"));
 
934
 
 
935
    /** Check for host type **/
 
936
    if (server_type != NETPRESENZ_SERVER)
 
937
        server_type = GENERIC_SERVER;   /* reset */
 
938
    use_list = FALSE;                   /* reset */
 
939
    if ((status=response("SYST\r\n")) == 2) {
 
940
        /* we got a line -- what kind of server are we talking to? */
 
941
        if (strncmp(response_text+4,
 
942
                    "UNIX Type: L8 MAC-OS MachTen", 28) == 0) {
 
943
            server_type = MACHTEN_SERVER;
 
944
            use_list = TRUE;
 
945
            CTRACE((tfp, "HTFTP: Treating as MachTen server.\n"));
 
946
 
 
947
        } else if (strstr(response_text+4, "UNIX") != NULL ||
 
948
                   strstr(response_text+4, "Unix") != NULL) {
 
949
            server_type = UNIX_SERVER;
 
950
            unsure_type = FALSE; /* to the best of out knowledge... */
 
951
            use_list = TRUE;
 
952
            CTRACE((tfp, "HTFTP: Treating as Unix server.\n"));
 
953
 
 
954
        } else if (strstr(response_text+4, "MSDOS") != NULL) {
 
955
            server_type = MSDOS_SERVER;
 
956
            use_list = TRUE;
 
957
            CTRACE((tfp, "HTFTP: Treating as MSDOS (Unix emulation) server.\n"));
 
958
 
 
959
        } else if (strncmp(response_text+4, "VMS", 3) == 0) {
 
960
            char *tilde = strstr(arg, "/~");
 
961
            use_list = TRUE;
 
962
            if (tilde != 0
 
963
             && tilde[2] != 0
 
964
             && strstr(response_text+4, "MadGoat") != 0) {
 
965
                server_type = UNIX_SERVER;
 
966
                CTRACE((tfp, "HTFTP: Treating VMS as UNIX server.\n"));
 
967
            } else {
 
968
                server_type = VMS_SERVER;
 
969
                CTRACE((tfp, "HTFTP: Treating as VMS server.\n"));
 
970
            }
 
971
 
 
972
        } else if ((strncmp(response_text+4, "VM/CMS", 6) == 0) ||
 
973
                   (strncmp(response_text+4, "VM ", 3) == 0)) {
 
974
            server_type = CMS_SERVER;
 
975
            use_list = TRUE;
 
976
            CTRACE((tfp, "HTFTP: Treating as CMS server.\n"));
 
977
 
 
978
        } else if (strncmp(response_text+4, "DCTS", 4) == 0) {
 
979
            server_type = DCTS_SERVER;
 
980
            CTRACE((tfp, "HTFTP: Treating as DCTS server.\n"));
 
981
 
 
982
        } else if (strstr(response_text+4, "MAC-OS TCP/Connect II") != NULL) {
 
983
            server_type = TCPC_SERVER;
 
984
            CTRACE((tfp, "HTFTP: Looks like a TCPC server.\n"));
 
985
            get_ftp_pwd(&server_type, &use_list);
 
986
            unsure_type = TRUE;
 
987
 
 
988
        } else if (server_type == NETPRESENZ_SERVER) { /* already set above */
 
989
            use_list = TRUE;
 
990
            set_mac_binary(server_type);
 
991
            CTRACE((tfp, "HTFTP: Treating as NetPresenz (MACOS) server.\n"));
 
992
 
 
993
        } else if (strncmp(response_text+4, "MACOS Peter's Server", 20) == 0) {
 
994
            server_type = PETER_LEWIS_SERVER;
 
995
            use_list = TRUE;
 
996
            set_mac_binary(server_type);
 
997
            CTRACE((tfp, "HTFTP: Treating as Peter Lewis (MACOS) server.\n"));
 
998
 
 
999
        } else if (strncmp(response_text+4, "Windows_NT", 10) == 0) {
 
1000
            server_type = WINDOWS_NT_SERVER;
 
1001
            CTRACE((tfp, "HTFTP: Treating as Window_NT server.\n"));
 
1002
            set_unix_dirstyle(&server_type, &use_list);
 
1003
 
 
1004
        } else if (strncmp(response_text+4, "Windows2000", 11) == 0) {
 
1005
            server_type = WINDOWS_2K_SERVER;
 
1006
            CTRACE((tfp, "HTFTP: Treating as Window_2K server.\n"));
 
1007
            set_unix_dirstyle(&server_type, &use_list);
 
1008
 
 
1009
        } else if (strncmp(response_text+4, "MS Windows", 10) == 0) {
 
1010
            server_type = MS_WINDOWS_SERVER;
 
1011
            use_list = TRUE;
 
1012
            CTRACE((tfp, "HTFTP: Treating as MS Windows server.\n"));
 
1013
 
 
1014
        } else if (strncmp(response_text+4,
 
1015
                           "MACOS AppleShare IP FTP Server", 30) == 0) {
 
1016
            server_type = APPLESHARE_SERVER;
 
1017
            use_list = TRUE;
 
1018
            set_mac_binary(server_type);
 
1019
            CTRACE((tfp, "HTFTP: Treating as AppleShare server.\n"));
 
1020
 
 
1021
        } else  {
 
1022
            server_type = GENERIC_SERVER;
 
1023
            CTRACE((tfp, "HTFTP: Ugh!  A Generic server.\n"));
 
1024
            get_ftp_pwd(&server_type, &use_list);
 
1025
            unsure_type = TRUE;
 
1026
         }
 
1027
    } else {
 
1028
        /* SYST fails :(  try to get the type from the PWD command */
 
1029
         get_ftp_pwd(&server_type, &use_list);
 
1030
    }
 
1031
 
 
1032
/*  Now we inform the server of the port number we will listen on
 
1033
*/
 
1034
#ifdef NOTREPEAT_PORT
 
1035
    {
 
1036
        int status = response(port_command);
 
1037
        if (status != 2) {
 
1038
            if (control->socket)
 
1039
                close_connection(control->socket);
 
1040
            return -status;             /* Bad return */
 
1041
        }
 
1042
        CTRACE((tfp, "HTFTP: Port defined.\n"));
 
1043
    }
 
1044
#endif /* NOTREPEAT_PORT */
 
1045
    return con->socket;                 /* Good return */
 
1046
}
 
1047
 
 
1048
 
 
1049
/*      Close Master (listening) socket
 
1050
**      -------------------------------
 
1051
**
 
1052
**
 
1053
*/
 
1054
PRIVATE int close_master_socket NOARGS
 
1055
{
 
1056
    int status;
 
1057
 
 
1058
    if (master_socket != -1)
 
1059
        FD_CLR(master_socket, &open_sockets);
 
1060
    status = NETCLOSE(master_socket);
 
1061
    CTRACE((tfp, "HTFTP: Closed master socket %d\n", master_socket));
 
1062
    master_socket = -1;
 
1063
    if (status < 0)
 
1064
        return HTInetStatus(gettext("close master socket"));
 
1065
    else
 
1066
        return status;
 
1067
}
 
1068
 
 
1069
 
 
1070
/*      Open a master socket for listening on
 
1071
**      -------------------------------------
 
1072
**
 
1073
**      When data is transferred, we open a port, and wait for the server to
 
1074
**      connect with the data.
 
1075
**
 
1076
** On entry,
 
1077
**      master_socket   Must be negative if not set up already.
 
1078
** On exit,
 
1079
**      Returns         socket number if good
 
1080
**                      less than zero if error.
 
1081
**      master_socket   is socket number if good, else negative.
 
1082
**      port_number     is valid if good.
 
1083
*/
 
1084
PRIVATE int get_listen_socket NOARGS
 
1085
{
 
1086
#ifdef INET6
 
1087
    struct sockaddr_storage soc_address;        /* Binary network address */
 
1088
    struct sockaddr_in* soc_in = (struct sockaddr_in *)&soc_address;
 
1089
    int af;
 
1090
    int slen;
 
1091
#else
 
1092
    struct sockaddr_in soc_address;     /* Binary network address */
 
1093
    struct sockaddr_in* soc_in = &soc_address;
 
1094
#endif /* INET6 */
 
1095
    int new_socket;                     /* Will be master_socket */
 
1096
 
 
1097
 
 
1098
    FD_ZERO(&open_sockets);     /* Clear our record of open sockets */
 
1099
    num_sockets = 0;
 
1100
 
 
1101
#ifndef REPEAT_LISTEN
 
1102
    if (master_socket >= 0)
 
1103
        return master_socket;  /* Done already */
 
1104
#endif /* !REPEAT_LISTEN */
 
1105
 
 
1106
#ifdef INET6
 
1107
    /* query address family of control connection */
 
1108
    slen = sizeof(soc_address);
 
1109
    if (getsockname(control->socket, (struct sockaddr *)&soc_address,
 
1110
                &slen) < 0) {
 
1111
        return HTInetStatus("getsockname failed");
 
1112
    }
 
1113
    af = ((struct sockaddr *)&soc_address)->sa_family;
 
1114
    memset(&soc_address, 0, sizeof(soc_address));
 
1115
#endif /* INET6 */
 
1116
 
 
1117
/*  Create internet socket
 
1118
*/
 
1119
#ifdef INET6
 
1120
    new_socket = socket(af, SOCK_STREAM, IPPROTO_TCP);
 
1121
#else
 
1122
    new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
1123
#endif /* INET6 */
 
1124
 
 
1125
    if (new_socket < 0)
 
1126
        return HTInetStatus(gettext("socket for master socket"));
 
1127
 
 
1128
    CTRACE((tfp, "HTFTP: Opened master socket number %d\n", new_socket));
 
1129
 
 
1130
/*  Search for a free port.
 
1131
*/
 
1132
#ifdef INET6
 
1133
    memset(&soc_address, 0, sizeof(soc_address));
 
1134
    ((struct sockaddr *)&soc_address)->sa_family = af;
 
1135
    switch (af) {
 
1136
    case AF_INET:
 
1137
#ifdef SIN6_LEN
 
1138
        ((struct sockaddr *)&soc_address)->sa_len = sizeof(struct sockaddr_in);
 
1139
#endif /* SIN6_LEN */
 
1140
        break;
 
1141
    case AF_INET6:
 
1142
#ifdef SIN6_LEN
 
1143
        ((struct sockaddr *)&soc_address)->sa_len = sizeof(struct sockaddr_in6);
 
1144
#endif /* SIN6_LEN */
 
1145
        break;
 
1146
    default:
 
1147
        HTInetStatus("AF");
 
1148
    }
 
1149
#else
 
1150
    soc_in->sin_family = AF_INET;           /* Family = internet, host order  */
 
1151
    soc_in->sin_addr.s_addr = INADDR_ANY;   /* Any peer address */
 
1152
#endif /* INET6 */
 
1153
#ifdef POLL_PORTS
 
1154
    {
 
1155
        PortNumber old_port_number = port_number;
 
1156
        for (port_number = (old_port_number+1); ; port_number++) {
 
1157
            int status;
 
1158
            if (port_number > LAST_TCP_PORT)
 
1159
                port_number = FIRST_TCP_PORT;
 
1160
            if (port_number == old_port_number) {
 
1161
                return HTInetStatus("bind");
 
1162
            }
 
1163
#ifdef INET6
 
1164
            soc_in->sin_port = htons(port_number);
 
1165
#else
 
1166
            soc_address.sin_port = htons(port_number);
 
1167
#endif /* INET6 */
 
1168
#ifdef SOCKS
 
1169
            if (socks_flag)
 
1170
                if ((status=Rbind(new_socket,
 
1171
                        (struct sockaddr*)&soc_address,
 
1172
                            /* Cast to generic sockaddr */
 
1173
                        SOCKADDR_LEN(soc_address)
 
1174
#ifndef SHORTENED_RBIND
 
1175
                        ,socks_bind_remoteAddr
 
1176
#endif /* !SHORTENED_RBIND */
 
1177
                                                )) == 0) {
 
1178
                    break;
 
1179
                } else
 
1180
#endif /* SOCKS */
 
1181
            if ((status=bind(new_socket,
 
1182
                    (struct sockaddr*)&soc_address,
 
1183
                            /* Cast to generic sockaddr */
 
1184
                    SOCKADDR_LEN(soc_address)
 
1185
                    )) == 0) {
 
1186
                break;
 
1187
            }
 
1188
            CTRACE((tfp, "TCP bind attempt to port %d yields %d, errno=%d\n",
 
1189
                port_number, status, SOCKET_ERRNO));
 
1190
        } /* for */
 
1191
    }
 
1192
#else
 
1193
    {
 
1194
        int status;
 
1195
        int address_length = sizeof(soc_address);
 
1196
#ifdef SOCKS
 
1197
        if (socks_flag)
 
1198
            status = Rgetsockname(control->socket,
 
1199
                                  (struct sockaddr *)&soc_address,
 
1200
                                  (void *)&address_length);
 
1201
        else
 
1202
#endif /* SOCKS */
 
1203
        status = getsockname(control->socket,
 
1204
                             (struct sockaddr *)&soc_address,
 
1205
                             (void *)&address_length);
 
1206
        if (status<0) return HTInetStatus("getsockname");
 
1207
#ifdef INET6
 
1208
        CTRACE((tfp, "HTFTP: This host is %s\n",
 
1209
            HTInetString((SockA *)soc_in)));
 
1210
 
 
1211
        soc_in->sin_port = 0;   /* Unspecified: please allocate */
 
1212
#else
 
1213
        CTRACE((tfp, "HTFTP: This host is %s\n",
 
1214
            HTInetString(soc_in)));
 
1215
 
 
1216
        soc_address.sin_port = 0;       /* Unspecified: please allocate */
 
1217
#endif /* INET6 */
 
1218
#ifdef SOCKS
 
1219
        if (socks_flag)
 
1220
            status=Rbind(new_socket,
 
1221
                         (struct sockaddr*)&soc_address,
 
1222
                         /* Cast to generic sockaddr */
 
1223
                         sizeof(soc_address)
 
1224
#ifndef SHORTENED_RBIND
 
1225
#ifdef INET6
 
1226
                        socks_bind_remoteAddr
 
1227
#else
 
1228
                        ,socks_bind_remoteAddr
 
1229
#endif /* INET6 */
 
1230
#endif /* !SHORTENED_RBIND */
 
1231
                                                );
 
1232
        else
 
1233
#endif /* SOCKS */
 
1234
        status=bind(new_socket,
 
1235
                    (struct sockaddr*)&soc_address,
 
1236
                    /* Cast to generic sockaddr */
 
1237
                    SOCKADDR_LEN(soc_address)
 
1238
                    );
 
1239
        if (status<0) return HTInetStatus("bind");
 
1240
 
 
1241
        address_length = sizeof(soc_address);
 
1242
#ifdef SOCKS
 
1243
        if (socks_flag)
 
1244
            status = Rgetsockname(new_socket,
 
1245
                                  (struct sockaddr*)&soc_address,
 
1246
                                  (void *)&address_length);
 
1247
        else
 
1248
#endif /* SOCKS */
 
1249
        status = getsockname(new_socket,
 
1250
                             (struct sockaddr*)&soc_address,
 
1251
                             (void *)&address_length);
 
1252
        if (status<0) return HTInetStatus("getsockname");
 
1253
    }
 
1254
#endif /* POLL_PORTS */
 
1255
 
 
1256
#ifdef INET6
 
1257
    CTRACE((tfp, "HTFTP: bound to port %d on %s\n",
 
1258
                (int)ntohs(soc_in->sin_port),
 
1259
                HTInetString((SockA *)soc_in)));
 
1260
#else
 
1261
    CTRACE((tfp, "HTFTP: bound to port %d on %s\n",
 
1262
                (int)ntohs(soc_in->sin_port),
 
1263
                HTInetString(soc_in)));
 
1264
#endif /* INET6 */
 
1265
 
 
1266
#ifdef REPEAT_LISTEN
 
1267
    if (master_socket >= 0)
 
1268
        (void) close_master_socket();
 
1269
#endif /* REPEAT_LISTEN */
 
1270
 
 
1271
    master_socket = new_socket;
 
1272
 
 
1273
/*      Now we must find out who we are to tell the other guy
 
1274
*/
 
1275
    (void)HTHostName();         /* Make address valid - doesn't work*/
 
1276
#ifdef INET6
 
1277
    switch (((struct sockaddr *)&soc_address)->sa_family) {
 
1278
    case AF_INET:
 
1279
#endif /* INET6 */
 
1280
        sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
 
1281
                    (int)*((unsigned char *)(&soc_in->sin_addr)+0),
 
1282
                    (int)*((unsigned char *)(&soc_in->sin_addr)+1),
 
1283
                    (int)*((unsigned char *)(&soc_in->sin_addr)+2),
 
1284
                    (int)*((unsigned char *)(&soc_in->sin_addr)+3),
 
1285
                    (int)*((unsigned char *)(&soc_in->sin_port)+0),
 
1286
                    (int)*((unsigned char *)(&soc_in->sin_port)+1),
 
1287
                    CR, LF);
 
1288
 
 
1289
#ifdef INET6
 
1290
        break;
 
1291
 
 
1292
    case AF_INET6:
 
1293
      {
 
1294
        char hostbuf[MAXHOSTNAMELEN];
 
1295
        char portbuf[MAXHOSTNAMELEN];
 
1296
        getnameinfo((struct sockaddr *)&soc_address,
 
1297
            SOCKADDR_LEN(soc_address),
 
1298
            hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf),
 
1299
            NI_NUMERICHOST | NI_NUMERICSERV);
 
1300
        sprintf(port_command, "EPRT |%d|%s|%s|%c%c", 2, hostbuf, portbuf,
 
1301
                CR, LF);
 
1302
        break;
 
1303
      }
 
1304
    default:
 
1305
        sprintf(port_command, "JUNK%c%c", CR, LF);
 
1306
        break;
 
1307
    }
 
1308
#endif /* INET6 */
 
1309
 
 
1310
    /*  Inform TCP that we will accept connections
 
1311
    */
 
1312
    {
 
1313
        int status;
 
1314
#ifdef SOCKS
 
1315
        if (socks_flag)
 
1316
            status = Rlisten(master_socket, 1);
 
1317
        else
 
1318
#endif /* SOCKS */
 
1319
            status = listen(master_socket, 1);
 
1320
        if (status < 0) {
 
1321
            master_socket = -1;
 
1322
            return HTInetStatus("listen");
 
1323
        }
 
1324
    }
 
1325
    CTRACE((tfp, "TCP: Master socket(), bind() and listen() all OK\n"));
 
1326
    FD_SET(master_socket, &open_sockets);
 
1327
    if ((master_socket+1) > num_sockets)
 
1328
        num_sockets = master_socket+1;
 
1329
 
 
1330
    return master_socket;               /* Good */
 
1331
 
 
1332
} /* get_listen_socket */
 
1333
 
 
1334
PRIVATE char * months[12] = {
 
1335
    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
 
1336
};
 
1337
 
 
1338
/*      Procedure: Set the current and last year strings and date integer
 
1339
**      -----------------------------------------------------------------
 
1340
**
 
1341
**      Bug:
 
1342
**              This code is for sorting listings by date, if that option
 
1343
**              is selected in Lynx, and doesn't take into account time
 
1344
**              zones or ensure resetting at midnight, so the sort may not
 
1345
**              be perfect, but the actual date isn't changed in the display,
 
1346
**              i.e., the date is still correct. - FM
 
1347
*/
 
1348
PRIVATE void set_years_and_date NOARGS
 
1349
{
 
1350
    char day[8], month[8], date[12];
 
1351
    time_t NowTime;
 
1352
    int i;
 
1353
 
 
1354
    NowTime = time(NULL);
 
1355
    strncpy(day, (char *)ctime(&NowTime)+8, 2);
 
1356
    day[2] = '\0';
 
1357
    if (day[0] == ' ') {
 
1358
        day[0] = '0';
 
1359
    }
 
1360
    strncpy(month, (char *)ctime(&NowTime)+4, 3);
 
1361
    month[3] = '\0';
 
1362
    for (i = 0; i < 12; i++) {
 
1363
        if (!strcasecomp(month, months[i])) {
 
1364
            break;
 
1365
        }
 
1366
    }
 
1367
    i++;
 
1368
    sprintf(date, "9999%02d%.2s", i, day);
 
1369
    TheDate = atoi(date);
 
1370
    strcpy(ThisYear, (char *)ctime(&NowTime)+20);
 
1371
    ThisYear[4] = '\0';
 
1372
    sprintf(LastYear, "%d", (atoi(ThisYear) - 1));
 
1373
    HaveYears = TRUE;
 
1374
}
 
1375
 
 
1376
typedef struct _EntryInfo {
 
1377
    char *       filename;
 
1378
    char *       type;
 
1379
    char *       date;
 
1380
    unsigned int size;
 
1381
    BOOLEAN      display;  /* show this entry? */
 
1382
} EntryInfo;
 
1383
 
 
1384
PRIVATE void free_entryinfo_struct_contents ARGS1(
 
1385
        EntryInfo *,    entry_info)
 
1386
{
 
1387
    if (entry_info) {
 
1388
        FREE(entry_info->filename);
 
1389
        FREE(entry_info->type);
 
1390
        FREE(entry_info->date);
 
1391
    }
 
1392
   /* dont free the struct */
 
1393
}
 
1394
 
 
1395
/*
 
1396
 * is_ls_date() --
 
1397
 *      Return TRUE if s points to a string of the form:
 
1398
 *              "Sep  1  1990 " or
 
1399
 *              "Sep 11 11:59 " or
 
1400
 *              "Dec 12 1989  " or
 
1401
 *              "FCv 23 1990  " ...
 
1402
 */
 
1403
PRIVATE BOOLEAN is_ls_date ARGS1(
 
1404
        char *,         s)
 
1405
{
 
1406
    /* must start with three alpha characters */
 
1407
    if (!isalpha(UCH(*s++)) || !isalpha(UCH(*s++)) || !isalpha(UCH(*s++)))
 
1408
        return FALSE;
 
1409
 
 
1410
    /* space or HT_NON_BREAK_SPACE */
 
1411
    if (!(*s == ' ' || *s == HT_NON_BREAK_SPACE)) {
 
1412
        s++;
 
1413
        return FALSE;
 
1414
    }
 
1415
    s++;
 
1416
 
 
1417
    /* space or digit */
 
1418
    if (!(*s == ' ' || isdigit(UCH(*s)))) {
 
1419
        s++;
 
1420
        return FALSE;
 
1421
    }
 
1422
    s++;
 
1423
 
 
1424
    /* digit */
 
1425
    if (!isdigit(UCH(*s++)))
 
1426
        return FALSE;
 
1427
 
 
1428
    /* space */
 
1429
    if (*s++ != ' ')
 
1430
        return FALSE;
 
1431
 
 
1432
    /* space or digit */
 
1433
    if (!(*s == ' ' || isdigit(UCH(*s)))) {
 
1434
        s++;
 
1435
        return FALSE;
 
1436
    }
 
1437
    s++;
 
1438
 
 
1439
    /* digit */
 
1440
    if (!isdigit(UCH(*s++)))
 
1441
        return FALSE;
 
1442
 
 
1443
    /* colon or digit */
 
1444
    if (!(*s == ':' || isdigit(UCH(*s)))) {
 
1445
        s++;
 
1446
        return FALSE;
 
1447
    }
 
1448
    s++;
 
1449
 
 
1450
    /* digit */
 
1451
    if (!isdigit(UCH(*s++)))
 
1452
        return FALSE;
 
1453
 
 
1454
    /* space or digit */
 
1455
    if (!(*s == ' ' || isdigit(UCH(*s)))) {
 
1456
        s++;
 
1457
        return FALSE;
 
1458
    }
 
1459
    s++;
 
1460
 
 
1461
    /* space */
 
1462
    if (*s++ != ' ')
 
1463
        return FALSE;
 
1464
 
 
1465
    return TRUE;
 
1466
} /* is_ls_date() */
 
1467
 
 
1468
/*
 
1469
 *  parse_eplf_line() --
 
1470
 *      Extract the name, size, and date from an EPLF line. - 08-06-96 DJB
 
1471
 */
 
1472
PRIVATE void parse_eplf_line ARGS2(
 
1473
        char *,         line,
 
1474
        EntryInfo *,    info)
 
1475
{
 
1476
    char *cp = line;
 
1477
    char ct[26];
 
1478
    unsigned long size;
 
1479
    time_t secs;
 
1480
    static time_t base; /* time() value on this OS in 1970 */
 
1481
    static int flagbase = 0;
 
1482
 
 
1483
    if (!flagbase) {
 
1484
        struct tm t;
 
1485
        t.tm_year = 70; t.tm_mon = 0; t.tm_mday = 0;
 
1486
        t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0;
 
1487
        t.tm_isdst = -1;
 
1488
        base = mktime(&t); /* could return -1 */
 
1489
        flagbase = 1;
 
1490
    }
 
1491
 
 
1492
    while (*cp) {
 
1493
        switch(*cp) {
 
1494
            case '\t':
 
1495
                StrAllocCopy(info->filename, cp + 1);
 
1496
                return;
 
1497
            case 's':
 
1498
                size = 0;
 
1499
                while (*(++cp) && (*cp != ','))
 
1500
                    size = (size * 10) + (*cp - '0');
 
1501
                info->size = size;
 
1502
                break;
 
1503
            case 'm':
 
1504
                secs = 0;
 
1505
                while (*(++cp) && (*cp != ','))
 
1506
                    secs = (secs * 10) + (*cp - '0');
 
1507
                secs += base; /* assumes that time_t is #seconds */
 
1508
                strcpy(ct, ctime(&secs));
 
1509
                ct[24] = 0;
 
1510
                StrAllocCopy(info->date, ct);
 
1511
                break;
 
1512
            case '/':
 
1513
                StrAllocCopy(info->type, ENTRY_IS_DIRECTORY);
 
1514
                /* FALLTHRU */
 
1515
            default:
 
1516
                while (*cp) {
 
1517
                    if (*cp++ == ',')
 
1518
                      break;
 
1519
                }
 
1520
                break;
 
1521
        }
 
1522
    }
 
1523
} /* parse_eplf_line */
 
1524
 
 
1525
/*
 
1526
 * parse_ls_line() --
 
1527
 *      Extract the name, size, and date from an ls -l line.
 
1528
 */
 
1529
PRIVATE void parse_ls_line ARGS2(
 
1530
        char *,         line,
 
1531
        EntryInfo *,    entry_info)
 
1532
{
 
1533
    int    i, j;
 
1534
    int    base=1;
 
1535
    int    size_num=0;
 
1536
 
 
1537
    for (i = strlen(line) - 1;
 
1538
         (i > 13) && (!isspace(UCH(line[i])) || !is_ls_date(&line[i-12])); i--)
 
1539
        ; /* null body */
 
1540
    line[i] = '\0';
 
1541
    if (i > 13) {
 
1542
        StrAllocCopy(entry_info->date, &line[i-12]);
 
1543
        /* replace the 4th location with nbsp if it is a space or zero */
 
1544
        if (entry_info->date[4] == ' ' || entry_info->date[4] == '0')
 
1545
            entry_info->date[4] = HT_NON_BREAK_SPACE;
 
1546
        /* make sure year or time is flush right */
 
1547
        if (entry_info->date[11] == ' ') {
 
1548
            for (j = 11; j > 6; j--) {
 
1549
                entry_info->date[j] = entry_info->date[j-1];
 
1550
            }
 
1551
        }
 
1552
    }
 
1553
    j = i - 14;
 
1554
    while (isdigit(UCH(line[j]))) {
 
1555
        size_num += (line[j] - '0') * base;
 
1556
        base *= 10;
 
1557
        j--;
 
1558
    }
 
1559
    entry_info->size = size_num;
 
1560
    StrAllocCopy(entry_info->filename, &line[i + 1]);
 
1561
} /* parse_ls_line() */
 
1562
 
 
1563
/*
 
1564
 * parse_dls_line() --
 
1565
 *      Extract the name and size info and whether it refers to a
 
1566
 *      directory from a LIST line in "dls" format.
 
1567
 */
 
1568
PRIVATE void parse_dls_line ARGS3(
 
1569
        char *,         line,
 
1570
        EntryInfo *,    entry_info,
 
1571
        char **,        pspilledname)
 
1572
{
 
1573
    short  j;
 
1574
    int    base=1;
 
1575
    int    size_num=0;
 
1576
    int    len;
 
1577
    char *cps = NULL;
 
1578
 
 
1579
    /* README              763  Information about this server\0
 
1580
       bin/                  -  \0
 
1581
       etc/                  =  \0
 
1582
       ls-lR                 0  \0
 
1583
       ls-lR.Z               3  \0
 
1584
       pub/                  =  Public area\0
 
1585
       usr/                  -  \0
 
1586
       morgan               14  -> ../real/morgan\0
 
1587
       TIMIT.mostlikely.Z\0
 
1588
                         79215  \0
 
1589
        */
 
1590
 
 
1591
    len = strlen(line);
 
1592
    if (len == 0) {
 
1593
        FREE(*pspilledname);
 
1594
        entry_info->display = FALSE;
 
1595
        return;
 
1596
    }
 
1597
    cps = LYSkipNonBlanks(line);
 
1598
    if (*cps == '\0') {         /* only a filename, save it and return. */
 
1599
        StrAllocCopy(*pspilledname, line);
 
1600
        entry_info->display = FALSE;
 
1601
        return;
 
1602
    }
 
1603
    if (len < 24 || line[23] != ' ' ||
 
1604
        (isspace(UCH(line[0])) && !*pspilledname)) {
 
1605
        /* this isn't the expected "dls" format! */
 
1606
        if (!isspace(UCH(line[0])))
 
1607
            *cps = '\0';
 
1608
        if (*pspilledname && !*line) {
 
1609
            entry_info->filename = *pspilledname;
 
1610
            *pspilledname = NULL;
 
1611
            if (entry_info->filename[strlen(entry_info->filename)-1] == '/')
 
1612
                StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
1613
            else
 
1614
                StrAllocCopy(entry_info->type, "");
 
1615
        } else {
 
1616
            StrAllocCopy(entry_info->filename, line);
 
1617
            if (cps && cps != line && *(cps-1) == '/')
 
1618
                StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
1619
            else
 
1620
                StrAllocCopy(entry_info->type, "");
 
1621
            FREE(*pspilledname);
 
1622
        }
 
1623
        return;
 
1624
    }
 
1625
 
 
1626
    j = 22;
 
1627
    if (line[j] == '=' || line[j] == '-') {
 
1628
        StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
1629
    } else {
 
1630
        while (isdigit(UCH(line[j]))) {
 
1631
            size_num += (line[j] - '0') * base;
 
1632
            base *= 10;
 
1633
            j--;
 
1634
        }
 
1635
    }
 
1636
    entry_info->size = size_num;
 
1637
 
 
1638
    cps = LYSkipBlanks(&line[23]);
 
1639
    if (!strncmp(cps, "-> ", 3) && cps[3] != '\0' && cps[3] != ' ') {
 
1640
        StrAllocCopy(entry_info->type, gettext("Symbolic Link"));
 
1641
        entry_info->size = 0;   /* don't display size */
 
1642
    }
 
1643
 
 
1644
    if (j > 0)
 
1645
        line[j] = '\0';
 
1646
 
 
1647
    LYTrimTrailing(line);
 
1648
 
 
1649
    len = strlen(line);
 
1650
    if (len == 0 && *pspilledname && **pspilledname) {
 
1651
        line = *pspilledname;
 
1652
        len = strlen(*pspilledname);
 
1653
    }
 
1654
    if (len > 0 && line[len-1] == '/') {
 
1655
                /*
 
1656
                **  It's a dir, remove / and mark it as such.
 
1657
                */
 
1658
        if (len > 1)
 
1659
            line[len-1] = '\0';
 
1660
        if (!entry_info->type)
 
1661
            StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
1662
    }
 
1663
 
 
1664
    StrAllocCopy(entry_info->filename, line);
 
1665
    FREE(*pspilledname);
 
1666
} /* parse_dls_line() */
 
1667
 
 
1668
/*
 
1669
 * parse_vms_dir_entry()
 
1670
 *      Format the name, date, and size from a VMS LIST line
 
1671
 *      into the EntryInfo structure - FM
 
1672
 */
 
1673
PRIVATE void parse_vms_dir_entry ARGS2(
 
1674
        char *,         line,
 
1675
        EntryInfo *,    entry_info)
 
1676
{
 
1677
    int i, j;
 
1678
    unsigned int ialloc;
 
1679
    char *cp, *cpd, *cps, date[16], *sp = " ";
 
1680
 
 
1681
    /**  Get rid of blank lines, and information lines.  **/
 
1682
    /**  Valid lines have the ';' version number token.  **/
 
1683
    if (!strlen(line) || (cp = strchr(line, ';')) == NULL) {
 
1684
        entry_info->display = FALSE;
 
1685
        return;
 
1686
    }
 
1687
 
 
1688
    /** Cut out file or directory name at VMS version number. **/
 
1689
    *cp++ ='\0';
 
1690
    StrAllocCopy(entry_info->filename,line);
 
1691
 
 
1692
    /** Cast VMS non-README file and directory names to lowercase. **/
 
1693
    if (strstr(entry_info->filename, "READ") == NULL) {
 
1694
        LYLowerCase(entry_info->filename);
 
1695
        i = strlen(entry_info->filename);
 
1696
    } else {
 
1697
        i = ((strstr(entry_info->filename, "READ") - entry_info->filename) + 4);
 
1698
        if (!strncmp(&entry_info->filename[i], "ME", 2)) {
 
1699
            i += 2;
 
1700
            while (entry_info->filename[i] && entry_info->filename[i] != '.') {
 
1701
                i++;
 
1702
            }
 
1703
        } else if (!strncmp(&entry_info->filename[i], ".ME", 3)) {
 
1704
            i = strlen(entry_info->filename);
 
1705
        } else {
 
1706
            i = 0;
 
1707
        }
 
1708
        LYLowerCase(entry_info->filename + i);
 
1709
    }
 
1710
 
 
1711
    /** Uppercase terminal .z's or _z's. **/
 
1712
    if ((--i > 2) &&
 
1713
        entry_info->filename[i] == 'z' &&
 
1714
        (entry_info->filename[i-1] == '.' ||
 
1715
         entry_info->filename[i-1] == '_'))
 
1716
        entry_info->filename[i] = 'Z';
 
1717
 
 
1718
    /** Convert any tabs in rest of line to spaces. **/
 
1719
    cps = cp-1;
 
1720
    while ((cps=strchr(cps+1, '\t')) != NULL)
 
1721
        *cps = ' ';
 
1722
 
 
1723
    /** Collapse serial spaces. **/
 
1724
    i = 0; j = 1;
 
1725
    cps = cp;
 
1726
    while (cps[j] != '\0') {
 
1727
        if (cps[i] == ' ' && cps[j] == ' ')
 
1728
            j++;
 
1729
        else
 
1730
            cps[++i] = cps[j++];
 
1731
    }
 
1732
    cps[++i] = '\0';
 
1733
 
 
1734
    /* Set the years and date, if we don't have them yet. **/
 
1735
    if (!HaveYears) {
 
1736
        set_years_and_date();
 
1737
    }
 
1738
 
 
1739
    /** Track down the date. **/
 
1740
    if ((cpd=strchr(cp, '-')) != NULL &&
 
1741
        strlen(cpd) > 9 && isdigit(UCH(*(cpd-1))) &&
 
1742
        isalpha(UCH(*(cpd+1))) && *(cpd+4) == '-') {
 
1743
 
 
1744
        /** Month **/
 
1745
        *(cpd+2) = (char) TOLOWER(*(cpd+2));
 
1746
        *(cpd+3) = (char) TOLOWER(*(cpd+3));
 
1747
        sprintf(date, "%.3s ", cpd+1);
 
1748
 
 
1749
        /** Day **/
 
1750
        if (isdigit(UCH(*(cpd-2))))
 
1751
            sprintf(date+4, "%.2s ", cpd-2);
 
1752
        else
 
1753
            sprintf(date+4, "%c%.1s ", HT_NON_BREAK_SPACE, cpd-1);
 
1754
 
 
1755
        /** Time or Year **/
 
1756
        if (!strncmp(ThisYear, cpd+5, 4) &&
 
1757
            strlen(cpd) > 15 && *(cpd+12) == ':') {
 
1758
            sprintf(date+7, "%.5s", cpd+10);
 
1759
        } else {
 
1760
            sprintf(date+7, " %.4s", cpd+5);
 
1761
        }
 
1762
 
 
1763
        StrAllocCopy(entry_info->date, date);
 
1764
    }
 
1765
 
 
1766
    /** Track down the size **/
 
1767
    if ((cpd=strchr(cp, '/')) != NULL) {
 
1768
        /* Appears be in used/allocated format */
 
1769
        cps = cpd;
 
1770
        while (isdigit(UCH(*(cps-1))))
 
1771
            cps--;
 
1772
        if (cps < cpd)
 
1773
            *cpd = '\0';
 
1774
        entry_info->size = atoi(cps);
 
1775
        cps = cpd+1;
 
1776
        while (isdigit(UCH(*cps)))
 
1777
            cps++;
 
1778
        *cps = '\0';
 
1779
        ialloc = atoi(cpd+1);
 
1780
        /* Check if used is in blocks or bytes */
 
1781
        if (entry_info->size <= ialloc)
 
1782
            entry_info->size *= 512;
 
1783
 
 
1784
    } else if ((cps=strtok(cp, sp)) != NULL) {
 
1785
        /* We just initialized on the version number */
 
1786
        /* Now let's hunt for a lone, size number    */
 
1787
        while ((cps=strtok(NULL, sp)) != NULL) {
 
1788
            cpd = cps;
 
1789
            while (isdigit(UCH(*cpd)))
 
1790
                cpd++;
 
1791
            if (*cpd == '\0') {
 
1792
                /* Assume it's blocks */
 
1793
                entry_info->size = atoi(cps) * 512;
 
1794
                break;
 
1795
            }
 
1796
        }
 
1797
    }
 
1798
 
 
1799
    /** Wrap it up **/
 
1800
    CTRACE((tfp, "HTFTP: VMS filename: %s  date: %s  size: %d\n",
 
1801
                entry_info->filename,
 
1802
                NonNull(entry_info->date),
 
1803
                entry_info->size));
 
1804
    return;
 
1805
} /* parse_vms_dir_entry() */
 
1806
 
 
1807
/*
 
1808
 * parse_ms_windows_dir_entry() --
 
1809
 *      Format the name, date, and size from an MS_WINDOWS LIST line into
 
1810
 *      the EntryInfo structure (assumes Chameleon NEWT format). - FM
 
1811
 */
 
1812
PRIVATE void parse_ms_windows_dir_entry ARGS2(
 
1813
        char *,         line,
 
1814
        EntryInfo *,    entry_info)
 
1815
{
 
1816
    char *cp = line;
 
1817
    char *cps, *cpd, date[16];
 
1818
    char *end = line + strlen(line);
 
1819
 
 
1820
    /**  Get rid of blank or junk lines.  **/
 
1821
    cp = LYSkipBlanks(cp);
 
1822
    if (!(*cp)) {
 
1823
        entry_info->display = FALSE;
 
1824
        return;
 
1825
    }
 
1826
 
 
1827
    /** Cut out file or directory name. **/
 
1828
    cps = LYSkipNonBlanks(cp);
 
1829
    *cps++ ='\0';
 
1830
    cpd = cps;
 
1831
    StrAllocCopy(entry_info->filename, cp);
 
1832
 
 
1833
    /** Track down the size **/
 
1834
    if (cps < end) {
 
1835
        cps = LYSkipBlanks(cps);
 
1836
        cpd = LYSkipNonBlanks(cps);
 
1837
        *cpd++ = '\0';
 
1838
        if (isdigit(UCH(*cps))) {
 
1839
            entry_info->size = atoi(cps);
 
1840
        } else {
 
1841
            StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
1842
        }
 
1843
    } else {
 
1844
        StrAllocCopy(entry_info->type, "");
 
1845
    }
 
1846
 
 
1847
    /* Set the years and date, if we don't have them yet. **/
 
1848
    if (!HaveYears) {
 
1849
        set_years_and_date();
 
1850
    }
 
1851
 
 
1852
    /** Track down the date. **/
 
1853
    if (cpd < end) {
 
1854
        cpd = LYSkipBlanks(cpd);
 
1855
        if (strlen(cpd) > 17) {
 
1856
            *(cpd+6)  = '\0';  /* Month and Day */
 
1857
            *(cpd+11) = '\0';  /* Year */
 
1858
            *(cpd+17) = '\0';  /* Time */
 
1859
            if (strcmp(ThisYear, cpd+7))
 
1860
                /* Not this year, so show the year */
 
1861
                sprintf(date, "%.6s  %.4s", cpd, (cpd+7));
 
1862
            else
 
1863
                /* Is this year, so show the time */
 
1864
                sprintf(date, "%.6s %.5s", cpd, (cpd+12));
 
1865
            StrAllocCopy(entry_info->date, date);
 
1866
            if (entry_info->date[4] == ' '|| entry_info->date[4] == '0') {
 
1867
                entry_info->date[4] = HT_NON_BREAK_SPACE;
 
1868
            }
 
1869
        }
 
1870
    }
 
1871
 
 
1872
    /** Wrap it up **/
 
1873
    CTRACE((tfp, "HTFTP: MS Windows filename: %s  date: %s  size: %d\n",
 
1874
                entry_info->filename,
 
1875
                NonNull(entry_info->date),
 
1876
                entry_info->size));
 
1877
    return;
 
1878
} /* parse_ms_windows_dir_entry */
 
1879
 
 
1880
/*
 
1881
 * parse_windows_nt_dir_entry() --
 
1882
 *      Format the name, date, and size from a WINDOWS_NT LIST line into
 
1883
 *      the EntryInfo structure (assumes Chameleon NEWT format). - FM
 
1884
 */
 
1885
#ifdef NOTDEFINED
 
1886
PRIVATE void parse_windows_nt_dir_entry ARGS2(
 
1887
        char *,         line,
 
1888
        EntryInfo *,    entry_info)
 
1889
{
 
1890
    char *cp = line;
 
1891
    char *cps, *cpd, date[16];
 
1892
    char *end = line + strlen(line);
 
1893
    int i;
 
1894
 
 
1895
    /**  Get rid of blank or junk lines.  **/
 
1896
    cp = LYSkipBlanks(cp);
 
1897
    if (!(*cp)) {
 
1898
        entry_info->display = FALSE;
 
1899
        return;
 
1900
    }
 
1901
 
 
1902
    /** Cut out file or directory name. **/
 
1903
    cpd = cp;
 
1904
    cps = LYSkipNonBlanks(end-1);
 
1905
    cp = (cps+1);
 
1906
    if (!strcmp(cp, ".") || !strcmp(cp, "..")) {
 
1907
        entry_info->display = FALSE;
 
1908
        return;
 
1909
    }
 
1910
    StrAllocCopy(entry_info->filename, cp);
 
1911
    if (cps < cpd)
 
1912
        return;
 
1913
    *cp = '\0';
 
1914
    end = cp;
 
1915
 
 
1916
    /* Set the years and date, if we don't have them yet. **/
 
1917
    if (!HaveYears) {
 
1918
        set_years_and_date();
 
1919
    }
 
1920
 
 
1921
    /** Cut out the date. **/
 
1922
    cp = cps = cpd;
 
1923
    cps = LYSkipNonBlanks(cps);
 
1924
    *cps++ ='\0';
 
1925
    if (cps > end) {
 
1926
        entry_info->display = FALSE;
 
1927
        return;
 
1928
    }
 
1929
    cps = LYSkipBlanks(cps);
 
1930
    cpd = LYSkipNonBlanks(cps);
 
1931
    *cps++ ='\0';
 
1932
    if (cps > end || cpd == cps || strlen(cpd) < 7) {
 
1933
        entry_info->display = FALSE;
 
1934
        return;
 
1935
    }
 
1936
    if (strlen(cp) == 8 &&
 
1937
        isdigit(*cp) && isdigit(*(cp+1)) && *(cp+2) == '-' &&
 
1938
        isdigit(*(cp+3)) && isdigit(*(cp+4)) && *(cp+5) == '-') {
 
1939
        *(cp+2)  = '\0';        /* Month */
 
1940
        i = atoi(cp) - 1;
 
1941
        *(cp+5) = '\0';         /* Day */
 
1942
        sprintf(date, "%.3s %.2s", months[i], (cp+3));
 
1943
        if (date[4] == '0')
 
1944
            date[4] = ' ';
 
1945
        cp += 6;                        /* Year */
 
1946
        if (strcmp((ThisYear+2), cp)) {
 
1947
            /* Not this year, so show the year */
 
1948
            if (atoi(cp) < 70) {
 
1949
                sprintf(&date[6], "  20%.2s", cp);
 
1950
            } else {
 
1951
                sprintf(&date[6], "  19%.2s", cp);
 
1952
            }
 
1953
        } else {
 
1954
            /* Is this year, so show the time */
 
1955
            *(cpd+2) = '\0';    /* Hour */
 
1956
            i = atoi(cpd);
 
1957
            if (*(cpd+5) == 'P' || *(cpd+5) == 'p')
 
1958
                i += 12;
 
1959
            sprintf(&date[6], " %02d:%.2s", i, (cpd+3));
 
1960
        }
 
1961
        StrAllocCopy(entry_info->date, date);
 
1962
        if (entry_info->date[4] == ' '|| entry_info->date[4] == '0') {
 
1963
            entry_info->date[4] = HT_NON_BREAK_SPACE;
 
1964
        }
 
1965
    }
 
1966
 
 
1967
    /** Track down the size **/
 
1968
    if (cps < end) {
 
1969
        cps = LYSkipBlanks(cps);
 
1970
        cpd = LYSkipNonBlanks(cps);
 
1971
        *cpd = '\0';
 
1972
        if (isdigit(*cps)) {
 
1973
            entry_info->size = atoi(cps);
 
1974
        } else {
 
1975
            StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
1976
        }
 
1977
    } else {
 
1978
        StrAllocCopy(entry_info->type, "");
 
1979
    }
 
1980
 
 
1981
    /** Wrap it up **/
 
1982
    CTRACE((tfp, "HTFTP: Windows NT filename: %s  date: %s  size: %d\n",
 
1983
                entry_info->filename,
 
1984
                NonNull(entry_info->date),
 
1985
                entry_info->size));
 
1986
    return;
 
1987
} /* parse_windows_nt_dir_entry */
 
1988
#endif /* NOTDEFINED */
 
1989
 
 
1990
/*
 
1991
 * parse_cms_dir_entry() --
 
1992
 *      Format the name, date, and size from a VM/CMS line into
 
1993
 *      the EntryInfo structure. - FM
 
1994
 */
 
1995
PRIVATE void parse_cms_dir_entry ARGS2(
 
1996
        char *,         line,
 
1997
        EntryInfo *,    entry_info)
 
1998
{
 
1999
    char *cp = line;
 
2000
    char *cps, *cpd, date[16];
 
2001
    char *end = line + strlen(line);
 
2002
    int RecordLength = 0;
 
2003
    int Records = 0;
 
2004
    int i;
 
2005
 
 
2006
    /**  Get rid of blank or junk lines.  **/
 
2007
    cp = LYSkipBlanks(cp);
 
2008
    if (!(*cp)) {
 
2009
        entry_info->display = FALSE;
 
2010
        return;
 
2011
    }
 
2012
 
 
2013
    /** Cut out file or directory name. **/
 
2014
    cps = LYSkipNonBlanks(cp);
 
2015
    *cps++ ='\0';
 
2016
    StrAllocCopy(entry_info->filename, cp);
 
2017
    if (strchr(entry_info->filename, '.') != NULL)
 
2018
        /** If we already have a dot, we did an NLST. **/
 
2019
        return;
 
2020
    cp = LYSkipBlanks(cps);
 
2021
    if (!(*cp)) {
 
2022
        /** If we don't have more, we've misparsed. **/
 
2023
        FREE(entry_info->filename);
 
2024
        FREE(entry_info->type);
 
2025
        entry_info->display = FALSE;
 
2026
        return;
 
2027
    }
 
2028
    cps = LYSkipNonBlanks(cp);
 
2029
    *cps++ ='\0';
 
2030
    if ((0 == strcasecomp(cp, "DIR")) && (cp - line) > 17) {
 
2031
        /** It's an SFS directory. **/
 
2032
        StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
2033
        entry_info->size = 0;
 
2034
    } else {
 
2035
        /** It's a file. **/
 
2036
        cp--;
 
2037
        *cp = '.';
 
2038
        StrAllocCat(entry_info->filename, cp);
 
2039
 
 
2040
        /** Track down the VM/CMS RECFM or type. **/
 
2041
        cp = cps;
 
2042
        if (cp < end) {
 
2043
            cp = LYSkipBlanks(cp);
 
2044
            cps = LYSkipNonBlanks(cp);
 
2045
            *cps++ = '\0';
 
2046
            /** Check cp here, if it's relevant someday. **/
 
2047
        }
 
2048
    }
 
2049
 
 
2050
    /** Track down the record length or dash. **/
 
2051
    cp = cps;
 
2052
    if (cp < end) {
 
2053
        cp = LYSkipBlanks(cp);
 
2054
        cps = LYSkipNonBlanks(cp);
 
2055
        *cps++ = '\0';
 
2056
        if (isdigit(UCH(*cp))) {
 
2057
            RecordLength = atoi(cp);
 
2058
        }
 
2059
    }
 
2060
 
 
2061
    /** Track down the number of records or the dash. **/
 
2062
    cp = cps;
 
2063
    if (cps < end) {
 
2064
        cp = LYSkipBlanks(cp);
 
2065
        cps = LYSkipNonBlanks(cp);
 
2066
        *cps++ = '\0';
 
2067
        if (isdigit(UCH(*cp))) {
 
2068
            Records = atoi(cp);
 
2069
        }
 
2070
        if (Records > 0 && RecordLength > 0) {
 
2071
            /** Compute an approximate size. **/
 
2072
            entry_info->size = (Records * RecordLength);
 
2073
        }
 
2074
    }
 
2075
 
 
2076
    /** Set the years and date, if we don't have them yet. **/
 
2077
    if (!HaveYears) {
 
2078
        set_years_and_date();
 
2079
    }
 
2080
 
 
2081
    /** Track down the date. **/
 
2082
    cpd = cps;
 
2083
    if (((cps < end) &&
 
2084
         (cps = strchr(cpd, ':')) != NULL) &&
 
2085
        (cps < (end - 3) &&
 
2086
         isdigit(UCH(*(cps+1))) && isdigit(UCH(*(cps+2))) && *(cps+3) == ':')) {
 
2087
        cps += 3;
 
2088
        *cps = '\0';
 
2089
        if ((cps - cpd) >= 14) {
 
2090
            cpd = (cps - 14);
 
2091
            *(cpd+2) = '\0';    /* Month */
 
2092
            *(cpd+5) = '\0';    /* Day */
 
2093
            *(cpd+8) = '\0';    /* Year */
 
2094
            cps -= 5;           /* Time */
 
2095
            if (*cpd == ' ')
 
2096
                *cpd = '0';
 
2097
            i = atoi(cpd) - 1;
 
2098
            sprintf(date, "%.3s %.2s", months[i], (cpd+3));
 
2099
            if (date[4] == '0')
 
2100
                date[4] = ' ';
 
2101
            cpd += 6;           /* Year */
 
2102
            if (strcmp((ThisYear+2), cpd)) {
 
2103
                /* Not this year, so show the year. */
 
2104
                if (atoi(cpd) < 70) {
 
2105
                    sprintf(&date[6], "  20%.2s", cpd);
 
2106
                } else {
 
2107
                    sprintf(&date[6], "  19%.2s", cpd);
 
2108
                }
 
2109
            } else {
 
2110
                /* Is this year, so show the time. */
 
2111
                *(cps+2) = '\0';        /* Hour */
 
2112
                i = atoi(cps);
 
2113
                sprintf(&date[6], " %02d:%.2s", i, (cps+3));
 
2114
            }
 
2115
            StrAllocCopy(entry_info->date, date);
 
2116
            if (entry_info->date[4] == ' '|| entry_info->date[4] == '0') {
 
2117
                entry_info->date[4] = HT_NON_BREAK_SPACE;
 
2118
            }
 
2119
        }
 
2120
    }
 
2121
 
 
2122
    /** Wrap it up. **/
 
2123
    CTRACE((tfp, "HTFTP: VM/CMS filename: %s  date: %s  size: %d\n",
 
2124
                entry_info->filename,
 
2125
                NonNull(entry_info->date),
 
2126
                entry_info->size));
 
2127
    return;
 
2128
} /* parse_cms_dir_entry */
 
2129
 
 
2130
/*
 
2131
 *     parse_dir_entry()
 
2132
 *      Given a line of LIST/NLST output in entry, return results
 
2133
 *      and a file/dir name in entry_info struct
 
2134
 *
 
2135
 *      If first is true, this is the first name in a directory.
 
2136
 */
 
2137
 
 
2138
PRIVATE EntryInfo * parse_dir_entry ARGS3(
 
2139
        char *,         entry,
 
2140
        BOOLEAN *,      first,
 
2141
        char **,        pspilledname)
 
2142
{
 
2143
    EntryInfo *entry_info;
 
2144
    int  i;
 
2145
    int  len;
 
2146
    BOOLEAN remove_size=FALSE;
 
2147
    char *cp;
 
2148
 
 
2149
    entry_info = (EntryInfo *)malloc(sizeof(EntryInfo));
 
2150
    if (entry_info == NULL)
 
2151
        outofmem(__FILE__, "parse_dir_entry");
 
2152
    entry_info->filename = NULL;
 
2153
    entry_info->type = NULL;
 
2154
    entry_info->date = NULL;
 
2155
    entry_info->size = 0;
 
2156
    entry_info->display = TRUE;
 
2157
 
 
2158
    switch (server_type) {
 
2159
        case DLS_SERVER:
 
2160
 
 
2161
            /*
 
2162
            **  Interpret and edit LIST output from a Unix server
 
2163
            **  in "dls" format.
 
2164
            **  This one must have claimed to be Unix in order to
 
2165
            **  get here; if the first line looks fishy, we revert
 
2166
            **  to Unix and hope that fits better (this recovery is
 
2167
            **  untested). - kw
 
2168
            */
 
2169
 
 
2170
            if (*first) {
 
2171
                len = strlen(entry);
 
2172
                if (!len || entry[0] == ' ' ||
 
2173
                    (len >= 24 && entry[23] != ' ') ||
 
2174
                    (len < 24 && strchr(entry, ' '))) {
 
2175
                    server_type = UNIX_SERVER;
 
2176
                    CTRACE((tfp,
 
2177
                           "HTFTP: Falling back to treating as Unix server.\n"));
 
2178
                } else {
 
2179
                    *first = FALSE;
 
2180
                }
 
2181
            }
 
2182
 
 
2183
            if (server_type == DLS_SERVER) {
 
2184
                /* if still unchanged... */
 
2185
                parse_dls_line(entry, entry_info, pspilledname);
 
2186
 
 
2187
                if (!entry_info->filename || *entry_info->filename == '\0') {
 
2188
                    entry_info->display = FALSE;
 
2189
                    return(entry_info);
 
2190
                }
 
2191
                if (!strcmp(entry_info->filename,"..") ||
 
2192
                    !strcmp(entry_info->filename,"."))
 
2193
                    entry_info->display = FALSE;
 
2194
                if (entry_info->type && *entry_info->type == '\0') {
 
2195
                    FREE(entry_info->type);
 
2196
                    return(entry_info);
 
2197
                }
 
2198
                /*
 
2199
                **      Goto the bottom and get real type.
 
2200
                */
 
2201
                break;
 
2202
            } /* fall through if server_type changed for *first == TRUE ! */
 
2203
 
 
2204
        case UNIX_SERVER:
 
2205
        case PETER_LEWIS_SERVER:
 
2206
        case MACHTEN_SERVER:
 
2207
        case MSDOS_SERVER:
 
2208
        case WINDOWS_NT_SERVER:
 
2209
        case WINDOWS_2K_SERVER:
 
2210
        case APPLESHARE_SERVER:
 
2211
        case NETPRESENZ_SERVER:
 
2212
            /*
 
2213
            **  Check for EPLF output (local times).
 
2214
            */
 
2215
            if (*entry == '+') {
 
2216
                parse_eplf_line(entry, entry_info);
 
2217
                break;
 
2218
            }
 
2219
 
 
2220
            /*
 
2221
            **  Interpret and edit LIST output from Unix server.
 
2222
            */
 
2223
            len = strlen(entry);
 
2224
            if (*first) {
 
2225
                /* don't gettext() this -- incoming text: */
 
2226
                if (!strcmp(entry, "can not access directory .")) {
 
2227
                    /*
 
2228
                     *  Don't reset *first, nothing real will follow. - KW
 
2229
                     */
 
2230
                    entry_info->display = FALSE;
 
2231
                    return(entry_info);
 
2232
                }
 
2233
                *first = FALSE;
 
2234
                if (!strncmp(entry, "total ", 6) ||
 
2235
                    strstr(entry, "not available") != NULL) {
 
2236
                    entry_info->display = FALSE;
 
2237
                    return(entry_info);
 
2238
                } else if (unsure_type) {
 
2239
                    /* this isn't really a unix server! */
 
2240
                    server_type = GENERIC_SERVER;
 
2241
                    entry_info->display = FALSE;
 
2242
                    return(entry_info);
 
2243
                }
 
2244
            }
 
2245
 
 
2246
            /*
 
2247
            **  Check first character of ls -l output.
 
2248
            */
 
2249
            if (TOUPPER(entry[0]) == 'D')  {
 
2250
                /*
 
2251
                **  It's a directory.
 
2252
                */
 
2253
                StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
2254
                remove_size=TRUE; /* size is not useful */
 
2255
            } else if (entry[0] == 'l') {
 
2256
                /*
 
2257
                **  It's a symbolic link, does the user care about
 
2258
                **  knowing if it is symbolic?  I think so since
 
2259
                **  it might be a directory.
 
2260
                */
 
2261
                StrAllocCopy(entry_info->type, gettext("Symbolic Link"));
 
2262
                remove_size=TRUE; /* size is not useful */
 
2263
 
 
2264
                /*
 
2265
                **  Strip off " -> pathname".
 
2266
                */
 
2267
                for (i = len - 1; (i > 3) &&
 
2268
                                  (!isspace(UCH(entry[i])) ||
 
2269
                                   (entry[i-1] != '>')  ||
 
2270
                                   (entry[i-2] != '-') ||
 
2271
                                   (entry[i-3] != ' ')); i--)
 
2272
                    ; /* null body */
 
2273
                if (i > 3) {
 
2274
                    entry[i-3] = '\0';
 
2275
                    len = i - 3;
 
2276
                }
 
2277
            } /* link */
 
2278
 
 
2279
            parse_ls_line(entry, entry_info);
 
2280
 
 
2281
            if (!strcmp(entry_info->filename,"..") ||
 
2282
                !strcmp(entry_info->filename,"."))
 
2283
                entry_info->display = FALSE;
 
2284
            /*
 
2285
            **  Goto the bottom and get real type.
 
2286
            */
 
2287
            break;
 
2288
 
 
2289
        case VMS_SERVER:
 
2290
            /*
 
2291
            **  Interpret and edit LIST output from VMS server
 
2292
            **  and convert information lines to zero length.
 
2293
            */
 
2294
            parse_vms_dir_entry(entry, entry_info);
 
2295
 
 
2296
            /*
 
2297
            **  Get rid of any junk lines.
 
2298
            */
 
2299
            if (!entry_info->display)
 
2300
                return(entry_info);
 
2301
 
 
2302
            /*
 
2303
            **  Trim off VMS directory extensions.
 
2304
            */
 
2305
            len = strlen(entry_info->filename);
 
2306
            if ((len > 4) && !strcmp(&entry_info->filename[len-4], ".dir")) {
 
2307
                entry_info->filename[len-4] = '\0';
 
2308
                StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
2309
                remove_size=TRUE; /* size is not useful */
 
2310
            }
 
2311
            /*
 
2312
            **  Goto the bottom and get real type.
 
2313
            */
 
2314
            break;
 
2315
 
 
2316
        case MS_WINDOWS_SERVER:
 
2317
            /*
 
2318
            **  Interpret and edit LIST output from MS_WINDOWS server
 
2319
            **  and convert information lines to zero length.
 
2320
            */
 
2321
            parse_ms_windows_dir_entry(entry, entry_info);
 
2322
 
 
2323
            /*
 
2324
            **  Get rid of any junk lines.
 
2325
            */
 
2326
            if (!entry_info->display)
 
2327
                return(entry_info);
 
2328
            if (entry_info->type && *entry_info->type == '\0') {
 
2329
                FREE(entry_info->type);
 
2330
                return(entry_info);
 
2331
            }
 
2332
            /*
 
2333
            **  Goto the bottom and get real type.
 
2334
            */
 
2335
            break;
 
2336
 
 
2337
#ifdef NOTDEFINED
 
2338
        case WINDOWS_NT_SERVER:
 
2339
            /*
 
2340
            **  Interpret and edit LIST output from MS_WINDOWS server
 
2341
            **  and convert information lines to zero length.
 
2342
            */
 
2343
            parse_windows_nt_dir_entry(entry, entry_info);
 
2344
 
 
2345
            /*
 
2346
            **  Get rid of any junk lines.
 
2347
            */
 
2348
            if (!entry_info->display)
 
2349
                return(entry_info);
 
2350
            if (entry_info->type && *entry_info->type == '\0') {
 
2351
                FREE(entry_info->type);
 
2352
                return(entry_info);
 
2353
            }
 
2354
            /*
 
2355
            **  Goto the bottom and get real type.
 
2356
            */
 
2357
            break;
 
2358
#endif /* NOTDEFINED */
 
2359
 
 
2360
        case CMS_SERVER:
 
2361
          {
 
2362
            /*
 
2363
            **  Interpret and edit LIST output from VM/CMS server
 
2364
            **  and convert any information lines to zero length.
 
2365
            */
 
2366
            parse_cms_dir_entry(entry, entry_info);
 
2367
 
 
2368
            /*
 
2369
            **  Get rid of any junk lines.
 
2370
            */
 
2371
            if (!entry_info->display)
 
2372
                return(entry_info);
 
2373
            if (entry_info->type && *entry_info->type == '\0') {
 
2374
                FREE(entry_info->type);
 
2375
                return(entry_info);
 
2376
            }
 
2377
            /*
 
2378
            **  Goto the bottom and get real type.
 
2379
            */
 
2380
            break;
 
2381
          }
 
2382
 
 
2383
        case NCSA_SERVER:
 
2384
        case TCPC_SERVER:
 
2385
            /*
 
2386
            **  Directories identified by trailing "/" characters.
 
2387
            */
 
2388
            StrAllocCopy(entry_info->filename, entry);
 
2389
            len = strlen(entry);
 
2390
            if (entry[len-1] == '/') {
 
2391
                /*
 
2392
                **  It's a dir, remove / and mark it as such.
 
2393
                */
 
2394
                entry[len-1] = '\0';
 
2395
                StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
 
2396
                remove_size=TRUE; /* size is not useful */
 
2397
            }
 
2398
            /*
 
2399
            **  Goto the bottom and get real type.
 
2400
            */
 
2401
            break;
 
2402
 
 
2403
        default:
 
2404
            /*
 
2405
            **  We can't tell if it is a directory since we only
 
2406
            **  did an NLST :(  List bad file types anyways?  NOT!
 
2407
            */
 
2408
            StrAllocCopy(entry_info->filename, entry);
 
2409
            return(entry_info); /* mostly empty info */
 
2410
 
 
2411
    } /* switch (server_type) */
 
2412
 
 
2413
    if (remove_size && entry_info->size) {
 
2414
        entry_info->size = 0;
 
2415
    }
 
2416
 
 
2417
    if (entry_info->filename && strlen(entry_info->filename) > 3) {
 
2418
        if (((cp=strrchr(entry_info->filename, '.')) != NULL &&
 
2419
             0 == strncasecomp(cp, ".me", 3)) &&
 
2420
            (cp[3] == '\0' || cp[3] == ';')) {
 
2421
            /*
 
2422
            **  Don't treat this as application/x-Troff-me
 
2423
            **  if it's a Unix server but has the string
 
2424
            **  "read.me", or if it's not a Unix server. - FM
 
2425
            */
 
2426
            if ((server_type != UNIX_SERVER) ||
 
2427
                (cp > (entry_info->filename + 3) &&
 
2428
                 0 == strncasecomp((cp - 4), "read.me", 7))) {
 
2429
                StrAllocCopy(entry_info->type, "text/plain");
 
2430
            }
 
2431
        }
 
2432
    }
 
2433
 
 
2434
    /*
 
2435
    **  Get real types eventually.
 
2436
    */
 
2437
    if (!entry_info->type) {
 
2438
        CONST char *cp2;
 
2439
        HTFormat format;
 
2440
        HTAtom * encoding;  /* @@ not used at all */
 
2441
        format = HTFileFormat(entry_info->filename, &encoding, &cp2);
 
2442
 
 
2443
        if (cp2 == NULL) {
 
2444
            if (!strncmp(HTAtom_name(format), "application",11)) {
 
2445
                cp2 = HTAtom_name(format) + 12;
 
2446
                if (!strncmp(cp2,"x-",2))
 
2447
                    cp2 += 2;
 
2448
            } else {
 
2449
                cp2 = HTAtom_name(format);
 
2450
            }
 
2451
        }
 
2452
 
 
2453
        StrAllocCopy(entry_info->type, cp2);
 
2454
    }
 
2455
 
 
2456
    return(entry_info);
 
2457
} /* parse_dir_entry */
 
2458
 
 
2459
PRIVATE int compare_EntryInfo_structs ARGS2(
 
2460
        EntryInfo *,    entry1,
 
2461
        EntryInfo *,    entry2)
 
2462
{
 
2463
    int i, status;
 
2464
    char date1[16], date2[16], time1[8], time2[8], month[4];
 
2465
 
 
2466
    switch(HTfileSortMethod) {
 
2467
        case FILE_BY_SIZE:
 
2468
            /* both equal or both 0 */
 
2469
            if (entry1->size == entry2->size)
 
2470
                return(strcmp(entry1->filename, entry2->filename));
 
2471
            else
 
2472
                if (entry1->size > entry2->size)
 
2473
                    return(1);
 
2474
                else
 
2475
                    return(-1);
 
2476
 
 
2477
        case FILE_BY_TYPE:
 
2478
            if (entry1->type && entry2->type) {
 
2479
                status = strcasecomp(entry1->type, entry2->type);
 
2480
                if (status)
 
2481
                    return(status);
 
2482
                /* else fall to filename comparison */
 
2483
            }
 
2484
            return (strcmp(entry1->filename, entry2->filename));
 
2485
 
 
2486
        case FILE_BY_DATE:
 
2487
            if (entry1->date && entry2->date) {
 
2488
                /*
 
2489
                ** Make sure we have the correct length. - FM
 
2490
                */
 
2491
                if (strlen(entry1->date) != 12 || strlen(entry2->date) != 12) {
 
2492
                    return(strcmp(entry1->filename, entry2->filename));
 
2493
                }
 
2494
                /*
 
2495
                ** Set the years and date,
 
2496
                ** if we don't have them yet.
 
2497
                */
 
2498
                if (!HaveYears) {
 
2499
                    set_years_and_date();
 
2500
                }
 
2501
                /*
 
2502
                ** Set up for sorting in reverse
 
2503
                ** chronological order. - FM
 
2504
                */
 
2505
                if (entry1->date[9] == ':') {
 
2506
                    strcpy(date1, "9999");
 
2507
                    strcpy(time1, &entry1->date[7]);
 
2508
                    if (time1[0] == ' ') {
 
2509
                         time1[0] = '0';
 
2510
                    }
 
2511
                } else {
 
2512
                    strcpy(date1, &entry1->date[8]);
 
2513
                    strcpy(time1, "00:00");
 
2514
                }
 
2515
                strncpy(month, entry1->date, 3);
 
2516
                month[3] = '\0';
 
2517
                for (i = 0; i < 12; i++) {
 
2518
                    if (!strcasecomp(month, months[i])) {
 
2519
                        break;
 
2520
                    }
 
2521
                }
 
2522
                i++;
 
2523
                sprintf(month, "%02d", i);
 
2524
                strcat(date1, month);
 
2525
                strncat(date1, &entry1->date[4], 2);
 
2526
                date1[8] = '\0';
 
2527
                if (date1[6] == ' ' || date1[6] == HT_NON_BREAK_SPACE) {
 
2528
                    date1[6] = '0';
 
2529
                }
 
2530
                /*  If no year given, assume last year if it would otherwise
 
2531
                 *  be in the future by more than one day.  The one day
 
2532
                 *  tolerance is to account for a possible timezone
 
2533
                 *  difference. - kw */
 
2534
                if (date1[0] == '9' && atoi(date1) > TheDate + 1) {
 
2535
                    for (i = 0; i < 4; i++) {
 
2536
                        date1[i] = LastYear[i];
 
2537
                    }
 
2538
                }
 
2539
                strcat(date1, time1);
 
2540
                if (entry2->date[9] == ':') {
 
2541
                    strcpy(date2, "9999");
 
2542
                    strcpy(time2, &entry2->date[7]);
 
2543
                    if (time2[0] == ' ') {
 
2544
                        time2[0] = '0';
 
2545
                    }
 
2546
                } else {
 
2547
                    strcpy(date2, &entry2->date[8]);
 
2548
                    strcpy(time2, "00:00");
 
2549
                }
 
2550
                strncpy(month, entry2->date, 3);
 
2551
                month[3] = '\0';
 
2552
                for (i = 0; i < 12; i++) {
 
2553
                    if (!strcasecomp(month, months[i])) {
 
2554
                        break;
 
2555
                    }
 
2556
                }
 
2557
                i++;
 
2558
                sprintf(month, "%02d", i);
 
2559
                strcat(date2, month);
 
2560
                strncat(date2, &entry2->date[4], 2);
 
2561
                date2[8] = '\0';
 
2562
                if (date2[6] == ' ' || date2[6] == HT_NON_BREAK_SPACE) {
 
2563
                    date2[6] = '0';
 
2564
                }
 
2565
                /*  If no year given, assume last year if it would otherwise
 
2566
                 *  be in the future by more than one day.  The one day
 
2567
                 *  tolerance is to account for a possible timezone
 
2568
                 *  difference. - kw */
 
2569
                if (date2[0] == '9' && atoi(date2) > TheDate + 1) {
 
2570
                    for (i = 0; i < 4; i++) {
 
2571
                        date2[i] = LastYear[i];
 
2572
                    }
 
2573
                }
 
2574
                strcat(date2, time2);
 
2575
                /*
 
2576
                ** Do the comparison. - FM
 
2577
                */
 
2578
                status = strcasecomp(date2, date1);
 
2579
                if (status)
 
2580
                    return(status);
 
2581
                /* else fall to filename comparison */
 
2582
            }
 
2583
            return (strcmp(entry1->filename, entry2->filename));
 
2584
 
 
2585
        case FILE_BY_NAME:
 
2586
        default:
 
2587
            return (strcmp(entry1->filename, entry2->filename));
 
2588
    }
 
2589
}
 
2590
 
 
2591
 
 
2592
/*      Read a directory into an hypertext object from the data socket
 
2593
**      --------------------------------------------------------------
 
2594
**
 
2595
** On entry,
 
2596
**      anchor          Parent anchor to link the this node to
 
2597
**      address         Address of the directory
 
2598
** On exit,
 
2599
**      returns         HT_LOADED if OK
 
2600
**                      <0 if error.
 
2601
*/
 
2602
PRIVATE int read_directory ARGS4(
 
2603
        HTParentAnchor *,       parent,
 
2604
        CONST char *,           address,
 
2605
        HTFormat,               format_out,
 
2606
        HTStream *,             sink)
 
2607
{
 
2608
    int status;
 
2609
    BOOLEAN WasInterrupted = FALSE;
 
2610
    HTStructured* target = HTML_new(parent, format_out, sink);
 
2611
    HTStructuredClass targetClass;
 
2612
    char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
 
2613
    EntryInfo *entry_info;
 
2614
    BOOLEAN first = TRUE;
 
2615
    char string_buffer[64];
 
2616
    char *lastpath = NULL;/* prefix for link, either "" (for root) or xxx  */
 
2617
    BOOL need_parent_link = FALSE;
 
2618
    BOOL tildeIsTop = FALSE;
 
2619
 
 
2620
    targetClass = *(target->isa);
 
2621
 
 
2622
    _HTProgress (gettext("Receiving FTP directory."));
 
2623
 
 
2624
    /*
 
2625
     *  Force the current Date and Year (TheDate, ThisYear, and LastYear)
 
2626
     *  to be recalculated for each directory request.  Otherwise we have
 
2627
     *  a problem with long-running sessions assuming the wrong date for
 
2628
     *  today. - kw
 
2629
     */
 
2630
    HaveYears = FALSE;
 
2631
    /*
 
2632
    **  Check whether we always want the home
 
2633
    **  directory treated as Welcome. - FM
 
2634
    */
 
2635
    if (server_type == VMS_SERVER)
 
2636
        tildeIsTop = TRUE;
 
2637
 
 
2638
    /*
 
2639
    **  This should always come back FALSE, since the
 
2640
    **  flag is set only for local directory listings
 
2641
    **  if LONG_LIST was defined on compilation, but
 
2642
    **  we could someday set up an equivalent listing
 
2643
    **  for Unix ftp servers. - FM
 
2644
    */
 
2645
    need_parent_link = HTDirTitles(target, parent, tildeIsTop);
 
2646
 
 
2647
    data_read_pointer = data_write_pointer = data_buffer;
 
2648
 
 
2649
    if (*filename == '\0') {              /* Empty filename: use root. */
 
2650
        StrAllocCopy (lastpath, "/");
 
2651
    } else if (!strcmp(filename,"/")) {   /* Root path. */
 
2652
        StrAllocCopy (lastpath, "/foo/..");
 
2653
    } else {
 
2654
        char * p = strrchr(filename, '/');           /* Find the lastslash. */
 
2655
        char *cp;
 
2656
 
 
2657
        if (server_type == CMS_SERVER) {
 
2658
            StrAllocCopy(lastpath, filename); /* Use absolute path for CMS. */
 
2659
        } else {
 
2660
            StrAllocCopy(lastpath, p+1);   /* Take slash off the beginning. */
 
2661
        }
 
2662
        if ((cp = strrchr(lastpath, ';')) != NULL) {   /* Trim type= param. */
 
2663
            if (!strncasecomp((cp+1), "type=", 5)) {
 
2664
                if (TOUPPER(*(cp+6)) == 'D' ||
 
2665
                    TOUPPER(*(cp+6)) == 'A' ||
 
2666
                    TOUPPER(*(cp+6)) == 'I')
 
2667
                    *cp = '\0';
 
2668
            }
 
2669
        }
 
2670
    }
 
2671
    FREE (filename);
 
2672
 
 
2673
    {
 
2674
        HTBTree * bt = HTBTree_new((HTComparer)compare_EntryInfo_structs);
 
2675
        int ic;
 
2676
        HTChunk * chunk = HTChunkCreate(128);
 
2677
        int BytesReceived = 0;
 
2678
        int BytesReported = 0;
 
2679
        char NumBytes[64];
 
2680
        char *spilledname = NULL;
 
2681
        PUTC('\n');  /* prettier LJM */
 
2682
        for (ic = 0; ic != EOF;) {      /* For each entry in the directory */
 
2683
            HTChunkClear(chunk);
 
2684
 
 
2685
            if (HTCheckForInterrupt()) {
 
2686
                CTRACE((tfp,
 
2687
                       "read_directory: interrupted after %d bytes\n",
 
2688
                       BytesReceived));
 
2689
                WasInterrupted = TRUE;
 
2690
                if (BytesReceived) {
 
2691
                    goto unload_btree;  /* unload btree */
 
2692
                } else {
 
2693
                    ABORT_TARGET;
 
2694
                    HTBTreeAndObject_free(bt);
 
2695
                    FREE(spilledname);
 
2696
                    return HT_INTERRUPTED;
 
2697
                }
 
2698
            }
 
2699
 
 
2700
            /*   read directory entry
 
2701
             */
 
2702
            interrupted_in_next_data_char = FALSE;
 
2703
            for (;;) {                  /* Read in one line as filename */
 
2704
                ic = NEXT_DATA_CHAR;
 
2705
AgainForMultiNet:
 
2706
                if (interrupted_in_next_data_char) {
 
2707
                    CTRACE((tfp,
 
2708
                           "read_directory: interrupted_in_next_data_char after %d bytes\n",
 
2709
                           BytesReceived));
 
2710
                    WasInterrupted = TRUE;
 
2711
                    if (BytesReceived) {
 
2712
                        goto unload_btree;  /* unload btree */
 
2713
                    } else {
 
2714
                        ABORT_TARGET;
 
2715
                        HTBTreeAndObject_free(bt);
 
2716
                        FREE(spilledname);
 
2717
                        return HT_INTERRUPTED;
 
2718
                    }
 
2719
                } else if ((char)ic == CR || (char)ic == LF) {    /* Terminator? */
 
2720
                    if (chunk->size != 0) {  /* got some text */
 
2721
                        /* Deal with MultiNet's wrapping of long lines */
 
2722
                        if (server_type == VMS_SERVER) {
 
2723
                        /* Deal with MultiNet's wrapping of long lines - F.M. */
 
2724
                            if (data_read_pointer < data_write_pointer &&
 
2725
                                *(data_read_pointer+1) == ' ')
 
2726
                                data_read_pointer++;
 
2727
                            else if (data_read_pointer >= data_write_pointer) {
 
2728
                                status = NETREAD(data_soc, data_buffer,
 
2729
                                                 DATA_BUFFER_SIZE);
 
2730
                                if (status == HT_INTERRUPTED) {
 
2731
                                    interrupted_in_next_data_char = 1;
 
2732
                                    goto AgainForMultiNet;
 
2733
                                }
 
2734
                                if (status <= 0) {
 
2735
                                    ic = EOF;
 
2736
                                    break;
 
2737
                                }
 
2738
                                data_write_pointer = data_buffer + status;
 
2739
                                data_read_pointer = data_buffer;
 
2740
                                if (*data_read_pointer == ' ')
 
2741
                                    data_read_pointer++;
 
2742
                                else
 
2743
                                    break;
 
2744
                            }
 
2745
                            else
 
2746
                                break;
 
2747
                        }
 
2748
                        else
 
2749
                            break;      /* finish getting one entry */
 
2750
                    }
 
2751
                } else if (ic == EOF) {
 
2752
                    break;              /* End of file */
 
2753
                } else {
 
2754
                    HTChunkPutc(chunk, (char)ic);
 
2755
                }
 
2756
            }
 
2757
            HTChunkTerminate(chunk);
 
2758
 
 
2759
            BytesReceived += chunk->size;
 
2760
            if (BytesReceived > BytesReported + 1024) {
 
2761
#ifdef _WINDOWS
 
2762
                sprintf(NumBytes,gettext("Transferred %d bytes (%5d)"),
 
2763
                                BytesReceived, ws_read_per_sec);
 
2764
#else
 
2765
                sprintf(NumBytes, TRANSFERRED_X_BYTES, BytesReceived);
 
2766
#endif
 
2767
                HTProgress(NumBytes);
 
2768
                BytesReported = BytesReceived;
 
2769
            }
 
2770
 
 
2771
            if (ic == EOF && chunk->size == 1)
 
2772
            /* 1 means empty: includes terminating 0 */
 
2773
                break;
 
2774
            CTRACE((tfp, "HTFTP: Line in %s is %s\n",
 
2775
                        lastpath, chunk->data));
 
2776
 
 
2777
            entry_info = parse_dir_entry(chunk->data, &first, &spilledname);
 
2778
            if (entry_info->display) {
 
2779
                FREE(spilledname);
 
2780
                CTRACE((tfp, "Adding file to BTree: %s\n",
 
2781
                            entry_info->filename));
 
2782
                HTBTree_add(bt, entry_info);
 
2783
            } else {
 
2784
                free_entryinfo_struct_contents(entry_info);
 
2785
                FREE(entry_info);
 
2786
            }
 
2787
 
 
2788
        }  /* next entry */
 
2789
 
 
2790
unload_btree:
 
2791
 
 
2792
        HTChunkFree(chunk);
 
2793
        FREE(spilledname);
 
2794
 
 
2795
        /* print out the handy help message if it exits :) */
 
2796
        if (help_message_cache_non_empty()) {
 
2797
            START(HTML_PRE);
 
2798
            START(HTML_HR);
 
2799
            PUTC('\n');
 
2800
            PUTS(help_message_cache_contents());
 
2801
            init_help_message_cache();  /* to free memory */
 
2802
            START(HTML_HR);
 
2803
            PUTC('\n');
 
2804
        } else {
 
2805
            START(HTML_PRE);
 
2806
            PUTC('\n');
 
2807
        }
 
2808
 
 
2809
        /* Put up header
 
2810
         */
 
2811
        /* PUTS("    Date        Type             Size     Filename\n");
 
2812
         */
 
2813
 
 
2814
        /* Run through tree printing out in order
 
2815
         */
 
2816
        {
 
2817
#ifdef SH_EX    /* 1997/10/18 (Sat) 14:14:28 */
 
2818
            char *p, name_buff[256];
 
2819
            int  name_len, dot_len;
 
2820
 
 
2821
#define FNAME_WIDTH     30
 
2822
#define FILE_GAP        2
 
2823
 
 
2824
#endif
 
2825
            HTBTElement * ele;
 
2826
            int i;
 
2827
            for (ele = HTBTree_next(bt, NULL);
 
2828
                 ele != NULL;
 
2829
                 ele = HTBTree_next(bt, ele)) {
 
2830
                entry_info = (EntryInfo *)HTBTree_object(ele);
 
2831
 
 
2832
                if (entry_info->date) {
 
2833
                    PUTS(entry_info->date);
 
2834
                    PUTS("  ");
 
2835
                } else {
 
2836
                    PUTS("     * ");
 
2837
                }
 
2838
 
 
2839
                if (entry_info->type) {
 
2840
                    for (i = 0; entry_info->type[i] != '\0' && i < 16; i++)
 
2841
                        PUTC(entry_info->type[i]);
 
2842
                    for (; i < 17; i++)
 
2843
                        PUTC(' ');
 
2844
                }
 
2845
 
 
2846
                /* start the anchor */
 
2847
                HTDirEntry(target, lastpath, entry_info->filename);
 
2848
#ifdef SH_EX    /* 1997/10/18 (Sat) 16:00 */
 
2849
                name_len = strlen(entry_info->filename);
 
2850
 
 
2851
                sprintf(name_buff, "%-30s", entry_info->filename);
 
2852
 
 
2853
                if (name_len < FNAME_WIDTH) {
 
2854
                    dot_len = FNAME_WIDTH - FILE_GAP - name_len;
 
2855
                    if (dot_len > 0) {
 
2856
                        p = name_buff + name_len + 1;
 
2857
                        while (dot_len--)
 
2858
                            *p++ = '.';
 
2859
                    }
 
2860
                } else {
 
2861
                    name_buff[FNAME_WIDTH] = '\0';
 
2862
                }
 
2863
 
 
2864
                PUTS(name_buff);
 
2865
#else
 
2866
                PUTS(entry_info->filename);
 
2867
#endif
 
2868
                END(HTML_A);
 
2869
 
 
2870
                if (entry_info->size) {
 
2871
#ifdef SH_EX    /* 1998/02/02 (Mon) 16:34:52 */
 
2872
                    if (entry_info->size < 1024)
 
2873
                        sprintf(string_buffer, "%6d bytes",
 
2874
                                               entry_info->size);
 
2875
                    else
 
2876
                        sprintf(string_buffer, "%6d Kb",
 
2877
                                                entry_info->size/1024);
 
2878
#else
 
2879
                    if (entry_info->size < 1024)
 
2880
                        sprintf(string_buffer, "  %d bytes",
 
2881
                                               entry_info->size);
 
2882
                    else
 
2883
                        sprintf(string_buffer, "  %dKb",
 
2884
                                                entry_info->size/1024);
 
2885
#endif
 
2886
                    PUTS(string_buffer);
 
2887
                }
 
2888
 
 
2889
                PUTC('\n'); /* end of this entry */
 
2890
 
 
2891
                free_entryinfo_struct_contents(entry_info);
 
2892
            }
 
2893
        }
 
2894
        END(HTML_PRE);
 
2895
        FREE_TARGET;
 
2896
        HTBTreeAndObject_free(bt);
 
2897
    }
 
2898
 
 
2899
    FREE(lastpath);
 
2900
 
 
2901
    if (WasInterrupted || data_soc != -1) { /* should always be true */
 
2902
        /*
 
2903
         *  Without closing the data socket first,
 
2904
         *  the response(0) later may hang.
 
2905
         *  Some servers expect the client to fin/ack the close
 
2906
         *  of the data connection before proceeding with the
 
2907
         *  conversation on the control connection. - kw
 
2908
         */
 
2909
        CTRACE((tfp, "HTFTP: Closing data socket %d\n", data_soc));
 
2910
        status = NETCLOSE(data_soc);
 
2911
        if (status == -1)
 
2912
            HTInetStatus("close");      /* Comment only */
 
2913
        data_soc = -1;
 
2914
    }
 
2915
 
 
2916
    if (WasInterrupted || HTCheckForInterrupt()) {
 
2917
        _HTProgress(TRANSFER_INTERRUPTED);
 
2918
    }
 
2919
    return HT_LOADED;
 
2920
}
 
2921
 
 
2922
/*
 
2923
 * Setup an FTP connection.
 
2924
 */
 
2925
PRIVATE int setup_connection ARGS2(
 
2926
        CONST char *,           name,
 
2927
        HTParentAnchor *,       anchor)
 
2928
{
 
2929
    int retry;                  /* How many times tried? */
 
2930
    int status;
 
2931
 
 
2932
    /* set use_list to NOT since we don't know what kind of server
 
2933
     * this is yet.  And set the type to GENERIC
 
2934
     */
 
2935
    use_list = FALSE;
 
2936
    server_type = GENERIC_SERVER;
 
2937
    ProFTPD_bugs = FALSE;
 
2938
 
 
2939
    for (retry = 0; retry < 2; retry++) { /* For timed out/broken connections */
 
2940
        status = get_connection(name, anchor);
 
2941
        if (status < 0)
 
2942
            return status;
 
2943
 
 
2944
        if (!ftp_local_passive) {
 
2945
            status = get_listen_socket();
 
2946
            if (status < 0) {
 
2947
                NETCLOSE (control->socket);
 
2948
                control->socket = -1;
 
2949
#ifdef INET6
 
2950
                if (master_socket >= 0)
 
2951
                    (void)close_master_socket ();
 
2952
#else
 
2953
                close_master_socket ();
 
2954
#endif /* INET6 */
 
2955
                /* HT_INTERRUPTED would fall through, if we could interrupt
 
2956
                   somehow in the middle of it, which we currently can't. */
 
2957
                return status;
 
2958
            }
 
2959
 
 
2960
#ifdef REPEAT_PORT
 
2961
            /*  Inform the server of the port number we will listen on
 
2962
            */
 
2963
            status = response(port_command);
 
2964
            if (status == HT_INTERRUPTED) {
 
2965
                CTRACE((tfp, "HTFTP: Interrupted in response (port_command)\n"));
 
2966
                _HTProgress (CONNECTION_INTERRUPTED);
 
2967
                NETCLOSE (control->socket);
 
2968
                control->socket = -1;
 
2969
                close_master_socket ();
 
2970
                return HT_INTERRUPTED;
 
2971
            }
 
2972
            if (status != 2) {          /* Could have timed out */
 
2973
                if (status < 0)
 
2974
                    continue;           /* try again - net error*/
 
2975
                return -status;         /* bad reply */
 
2976
            }
 
2977
            CTRACE((tfp, "HTFTP: Port defined.\n"));
 
2978
#endif /* REPEAT_PORT */
 
2979
        } else {                /* Tell the server to be passive */
 
2980
            char *command = NULL;
 
2981
            char *p;
 
2982
            int h0, h1, h2, h3, p0, p1; /* Parts of reply */
 
2983
#ifdef INET6
 
2984
            char dst[LINE_LENGTH+1];
 
2985
#endif
 
2986
 
 
2987
            data_soc = status;
 
2988
 
 
2989
#ifdef INET6
 
2990
            status = send_cmd_1(p = "EPSV");
 
2991
            if (status < 0)     /* retry or Bad return */
 
2992
                continue;
 
2993
            else if (status != 2) {
 
2994
                status = send_cmd_1(p = "PASV");
 
2995
                if (status < 0) /* retry or Bad return */
 
2996
                    continue;
 
2997
                else if (status != 2) {
 
2998
                    return -status;     /* bad reply */
 
2999
                }
 
3000
            }
 
3001
 
 
3002
            if (strcmp(p, "PASV") == 0) {
 
3003
                for (p = response_text; *p && *p != ','; p++)
 
3004
                    ; /* null body */
 
3005
 
 
3006
                while (--p > response_text && '0' <= *p && *p <= '9')
 
3007
                    ; /* null body */
 
3008
                status = sscanf(p+1, "%d,%d,%d,%d,%d,%d",
 
3009
                       &h0, &h1, &h2, &h3, &p0, &p1);
 
3010
                if (status < 4) {
 
3011
                    fprintf(tfp, "HTFTP: PASV reply has no inet address!\n");
 
3012
                    return -99;
 
3013
                }
 
3014
                passive_port = (p0<<8) + p1;
 
3015
                sprintf(dst, "%d.%d.%d.%d", h0, h1, h2, h3);
 
3016
            } else if (strcmp(p, "EPSV") == 0) {
 
3017
                unsigned char c0, c1, c2, c3;
 
3018
                struct sockaddr_storage ss;
 
3019
                int sslen;
 
3020
 
 
3021
                /*
 
3022
                 * EPSV bla (|||port|)
 
3023
                 */
 
3024
                for (p = response_text; *p && !isspace(*p); p++)
 
3025
                    ; /* null body */
 
3026
                for (/*nothing*/; *p && *p && *p != '('; p++)   /*)*/
 
3027
                    ; /* null body */
 
3028
                status = sscanf(p, "(%c%c%c%d%c)", &c0, &c1, &c2, &p0, &c3);
 
3029
                if (status != 5) {
 
3030
                    fprintf(tfp, "HTFTP: EPSV reply has invalid format!\n");
 
3031
                    return -99;
 
3032
                }
 
3033
                passive_port = p0;
 
3034
 
 
3035
                sslen = sizeof(ss);
 
3036
                if (getpeername(control->socket, (struct sockaddr *)&ss,
 
3037
                    &sslen) < 0) {
 
3038
                    fprintf(tfp, "HTFTP: getpeername(control) failed\n");
 
3039
                    return -99;
 
3040
                }
 
3041
                if (getnameinfo((struct sockaddr *)&ss, sslen, dst,
 
3042
                    sizeof(dst), NULL, 0, NI_NUMERICHOST)) {
 
3043
                    fprintf(tfp, "HTFTP: getnameinfo failed\n");
 
3044
                    return -99;
 
3045
                }
 
3046
            }
 
3047
#else
 
3048
            status = send_cmd_1("PASV");
 
3049
            if (status != 2) {
 
3050
                if (status < 0)
 
3051
                    continue;           /* retry or Bad return */
 
3052
                return -status;         /* bad reply */
 
3053
            }
 
3054
            for (p = response_text; *p && *p != ','; p++)
 
3055
                ; /* null body */
 
3056
 
 
3057
            while (--p > response_text && '0' <= *p && *p <= '9')
 
3058
                ; /* null body */
 
3059
 
 
3060
            status = sscanf(p+1, "%d,%d,%d,%d,%d,%d",
 
3061
                            &h0, &h1, &h2, &h3, &p0, &p1);
 
3062
            if (status < 4) {
 
3063
                fprintf(tfp, "HTFTP: PASV reply has no inet address!\n");
 
3064
                return -99;
 
3065
            }
 
3066
            passive_port = (PortNumber)((p0<<8) + p1);
 
3067
#endif /* INET6 */
 
3068
            CTRACE((tfp, "HTFTP: Server is listening on port %d\n",
 
3069
                         passive_port));
 
3070
 
 
3071
            /* Open connection for data:  */
 
3072
 
 
3073
#ifdef INET6
 
3074
            HTSprintf0(&command, "%s//%s:%d/", STR_FTP_URL, dst, passive_port);
 
3075
#else
 
3076
            HTSprintf0(&command, "%s//%d.%d.%d.%d:%d/",
 
3077
                    STR_FTP_URL, h0, h1, h2, h3, passive_port);
 
3078
#endif
 
3079
            status = HTDoConnect(command, "FTP data", passive_port, &data_soc);
 
3080
            FREE(command);
 
3081
 
 
3082
            if (status < 0) {
 
3083
                (void) HTInetStatus(gettext("connect for data"));
 
3084
                NETCLOSE(data_soc);
 
3085
                return status;                  /* Bad return */
 
3086
            }
 
3087
 
 
3088
            CTRACE((tfp, "FTP data connected, socket %d\n", data_soc));
 
3089
        }
 
3090
        status = 0;
 
3091
        break;  /* No more retries */
 
3092
 
 
3093
    } /* for retries */
 
3094
    return status;
 
3095
}
 
3096
 
 
3097
/*      Retrieve File from Server
 
3098
**      -------------------------
 
3099
**
 
3100
** On entry,
 
3101
**      name            WWW address of a file: document, including hostname
 
3102
** On exit,
 
3103
**      returns         Socket number for file if good.
 
3104
**                      <0 if bad.
 
3105
*/
 
3106
PUBLIC int HTFTPLoad ARGS4(
 
3107
        CONST char *,           name,
 
3108
        HTParentAnchor *,       anchor,
 
3109
        HTFormat,               format_out,
 
3110
        HTStream *,             sink)
 
3111
{
 
3112
    BOOL isDirectory = NO;
 
3113
    HTAtom * encoding = NULL;
 
3114
    int status, final_status;
 
3115
    int outstanding = 1;        /* outstanding control connection responses
 
3116
                                   that we are willing to wait for, if we
 
3117
                                   get to the point of reading data - kw */
 
3118
    HTFormat format;
 
3119
 
 
3120
    CTRACE((tfp, "HTFTPLoad(%s) %s connection\n", name, ftp_local_passive ? "passive" : "normal"));
 
3121
 
 
3122
    HTReadProgress(0,0);
 
3123
 
 
3124
    status = setup_connection(name, anchor);
 
3125
    if (status < 0)
 
3126
        return status;          /* Failed with this code */
 
3127
 
 
3128
/*      Ask for the file:
 
3129
*/
 
3130
    {
 
3131
        char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
 
3132
        char *fname = filename; /** Save for subsequent free() **/
 
3133
        char *vmsname = NULL;
 
3134
        BOOL binary;
 
3135
        char *type = NULL;
 
3136
        char *cp;
 
3137
 
 
3138
        if (server_type == CMS_SERVER) {
 
3139
            /** If the unescaped path has a %2f, reject it as illegal. - FM **/
 
3140
            if (((cp = strstr(filename, "%2")) != NULL) &&
 
3141
                TOUPPER(cp[2]) == 'F') {
 
3142
                FREE(fname);
 
3143
                init_help_message_cache();  /* to free memory */
 
3144
                NETCLOSE(control->socket);
 
3145
                control->socket = -1;
 
3146
                CTRACE((tfp, "HTFTP: Rejecting path due to illegal escaped slash.\n"));
 
3147
                return -1;
 
3148
            }
 
3149
        }
 
3150
 
 
3151
        if (!*filename) {
 
3152
            StrAllocCopy(filename, "/");
 
3153
            type = "D";
 
3154
        } else if ((type = strrchr(filename, ';')) != NULL) {
 
3155
            /*
 
3156
            **  Check and trim the type= parameter. - FM
 
3157
            */
 
3158
            if (!strncasecomp((type+1), "type=", 5)) {
 
3159
                switch(TOUPPER(*(type+6))) {
 
3160
                case 'D':
 
3161
                    *type = '\0';
 
3162
                    type = "D";
 
3163
                    break;
 
3164
                case 'A':
 
3165
                    *type = '\0';
 
3166
                    type = "A";
 
3167
                    break;
 
3168
                case 'I':
 
3169
                    *type = '\0';
 
3170
                    type = "I";
 
3171
                    break;
 
3172
                default:
 
3173
                    type = "";
 
3174
                    break;
 
3175
                }
 
3176
                if (!*filename) {
 
3177
                    *filename = '/';
 
3178
                    *(filename+1) = '\0';
 
3179
                }
 
3180
            }
 
3181
            if (*type != '\0') {
 
3182
                CTRACE((tfp, "HTFTP: type=%s\n", type));
 
3183
            }
 
3184
        }
 
3185
        HTUnEscape(filename);
 
3186
        CTRACE((tfp, "HTFTP: UnEscaped %s\n", filename));
 
3187
        if (filename[1] == '~') {
 
3188
            /*
 
3189
            ** Check if translation of HOME as tilde is supported,
 
3190
            ** and adjust filename if so. - FM
 
3191
            */
 
3192
            char *cp2 = NULL;
 
3193
            char *fn = NULL;
 
3194
 
 
3195
            if ((cp2 = strchr((filename+1), '/')) != NULL) {
 
3196
                *cp2 = '\0';
 
3197
            }
 
3198
            status = send_cmd_1("PWD");
 
3199
            if (status == 2 && response_text[5] == '/') {
 
3200
                status = send_cwd(filename+1);
 
3201
                if (status == 2) {
 
3202
                    StrAllocCopy(fn, (filename+1));
 
3203
                    if (cp2) {
 
3204
                        *cp2 = '/';
 
3205
                        if (fn[strlen(fn)-1] != '/') {
 
3206
                            StrAllocCat(fn, cp2);
 
3207
                        } else {
 
3208
                            StrAllocCat(fn, (cp2+1));
 
3209
                        }
 
3210
                        cp2 = NULL;
 
3211
                    }
 
3212
                    FREE(fname);
 
3213
                    fname = filename = fn;
 
3214
                }
 
3215
            }
 
3216
            if (cp2) {
 
3217
                *cp2 = '/';
 
3218
            }
 
3219
        }
 
3220
        if (strlen(filename) > 3) {
 
3221
            char *cp2;
 
3222
            if (((cp2=strrchr(filename, '.')) != NULL &&
 
3223
                 0 == strncasecomp(cp2, ".me", 3)) &&
 
3224
                (cp2[3] == '\0' || cp2[3] == ';')) {
 
3225
                /*
 
3226
                **  Don't treat this as application/x-Troff-me
 
3227
                **  if it's a Unix server but has the string
 
3228
                **  "read.me", or if it's not a Unix server. - FM
 
3229
                */
 
3230
                if ((server_type != UNIX_SERVER) ||
 
3231
                    (cp2 > (filename + 3) &&
 
3232
                     0 == strncasecomp((cp2 - 4), "read.me", 7))) {
 
3233
                    *cp2 = '\0';
 
3234
                    format = HTFileFormat(filename, &encoding, NULL);
 
3235
                    *cp2 = '.';
 
3236
                } else {
 
3237
                    format = HTFileFormat(filename, &encoding, NULL);
 
3238
                }
 
3239
            } else {
 
3240
                format = HTFileFormat(filename, &encoding, NULL);
 
3241
            }
 
3242
        } else {
 
3243
            format = HTFileFormat(filename, &encoding, NULL);
 
3244
        }
 
3245
        format = HTCharsetFormat(format, anchor, -1);
 
3246
        binary = (BOOL) (encoding != HTAtom_for("8bit") &&
 
3247
                  encoding != HTAtom_for("7bit"));
 
3248
        if (!binary &&
 
3249
            /*
 
3250
            **  Force binary if we're in source, download or dump
 
3251
            **  mode and this is not a VM/CMS server, so we don't
 
3252
            **  get CRLF instead of LF (or CR) for newlines in text
 
3253
            **  files.  Can't do this for VM/CMS or we'll get
 
3254
            **  raw EBCDIC.  - FM
 
3255
            */
 
3256
            (format_out == WWW_SOURCE ||
 
3257
             format_out == HTAtom_for("www/download") ||
 
3258
             format_out == HTAtom_for("www/dump")) &&
 
3259
            (server_type != CMS_SERVER))
 
3260
            binary = TRUE;
 
3261
        if (!binary && type && *type == 'I') {
 
3262
            /*
 
3263
            **  Force binary if we had ;type=I - FM
 
3264
            */
 
3265
            binary = TRUE;
 
3266
        } else if (binary && type && *type == 'A') {
 
3267
            /*
 
3268
            **  Force ASCII if we had ;type=A - FM
 
3269
            */
 
3270
            binary = FALSE;
 
3271
        }
 
3272
        if (binary != control->binary) {
 
3273
            /*
 
3274
            **  Act on our setting if not already set. - FM
 
3275
            */
 
3276
            char * mode = binary ? "I" : "A";
 
3277
            status = send_cmd_2("TYPE", mode);
 
3278
            if (status != 2) {
 
3279
                init_help_message_cache();  /* to free memory */
 
3280
                return ((status < 0) ? status : -status);
 
3281
            }
 
3282
            control->binary = binary;
 
3283
        }
 
3284
        switch (server_type) {
 
3285
        /*
 
3286
        **  Handle what for Lynx are special case servers, e.g.,
 
3287
        **  for which we respect RFC 1738, or which have known
 
3288
        **  conflicts in suffix mappings. - FM
 
3289
        */
 
3290
        case VMS_SERVER:
 
3291
          {
 
3292
            char *cp1, *cp2;
 
3293
            BOOL included_device = FALSE;
 
3294
            BOOL found_tilde = FALSE;
 
3295
            /** Accept only Unix-style filename **/
 
3296
            if (strchr(filename, ':') != NULL ||
 
3297
                strchr(filename, '[') != NULL) {
 
3298
                FREE(fname);
 
3299
                init_help_message_cache();  /* to free memory */
 
3300
                NETCLOSE(control->socket);
 
3301
                control->socket = -1;
 
3302
                CTRACE((tfp, "HTFTP: Rejecting path due to non-Unix-style syntax.\n"));
 
3303
                return -1;
 
3304
            }
 
3305
            /** Handle any unescaped "/%2F" path **/
 
3306
            if (!strncmp(filename, "//", 2)) {
 
3307
                int i;
 
3308
                included_device = TRUE;
 
3309
                for (i = 0; filename[(i+1)]; i++)
 
3310
                    filename[i] = filename[(i+1)];
 
3311
                filename[i] = '\0';
 
3312
                CTRACE((tfp, "HTFTP: Trimmed '%s'\n", filename));
 
3313
                cp = HTVMS_name("", filename);
 
3314
                CTRACE((tfp, "HTFTP: VMSized '%s'\n", cp));
 
3315
                if ((cp1=strrchr(cp, ']')) != NULL) {
 
3316
                    strcpy(filename, ++cp1);
 
3317
                    CTRACE((tfp, "HTFTP: Filename '%s'\n", filename));
 
3318
                    *cp1 = '\0';
 
3319
                    status = send_cwd(cp);
 
3320
                    if (status != 2) {
 
3321
                        char *dotslash = 0;
 
3322
                        if ((cp1=strchr(cp, '[')) != NULL) {
 
3323
                            *cp1++ = '\0';
 
3324
                            status = send_cwd(cp);
 
3325
                            if (status != 2) {
 
3326
                                FREE(fname);
 
3327
                                init_help_message_cache(); /* to free memory */
 
3328
                                NETCLOSE(control->socket);
 
3329
                                control->socket = -1;
 
3330
                                return ((status < 0) ? status : -status);
 
3331
                            }
 
3332
                            HTSprintf0(&dotslash, "[.%s", cp1);
 
3333
                            status = send_cwd(dotslash);
 
3334
                            FREE(dotslash);
 
3335
                            if (status != 2) {
 
3336
                                FREE(fname);
 
3337
                                init_help_message_cache(); /* to free memory */
 
3338
                                NETCLOSE(control->socket);
 
3339
                                control->socket = -1;
 
3340
                                return ((status < 0) ? status : -status);
 
3341
                            }
 
3342
                        } else {
 
3343
                            FREE(fname);
 
3344
                            init_help_message_cache();  /* to free memory */
 
3345
                            NETCLOSE(control->socket);
 
3346
                            control->socket = -1;
 
3347
                            return ((status < 0) ? status : -status);
 
3348
                        }
 
3349
                    }
 
3350
                } else if ((cp1=strchr(cp, ':')) != NULL &&
 
3351
                           strchr(cp, '[') == NULL &&
 
3352
                           strchr(cp, ']') == NULL) {
 
3353
                    cp1++;
 
3354
                    if (*cp1 != '\0') {
 
3355
                        strcpy(filename, cp1);
 
3356
                        CTRACE((tfp, "HTFTP: Filename '%s'\n", filename));
 
3357
                        HTSprintf0(&vmsname, "%.*s[%s]", cp1-cp, cp, filename);
 
3358
                        status = send_cwd(vmsname);
 
3359
                        if (status != 2) {
 
3360
                            HTSprintf(&vmsname, "%.*s[000000]", cp1-cp, cp);
 
3361
                            status = send_cwd(vmsname);
 
3362
                            if (status != 2) {
 
3363
                                HTSprintf(&vmsname, "%.*s", cp1-cp, cp);
 
3364
                                status = send_cwd(vmsname);
 
3365
                                if (status != 2) {
 
3366
                                    FREE(fname);
 
3367
                                    init_help_message_cache();
 
3368
                                    NETCLOSE(control->socket);
 
3369
                                    control->socket = -1;
 
3370
                                    return ((status < 0) ? status : -status);
 
3371
                                }
 
3372
                            }
 
3373
                        } else {
 
3374
                            HTSprintf0(&vmsname, "000000");
 
3375
                            filename = vmsname;
 
3376
                        }
 
3377
                    }
 
3378
                } else if (0==strcmp(cp, (filename+1))) {
 
3379
                    status = send_cwd(cp);
 
3380
                    if (status != 2) {
 
3381
                        HTSprintf0(&vmsname, "%s:", cp);
 
3382
                        status = send_cwd(vmsname);
 
3383
                        if (status != 2) {
 
3384
                            FREE(fname);
 
3385
                            init_help_message_cache();  /* to free memory */
 
3386
                            NETCLOSE(control->socket);
 
3387
                            control->socket = -1;
 
3388
                            return ((status < 0) ? status : -status);
 
3389
                        }
 
3390
                    }
 
3391
                    HTSprintf0(&vmsname, "000000");
 
3392
                    filename = vmsname;
 
3393
                }
 
3394
            }
 
3395
            /** Trim trailing slash if filename is not the top directory **/
 
3396
            if (strlen(filename) > 1 && filename[strlen(filename)-1] == '/')
 
3397
                filename[strlen(filename)-1] = '\0';
 
3398
 
 
3399
#ifdef MAINTAIN_CONNECTION /* Don't need this if always new connection - F.M. */
 
3400
            if (!included_device) {
 
3401
                /** Get the current default VMS device:[directory] **/
 
3402
                status = send_cmd_1("PWD");
 
3403
                if (status != 2) {
 
3404
                    FREE(fname);
 
3405
                    init_help_message_cache();  /* to free memory */
 
3406
                    NETCLOSE(control->socket);
 
3407
                    control->socket = -1;
 
3408
                    return ((status < 0) ? status : -status);
 
3409
                }
 
3410
                /** Go to the VMS account's top directory **/
 
3411
                if ((cp=strchr(response_text, '[')) != NULL &&
 
3412
                    (cp1=strrchr(response_text, ']')) != NULL) {
 
3413
                    char *tmp = 0;
 
3414
                    unsigned len = 4;
 
3415
 
 
3416
                    StrAllocCopy(tmp, cp);
 
3417
                    if ((cp2=strchr(cp, '.')) != NULL && cp2 < cp1) {
 
3418
                        len += (cp2 - cp);
 
3419
                    } else {
 
3420
                        len += (cp1 - cp);
 
3421
                    }
 
3422
                    tmp[len] = 0;
 
3423
                    StrAllocCat(tmp, "]");
 
3424
 
 
3425
                    status = send_cwd(tmp);
 
3426
                    FREE(tmp);
 
3427
 
 
3428
                    if (status != 2) {
 
3429
                        FREE(fname);
 
3430
                        init_help_message_cache();  /* to free memory */
 
3431
                        NETCLOSE(control->socket);
 
3432
                        control->socket = -1;
 
3433
                        return ((status < 0) ? status : -status);
 
3434
                    }
 
3435
                }
 
3436
            }
 
3437
#endif /* MAINTAIN_CONNECTION */
 
3438
 
 
3439
            /** If we want the VMS account's top directory, list it now **/
 
3440
            if (!(strcmp(filename, "/~")) ||
 
3441
                (included_device && 0==strcmp(filename, "000000")) ||
 
3442
                (strlen(filename) == 1 && *filename == '/')) {
 
3443
                isDirectory = YES;
 
3444
                status = send_cmd_1("LIST");
 
3445
                FREE(fname);
 
3446
                if (status != 1) {
 
3447
                    /* Action not started */
 
3448
                    init_help_message_cache();  /* to free memory */
 
3449
                    NETCLOSE(control->socket);
 
3450
                    control->socket = -1;
 
3451
                    return ((status < 0) ? status : -status);
 
3452
                }
 
3453
                /** Big goto! **/
 
3454
                goto listen;
 
3455
            }
 
3456
            /** Otherwise, go to appropriate directory and doctor filename **/
 
3457
            if (!strncmp(filename, "/~", 2)) {
 
3458
                filename += 2;
 
3459
                found_tilde = TRUE;
 
3460
            }
 
3461
            CTRACE((tfp, "check '%s' to translate x/y/ to [.x.y]\n", filename));
 
3462
            if (!included_device &&
 
3463
                (cp = strchr(filename, '/')) != NULL &&
 
3464
                (cp1 = strrchr(cp, '/')) != NULL &&
 
3465
                (cp1 - cp) > 1) {
 
3466
                char *tmp = 0;
 
3467
 
 
3468
                HTSprintf0(&tmp, "[.%.*s]", cp1-cp-1, cp+1);
 
3469
 
 
3470
                CTRACE((tfp, "change path '%s'\n", tmp));
 
3471
                while ((cp2 = strrchr(tmp, '/')) != NULL)
 
3472
                    *cp2 = '.';
 
3473
                CTRACE((tfp, "...to  path '%s'\n", tmp));
 
3474
 
 
3475
                status = send_cwd(tmp);
 
3476
                FREE(tmp);
 
3477
 
 
3478
                if (status != 2) {
 
3479
                    FREE(fname);
 
3480
                    init_help_message_cache();  /* to free memory */
 
3481
                    NETCLOSE(control->socket);
 
3482
                    control->socket = -1;
 
3483
                    return ((status < 0) ? status : -status);
 
3484
                }
 
3485
                filename = cp1+1;
 
3486
            } else {
 
3487
                if (!included_device && !found_tilde) {
 
3488
                    filename += 1;
 
3489
                }
 
3490
            }
 
3491
            break;
 
3492
          }
 
3493
        case CMS_SERVER:
 
3494
          {
 
3495
            /*
 
3496
            **  If we want the CMS account's top directory, or a base
 
3497
            **  SFS or anonymous directory path (i.e., without a slash),
 
3498
            **  list it now. FM
 
3499
            */
 
3500
            if ((strlen(filename) == 1 && *filename == '/') ||
 
3501
                ((0 == strncasecomp((filename+1), "vmsysu:", 7)) &&
 
3502
                 (cp = strchr((filename+1), '.')) != NULL &&
 
3503
                 strchr(cp, '/') == NULL) ||
 
3504
                (0 == strncasecomp(filename+1, "anonymou.", 9) &&
 
3505
                 strchr(filename+1, '/') == NULL)) {
 
3506
                if (filename[1] != '\0') {
 
3507
                    status = send_cwd(filename+1);
 
3508
                    if (status != 2) {
 
3509
                        /* Action not started */
 
3510
                        init_help_message_cache();  /* to free memory */
 
3511
                        NETCLOSE(control->socket);
 
3512
                        control->socket = -1;
 
3513
                        return ((status < 0) ? status : -status);
 
3514
                    }
 
3515
                }
 
3516
                isDirectory = YES;
 
3517
                if (use_list)
 
3518
                    status = send_cmd_1("LIST");
 
3519
                else
 
3520
                    status = send_cmd_1("NLST");
 
3521
                FREE(fname);
 
3522
                if (status != 1) {
 
3523
                    /* Action not started */
 
3524
                    init_help_message_cache();  /* to free memory */
 
3525
                    NETCLOSE(control->socket);
 
3526
                    control->socket = -1;
 
3527
                    return ((status < 0) ? status : -status);
 
3528
                }
 
3529
                /** Big goto! **/
 
3530
                goto listen;
 
3531
            }
 
3532
            filename++;
 
3533
 
 
3534
            /** Otherwise, go to appropriate directory and adjust filename **/
 
3535
            while ((cp = strchr(filename, '/')) != NULL) {
 
3536
                *cp++ = '\0';
 
3537
                status = send_cwd(filename);
 
3538
                if (status == 2) {
 
3539
                    if (*cp == '\0') {
 
3540
                        isDirectory = YES;
 
3541
                        if (use_list)
 
3542
                            status = send_cmd_1("LIST");
 
3543
                        else
 
3544
                            status = send_cmd_1("NLST");
 
3545
                        FREE(fname);
 
3546
                        if (status != 1) {
 
3547
                            /** Action not started **/
 
3548
                            init_help_message_cache();  /* to free memory */
 
3549
                            NETCLOSE(control->socket);
 
3550
                            control->socket = -1;
 
3551
                            return ((status < 0) ? status : -status);
 
3552
                        }
 
3553
                        /** Clear any messages from the login directory **/
 
3554
                        init_help_message_cache();
 
3555
                        /** Big goto! **/
 
3556
                        goto listen;
 
3557
                    }
 
3558
                    filename = cp;
 
3559
                }
 
3560
            }
 
3561
            break;
 
3562
          }
 
3563
        default:
 
3564
            /** Shift for any unescaped "/%2F" path **/
 
3565
            if (!strncmp(filename, "//", 2))
 
3566
                filename++;
 
3567
            break;
 
3568
        }
 
3569
        /*
 
3570
        **  Act on a file or listing request, or try to figure out
 
3571
        **  which we're dealing with if we don't know yet. - FM
 
3572
        */
 
3573
        if (!(type) || (type && *type != 'D')) {
 
3574
            status = send_cmd_2("RETR", filename);
 
3575
#ifdef BROKEN_PROFTPD
 
3576
            /*
 
3577
             * ProFTPD 1.2.5rc1 gets confused when asked to RETR a directory.
 
3578
             */
 
3579
            if (status >= 5) {
 
3580
                int check;
 
3581
 
 
3582
                if (ProFTPD_bugs) {
 
3583
                    CTRACE((tfp, "{{reconnecting...\n"));
 
3584
                    close_connection(control);
 
3585
                    check = setup_connection(name, anchor);
 
3586
                    CTRACE((tfp, "...done }}reconnecting\n"));
 
3587
                    if (check < 0)
 
3588
                        return check;
 
3589
                }
 
3590
            }
 
3591
#endif
 
3592
        } else {
 
3593
            status = 5;         /* Failed status set as flag. - FM */
 
3594
        }
 
3595
        if (status != 1) {      /* Failed : try to CWD to it */
 
3596
            /** Clear any login messages if this isn't the login directory **/
 
3597
            if (strcmp(filename, "/"))
 
3598
                init_help_message_cache();
 
3599
 
 
3600
            status = send_cwd(filename);
 
3601
            if (status == 2) {  /* Succeeded : let's NAME LIST it */
 
3602
                isDirectory = YES;
 
3603
                if (use_list)
 
3604
                    status = send_cmd_1("LIST");
 
3605
                else
 
3606
                    status = send_cmd_1("NLST");
 
3607
            }
 
3608
        }
 
3609
        FREE(fname);
 
3610
        FREE(vmsname);
 
3611
        if (status != 1) {
 
3612
            init_help_message_cache();  /* to free memory */
 
3613
            NETCLOSE(control->socket);
 
3614
            control->socket = -1;
 
3615
            if (status < 0)
 
3616
                return status;
 
3617
            else
 
3618
                return -status;
 
3619
        }
 
3620
    }
 
3621
 
 
3622
listen:
 
3623
    if(!ftp_local_passive) {
 
3624
        /* Wait for the connection */
 
3625
#ifdef INET6
 
3626
        struct sockaddr_storage soc_address;
 
3627
#else
 
3628
        struct sockaddr_in soc_address;
 
3629
#endif /* INET6 */
 
3630
        int     soc_addrlen=sizeof(soc_address);
 
3631
#ifdef SOCKS
 
3632
        if (socks_flag)
 
3633
            status = Raccept(master_socket,
 
3634
                             (struct sockaddr *)&soc_address,
 
3635
                             (void *)&soc_addrlen);
 
3636
        else
 
3637
#endif /* SOCKS */
 
3638
        status = accept(master_socket,
 
3639
                        (struct sockaddr *)&soc_address,
 
3640
                        (void *)&soc_addrlen);
 
3641
        if (status < 0) {
 
3642
            init_help_message_cache();  /* to free memory */
 
3643
            return HTInetStatus("accept");
 
3644
        }
 
3645
        CTRACE((tfp, "TCP: Accepted new socket %d\n", status));
 
3646
        data_soc = status;
 
3647
    } /* !ftp_local_passive */
 
3648
 
 
3649
#if 0   /* no - this makes the data connection go away too soon (2.8.3dev.22) */
 
3650
    if ((status = send_cmd_nowait("QUIT")) == 1)
 
3651
        outstanding++;
 
3652
#endif
 
3653
 
 
3654
    if (isDirectory) {
 
3655
        if (server_type == UNIX_SERVER && !unsure_type &&
 
3656
            !strcmp(response_text,
 
3657
                    "150 Opening ASCII mode data connection for /bin/dl.\n")) {
 
3658
            CTRACE((tfp, "HTFTP: Treating as \"dls\" server.\n"));
 
3659
            server_type = DLS_SERVER;
 
3660
        }
 
3661
        final_status = read_directory (anchor, name, format_out, sink);
 
3662
        if (final_status > 0) {
 
3663
            if (server_type != CMS_SERVER)
 
3664
                if (outstanding-- > 0) {
 
3665
                    status = response(0);
 
3666
                    if (status < 0 ||
 
3667
                        (status == 2 && !strncmp(response_text, "221", 3)))
 
3668
                        outstanding = 0;
 
3669
                }
 
3670
        } else {                /* HT_INTERRUPTED */
 
3671
            /* User may have pressed 'z' to give up because no
 
3672
               packets got through, so let's not make them wait
 
3673
               any longer - kw */
 
3674
            outstanding = 0;
 
3675
        }
 
3676
 
 
3677
        if (data_soc != -1) { /* normally done in read_directory */
 
3678
            CTRACE((tfp, "HTFTP: Closing data socket %d\n", data_soc));
 
3679
            status = NETCLOSE(data_soc);
 
3680
            if (status == -1)
 
3681
                HTInetStatus("close");  /* Comment only */
 
3682
        }
 
3683
        status = final_status;
 
3684
    } else {
 
3685
        int rv;
 
3686
        char *FileName = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
 
3687
 
 
3688
        /** Clear any login messages **/
 
3689
        init_help_message_cache();
 
3690
 
 
3691
        /** Fake a Content-Encoding for compressed files. - FM **/
 
3692
        HTUnEscape(FileName);
 
3693
        if (!IsUnityEnc(encoding)) {
 
3694
            /*
 
3695
             *  We already know from the call to HTFileFormat above that
 
3696
             *  this is a compressed file, no need to look at the filename
 
3697
             *  again. - kw
 
3698
             */
 
3699
            StrAllocCopy(anchor->content_type, format->name);
 
3700
            StrAllocCopy(anchor->content_encoding, HTAtom_name(encoding));
 
3701
            format = HTAtom_for("www/compressed");
 
3702
 
 
3703
        } else {
 
3704
            char *dot;
 
3705
            CompressFileType cft = HTCompressFileType(FileName, "._-", &dot);
 
3706
 
 
3707
            if (cft != cftNone) {
 
3708
                *dot = '\0';
 
3709
                format = HTFileFormat(FileName, &encoding, NULL);
 
3710
                format = HTCharsetFormat(format, anchor, -1);
 
3711
                StrAllocCopy(anchor->content_type, format->name);
 
3712
                format = HTAtom_for("www/compressed");
 
3713
 
 
3714
                switch (cft) {
 
3715
                case cftCompress:
 
3716
                    StrAllocCopy(anchor->content_encoding, "x-compress");
 
3717
                    break;
 
3718
                case cftGzip:
 
3719
                    StrAllocCopy(anchor->content_encoding, "x-gzip");
 
3720
                    break;
 
3721
                case cftBzip2:
 
3722
                    StrAllocCopy(anchor->content_encoding, "x-bzip2");
 
3723
                    break;
 
3724
                default:
 
3725
                    break;
 
3726
                }
 
3727
            }
 
3728
        }
 
3729
        FREE(FileName);
 
3730
 
 
3731
        _HTProgress (gettext("Receiving FTP file."));
 
3732
        rv = HTParseSocket(format, format_out, anchor, data_soc, sink);
 
3733
 
 
3734
        HTInitInput(control->socket);
 
3735
        /* Reset buffering to control connection DD 921208 */
 
3736
 
 
3737
        if (rv < 0) {
 
3738
#if 0   /* any known servers where ABOR would work this way? */
 
3739
            if (rv == HT_INTERRUPTED || rv == -501)
 
3740
                if (send_cmd_nowait("ABOR") == 1) {
 
3741
                    outstanding++;
 
3742
                    CTRACE((tfp, "HTFTP: outstanding responses: %d\n", outstanding));
 
3743
                }
 
3744
#endif
 
3745
            if (rv == -2)       /* weird error, don't expect much response */
 
3746
                outstanding--;
 
3747
            else if (rv == HT_INTERRUPTED || rv == -1)
 
3748
                /* User may have pressed 'z' to give up because no
 
3749
                   packets got through, so let's not make them wait
 
3750
                   longer - kw */
 
3751
                outstanding = 0;
 
3752
            CTRACE((tfp, "HTFTP: Closing data socket %d\n", data_soc));
 
3753
            status = NETCLOSE(data_soc);
 
3754
        } else
 
3755
            status = 2; /* data_soc already closed in HTCopy - kw */
 
3756
 
 
3757
        if (status < 0 && rv != HT_INTERRUPTED && rv != -1) {
 
3758
            (void) HTInetStatus("close");       /* Comment only */
 
3759
        } else {
 
3760
            if (rv != HT_LOADED && outstanding--) {
 
3761
                status = response(0);           /* Pick up final reply */
 
3762
                if (status != 2 && rv != HT_INTERRUPTED && rv != -1) {
 
3763
                    data_soc = -1;              /* invalidate it */
 
3764
                    init_help_message_cache();  /* to free memory */
 
3765
                    return HTLoadError(sink, 500, response_text);
 
3766
                } else if (status <= 0) {
 
3767
                    outstanding = 0;
 
3768
                } else if (status == 2 && !strncmp(response_text, "221", 3))
 
3769
                    outstanding = 0;
 
3770
            }
 
3771
        }
 
3772
        final_status = HT_LOADED;
 
3773
    }
 
3774
    while (outstanding-- > 0 &&
 
3775
           (status > 0)) {
 
3776
        status = response(0);
 
3777
        if (status == 2 && !strncmp(response_text, "221", 3))
 
3778
            break;
 
3779
    }
 
3780
    data_soc = -1;                      /* invalidate it */
 
3781
    CTRACE((tfp, "HTFTPLoad: normal end; "));
 
3782
    if (control->socket < 0) {
 
3783
        CTRACE((tfp, "control socket is %d\n", control->socket));
 
3784
    } else {
 
3785
        CTRACE((tfp, "closing control socket %d\n", control->socket));
 
3786
        status = NETCLOSE(control->socket);
 
3787
        if (status == -1)
 
3788
            HTInetStatus("control connection close");   /* Comment only */
 
3789
    }
 
3790
    control->socket = -1;
 
3791
    init_help_message_cache();  /* to free memory */
 
3792
      /* returns HT_LOADED (always for file if we get here) or error */
 
3793
    return final_status;
 
3794
} /* open_file_read */
 
3795
 
 
3796
/*
 
3797
**  This function frees any user entered password, so that
 
3798
**  it must be entered again for a future request. - FM
 
3799
*/
 
3800
PUBLIC void HTClearFTPPassword NOARGS
 
3801
{
 
3802
    /*
 
3803
    **  Need code to check cached documents from
 
3804
    **  non-anonymous ftp accounts and do something
 
3805
    **  to ensure that they no longer can be accessed
 
3806
    **  without a new retrieval. - FM
 
3807
    */
 
3808
 
 
3809
    /*
 
3810
    **  Now free the current user entered password,
 
3811
    **  if any. - FM
 
3812
    */
 
3813
    FREE(user_entered_password);
 
3814
}
 
3815
 
 
3816
#endif /* ifndef DISABLE_FTP */