~ubuntu-branches/ubuntu/lucid/w3m/lucid-proposed

« back to all changes in this revision

Viewing changes to url.c

  • Committer: Bazaar Package Importer
  • Author(s): Fumitoshi UKAI
  • Date: 2004-04-29 03:28:41 UTC
  • Revision ID: james.westby@ubuntu.com-20040429032841-uo4mu7a813aqrua8
Tags: upstream-0.5.1
ImportĀ upstreamĀ versionĀ 0.5.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: url.c,v 1.89 2004/04/16 18:47:19 ukai Exp $ */
 
2
#include "fm.h"
 
3
#include <sys/types.h>
 
4
#include <sys/socket.h>
 
5
#include <netinet/in.h>
 
6
#include <arpa/inet.h>
 
7
#include <netdb.h>
 
8
 
 
9
#include <signal.h>
 
10
#include <setjmp.h>
 
11
#include <errno.h>
 
12
 
 
13
#include <sys/stat.h>
 
14
#ifdef __EMX__
 
15
#include <io.h>                 /* ?? */
 
16
#endif                          /* __EMX__ */
 
17
 
 
18
#include "html.h"
 
19
#include "Str.h"
 
20
#include "myctype.h"
 
21
#include "regex.h"
 
22
 
 
23
#ifdef USE_SSL
 
24
#ifndef SSLEAY_VERSION_NUMBER
 
25
#include <openssl/crypto.h>             /* SSLEAY_VERSION_NUMBER may be here */
 
26
#endif
 
27
#include <openssl/err.h>
 
28
#endif
 
29
 
 
30
#ifdef  __WATT32__
 
31
#define write(a,b,c)    write_s(a,b,c)
 
32
#endif                          /* __WATT32__ */
 
33
 
 
34
#ifdef INET6
 
35
/* see rc.c, "dns_order" and dnsorders[] */
 
36
int ai_family_order_table[7][3] = {
 
37
    {PF_UNSPEC, PF_UNSPEC, PF_UNSPEC},  /* 0:unspec */
 
38
    {PF_INET, PF_INET6, PF_UNSPEC},     /* 1:inet inet6 */
 
39
    {PF_INET6, PF_INET, PF_UNSPEC},     /* 2:inet6 inet */
 
40
    {PF_UNSPEC, PF_UNSPEC, PF_UNSPEC},  /* 3: --- */
 
41
    {PF_INET, PF_UNSPEC, PF_UNSPEC},    /* 4:inet */
 
42
    {PF_UNSPEC, PF_UNSPEC, PF_UNSPEC},  /* 5: --- */
 
43
    {PF_INET6, PF_UNSPEC, PF_UNSPEC},   /* 6:inet6 */
 
44
};
 
45
#endif                          /* INET6 */
 
46
 
 
47
static JMP_BUF AbortLoading;
 
48
 
 
49
/* XXX: note html.h SCM_ */
 
50
static int
 
51
 DefaultPort[] = {
 
52
    80,                         /* http */
 
53
    70,                         /* gopher */
 
54
    21,                         /* ftp */
 
55
    21,                         /* ftpdir */
 
56
    0,                          /* local - not defined */
 
57
    0,                          /* local-CGI - not defined? */
 
58
    0,                          /* exec - not defined? */
 
59
    119,                        /* nntp */
 
60
    119,                        /* nntp group */
 
61
    119,                        /* news */
 
62
    119,                        /* news group */
 
63
    0,                          /* data - not defined */
 
64
    0,                          /* mailto - not defined */
 
65
#ifdef USE_SSL
 
66
    443,                        /* https */
 
67
#endif                          /* USE_SSL */
 
68
};
 
69
 
 
70
struct cmdtable schemetable[] = {
 
71
    {"http", SCM_HTTP},
 
72
    {"gopher", SCM_GOPHER},
 
73
    {"ftp", SCM_FTP},
 
74
    {"local", SCM_LOCAL},
 
75
    {"file", SCM_LOCAL},
 
76
    /*  {"exec", SCM_EXEC}, */
 
77
    {"nntp", SCM_NNTP},
 
78
    /*  {"nntp", SCM_NNTP_GROUP}, */
 
79
    {"news", SCM_NEWS},
 
80
    /*  {"news", SCM_NEWS_GROUP}, */
 
81
    {"data", SCM_DATA},
 
82
#ifndef USE_W3MMAILER
 
83
    {"mailto", SCM_MAILTO},
 
84
#endif
 
85
#ifdef USE_SSL
 
86
    {"https", SCM_HTTPS},
 
87
#endif                          /* USE_SSL */
 
88
    {NULL, SCM_UNKNOWN},
 
89
};
 
90
 
 
91
static struct table2 DefaultGuess[] = {
 
92
    {"html", "text/html"},
 
93
    {"htm", "text/html"},
 
94
    {"shtml", "text/html"},
 
95
    {"gif", "image/gif"},
 
96
    {"jpeg", "image/jpeg"},
 
97
    {"jpg", "image/jpeg"},
 
98
    {"png", "image/png"},
 
99
    {"xbm", "image/xbm"},
 
100
    {"au", "audio/basic"},
 
101
    {"gz", "application/x-gzip"},
 
102
    {"Z", "application/x-compress"},
 
103
    {"bz2", "application/x-bzip"},
 
104
    {"tar", "application/x-tar"},
 
105
    {"zip", "application/x-zip"},
 
106
    {"lha", "application/x-lha"},
 
107
    {"lzh", "application/x-lha"},
 
108
    {"ps", "application/postscript"},
 
109
    {"pdf", "application/pdf"},
 
110
    {NULL, NULL}
 
111
};
 
112
 
 
113
static void add_index_file(ParsedURL *pu, URLFile *uf);
 
114
 
 
115
/* #define HTTP_DEFAULT_FILE    "/index.html" */
 
116
 
 
117
#ifndef HTTP_DEFAULT_FILE
 
118
#define HTTP_DEFAULT_FILE "/"
 
119
#endif                          /* not HTTP_DEFAULT_FILE */
 
120
 
 
121
#ifdef SOCK_DEBUG
 
122
#include <stdarg.h>
 
123
 
 
124
static void
 
125
sock_log(char *message, ...)
 
126
{
 
127
    FILE *f = fopen("zzzsocklog", "a");
 
128
    va_list va;
 
129
 
 
130
    if (f == NULL)
 
131
        return;
 
132
    va_start(va, message);
 
133
    vfprintf(f, message, va);
 
134
    fclose(f);
 
135
}
 
136
 
 
137
#endif
 
138
 
 
139
static TextList *mimetypes_list;
 
140
static struct table2 **UserMimeTypes;
 
141
 
 
142
static struct table2 *
 
143
loadMimeTypes(char *filename)
 
144
{
 
145
    FILE *f;
 
146
    char *d, *type;
 
147
    int i, n;
 
148
    Str tmp;
 
149
    struct table2 *mtypes;
 
150
 
 
151
    f = fopen(expandPath(filename), "r");
 
152
    if (f == NULL)
 
153
        return NULL;
 
154
    n = 0;
 
155
    while (tmp = Strfgets(f), tmp->length > 0) {
 
156
        d = tmp->ptr;
 
157
        if (d[0] != '#') {
 
158
            d = strtok(d, " \t\n\r");
 
159
            if (d != NULL) {
 
160
                d = strtok(NULL, " \t\n\r");
 
161
                for (i = 0; d != NULL; i++)
 
162
                    d = strtok(NULL, " \t\n\r");
 
163
                n += i;
 
164
            }
 
165
        }
 
166
    }
 
167
    fseek(f, 0, 0);
 
168
    mtypes = New_N(struct table2, n + 1);
 
169
    i = 0;
 
170
    while (tmp = Strfgets(f), tmp->length > 0) {
 
171
        d = tmp->ptr;
 
172
        if (d[0] == '#')
 
173
            continue;
 
174
        type = strtok(d, " \t\n\r");
 
175
        if (type == NULL)
 
176
            continue;
 
177
        while (1) {
 
178
            d = strtok(NULL, " \t\n\r");
 
179
            if (d == NULL)
 
180
                break;
 
181
            mtypes[i].item1 = Strnew_charp(d)->ptr;
 
182
            mtypes[i].item2 = Strnew_charp(type)->ptr;
 
183
            i++;
 
184
        }
 
185
    }
 
186
    mtypes[i].item1 = NULL;
 
187
    mtypes[i].item2 = NULL;
 
188
    fclose(f);
 
189
    return mtypes;
 
190
}
 
191
 
 
192
void
 
193
initMimeTypes()
 
194
{
 
195
    int i;
 
196
    TextListItem *tl;
 
197
 
 
198
    if (non_null(mimetypes_files))
 
199
        mimetypes_list = make_domain_list(mimetypes_files);
 
200
    else
 
201
        mimetypes_list = NULL;
 
202
    if (mimetypes_list == NULL)
 
203
        return;
 
204
    UserMimeTypes = New_N(struct table2 *, mimetypes_list->nitem);
 
205
    for (i = 0, tl = mimetypes_list->first; tl; i++, tl = tl->next)
 
206
        UserMimeTypes[i] = loadMimeTypes(tl->ptr);
 
207
}
 
208
 
 
209
static char *
 
210
DefaultFile(int scheme)
 
211
{
 
212
    switch (scheme) {
 
213
    case SCM_HTTP:
 
214
#ifdef USE_SSL
 
215
    case SCM_HTTPS:
 
216
#endif                          /* USE_SSL */
 
217
        return allocStr(HTTP_DEFAULT_FILE, -1);
 
218
#ifdef USE_GOPHER
 
219
    case SCM_GOPHER:
 
220
        return allocStr("1", -1);
 
221
#endif                          /* USE_GOPHER */
 
222
    case SCM_LOCAL:
 
223
    case SCM_LOCAL_CGI:
 
224
    case SCM_FTP:
 
225
    case SCM_FTPDIR:
 
226
        return allocStr("/", -1);
 
227
    }
 
228
    return NULL;
 
229
}
 
230
 
 
231
static MySignalHandler
 
232
KeyAbort(SIGNAL_ARG)
 
233
{
 
234
    LONGJMP(AbortLoading, 1);
 
235
    SIGNAL_RETURN;
 
236
}
 
237
 
 
238
#ifdef USE_SSL
 
239
SSL_CTX *ssl_ctx = NULL;
 
240
 
 
241
void
 
242
free_ssl_ctx()
 
243
{
 
244
    if (ssl_ctx != NULL)
 
245
        SSL_CTX_free(ssl_ctx);
 
246
    ssl_ctx = NULL;
 
247
    ssl_accept_this_site(NULL);
 
248
}
 
249
 
 
250
#if SSLEAY_VERSION_NUMBER >= 0x00905100
 
251
#include <openssl/rand.h>
 
252
static void
 
253
init_PRNG()
 
254
{
 
255
    char buffer[256];
 
256
    const char *file;
 
257
    long l;
 
258
    if (RAND_status())
 
259
        return;
 
260
    if ((file = RAND_file_name(buffer, sizeof(buffer)))) {
 
261
#ifdef USE_EGD
 
262
        if (RAND_egd(file) > 0)
 
263
            return;
 
264
#endif
 
265
        RAND_load_file(file, -1);
 
266
    }
 
267
    if (RAND_status())
 
268
        goto seeded;
 
269
    srand48((long)time(NULL));
 
270
    while (!RAND_status()) {
 
271
        l = lrand48();
 
272
        RAND_seed((unsigned char *)&l, sizeof(long));
 
273
    }
 
274
  seeded:
 
275
    if (file)
 
276
        RAND_write_file(file);
 
277
}
 
278
#endif                          /* SSLEAY_VERSION_NUMBER >= 0x00905100 */
 
279
 
 
280
static SSL *
 
281
openSSLHandle(int sock, char *hostname, char **p_cert)
 
282
{
 
283
    SSL *handle = NULL;
 
284
    static char *old_ssl_forbid_method = NULL;
 
285
#ifdef USE_SSL_VERIFY
 
286
    static int old_ssl_verify_server = -1;
 
287
#endif
 
288
 
 
289
    if (old_ssl_forbid_method != ssl_forbid_method
 
290
        && (!old_ssl_forbid_method || !ssl_forbid_method ||
 
291
            strcmp(old_ssl_forbid_method, ssl_forbid_method))) {
 
292
        old_ssl_forbid_method = ssl_forbid_method;
 
293
#ifdef USE_SSL_VERIFY
 
294
        ssl_path_modified = 1;
 
295
#else
 
296
        free_ssl_ctx();
 
297
#endif
 
298
    }
 
299
#ifdef USE_SSL_VERIFY
 
300
    if (old_ssl_verify_server != ssl_verify_server) {
 
301
        old_ssl_verify_server = ssl_verify_server;
 
302
        ssl_path_modified = 1;
 
303
    }
 
304
    if (ssl_path_modified) {
 
305
        free_ssl_ctx();
 
306
        ssl_path_modified = 0;
 
307
    }
 
308
#endif                          /* defined(USE_SSL_VERIFY) */
 
309
    if (ssl_ctx == NULL) {
 
310
        int option;
 
311
#if SSLEAY_VERSION_NUMBER < 0x0800
 
312
        ssl_ctx = SSL_CTX_new();
 
313
        X509_set_default_verify_paths(ssl_ctx->cert);
 
314
#else                           /* SSLEAY_VERSION_NUMBER >= 0x0800 */
 
315
        SSLeay_add_ssl_algorithms();
 
316
        SSL_load_error_strings();
 
317
        if (!(ssl_ctx = SSL_CTX_new(SSLv23_client_method())))
 
318
            goto eend;
 
319
        option = SSL_OP_ALL;
 
320
        if (ssl_forbid_method) {
 
321
            if (strchr(ssl_forbid_method, '2'))
 
322
                option |= SSL_OP_NO_SSLv2;
 
323
            if (strchr(ssl_forbid_method, '3'))
 
324
                option |= SSL_OP_NO_SSLv3;
 
325
            if (strchr(ssl_forbid_method, 't'))
 
326
                option |= SSL_OP_NO_TLSv1;
 
327
            if (strchr(ssl_forbid_method, 'T'))
 
328
                option |= SSL_OP_NO_TLSv1;
 
329
        }
 
330
        SSL_CTX_set_options(ssl_ctx, option);
 
331
#ifdef USE_SSL_VERIFY
 
332
        /* derived from openssl-0.9.5/apps/s_{client,cb}.c */
 
333
#if 1                           /* use SSL_get_verify_result() to verify cert */
 
334
        SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
 
335
#else
 
336
        SSL_CTX_set_verify(ssl_ctx,
 
337
                           ssl_verify_server ? SSL_VERIFY_PEER :
 
338
                           SSL_VERIFY_NONE, NULL);
 
339
#endif
 
340
        if (ssl_cert_file != NULL && *ssl_cert_file != '\0') {
 
341
            int ng = 1;
 
342
            if (SSL_CTX_use_certificate_file
 
343
                (ssl_ctx, ssl_cert_file, SSL_FILETYPE_PEM) > 0) {
 
344
                char *key_file = (ssl_key_file == NULL
 
345
                                  || *ssl_key_file ==
 
346
                                  '\0') ? ssl_cert_file : ssl_key_file;
 
347
                if (SSL_CTX_use_PrivateKey_file
 
348
                    (ssl_ctx, key_file, SSL_FILETYPE_PEM) > 0)
 
349
                    if (SSL_CTX_check_private_key(ssl_ctx))
 
350
                        ng = 0;
 
351
            }
 
352
            if (ng) {
 
353
                free_ssl_ctx();
 
354
                goto eend;
 
355
            }
 
356
        }
 
357
        if ((!ssl_ca_file && !ssl_ca_path)
 
358
            || SSL_CTX_load_verify_locations(ssl_ctx, ssl_ca_file, ssl_ca_path))
 
359
#endif                          /* defined(USE_SSL_VERIFY) */
 
360
            SSL_CTX_set_default_verify_paths(ssl_ctx);
 
361
#endif                          /* SSLEAY_VERSION_NUMBER >= 0x0800 */
 
362
    }
 
363
    handle = SSL_new(ssl_ctx);
 
364
    SSL_set_fd(handle, sock);
 
365
#if SSLEAY_VERSION_NUMBER >= 0x00905100
 
366
    init_PRNG();
 
367
#endif                          /* SSLEAY_VERSION_NUMBER >= 0x00905100 */
 
368
    if (SSL_connect(handle) > 0) {
 
369
        Str serv_cert = ssl_get_certificate(handle, hostname);
 
370
        if (serv_cert) {
 
371
            *p_cert = serv_cert->ptr;
 
372
            return handle;
 
373
        }
 
374
        close(sock);
 
375
        SSL_free(handle);
 
376
        return NULL;
 
377
    }
 
378
  eend:
 
379
    close(sock);
 
380
    if (handle)
 
381
        SSL_free(handle);
 
382
    /* FIXME: gettextize? */
 
383
    disp_err_message(Sprintf
 
384
                     ("SSL error: %s",
 
385
                      ERR_error_string(ERR_get_error(), NULL))->ptr, FALSE);
 
386
    return NULL;
 
387
}
 
388
 
 
389
static void
 
390
SSL_write_from_file(SSL * ssl, char *file)
 
391
{
 
392
    FILE *fd;
 
393
    int c;
 
394
    char buf[1];
 
395
    fd = fopen(file, "r");
 
396
    if (fd != NULL) {
 
397
        while ((c = fgetc(fd)) != EOF) {
 
398
            buf[0] = c;
 
399
            SSL_write(ssl, buf, 1);
 
400
        }
 
401
        fclose(fd);
 
402
    }
 
403
}
 
404
 
 
405
#endif                          /* USE_SSL */
 
406
 
 
407
static void
 
408
write_from_file(int sock, char *file)
 
409
{
 
410
    FILE *fd;
 
411
    int c;
 
412
    char buf[1];
 
413
    fd = fopen(file, "r");
 
414
    if (fd != NULL) {
 
415
        while ((c = fgetc(fd)) != EOF) {
 
416
            buf[0] = c;
 
417
            write(sock, buf, 1);
 
418
        }
 
419
        fclose(fd);
 
420
    }
 
421
}
 
422
 
 
423
ParsedURL *
 
424
baseURL(Buffer *buf)
 
425
{
 
426
    if (buf->bufferprop & BP_NO_URL) {
 
427
        /* no URL is defined for the buffer */
 
428
        return NULL;
 
429
    }
 
430
    if (buf->baseURL != NULL) {
 
431
        /* <BASE> tag is defined in the document */
 
432
        return buf->baseURL;
 
433
    }
 
434
    else
 
435
        return &buf->currentURL;
 
436
}
 
437
 
 
438
int
 
439
openSocket(char *const hostname,
 
440
           char *remoteport_name, unsigned short remoteport_num)
 
441
{
 
442
    volatile int sock = -1;
 
443
#ifdef INET6
 
444
    int *af;
 
445
    struct addrinfo hints, *res0, *res;
 
446
    int error;
 
447
    char *hname;
 
448
#else                           /* not INET6 */
 
449
    struct sockaddr_in hostaddr;
 
450
    struct hostent *entry;
 
451
    struct protoent *proto;
 
452
    unsigned short s_port;
 
453
    int a1, a2, a3, a4;
 
454
    unsigned long adr;
 
455
#endif                          /* not INET6 */
 
456
    MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
 
457
 
 
458
    if (fmInitialized) {
 
459
        /* FIXME: gettextize? */
 
460
        message(Sprintf("Opening socket...")->ptr, 0, 0);
 
461
        refresh();
 
462
    }
 
463
    if (SETJMP(AbortLoading) != 0) {
 
464
#ifdef SOCK_DEBUG
 
465
        sock_log("openSocket() failed. reason: user abort\n");
 
466
#endif
 
467
        if (sock >= 0)
 
468
            close(sock);
 
469
        goto error;
 
470
    }
 
471
    TRAP_ON;
 
472
    if (hostname == NULL) {
 
473
#ifdef SOCK_DEBUG
 
474
        sock_log("openSocket() failed. reason: Bad hostname \"%s\"\n",
 
475
                 hostname);
 
476
#endif
 
477
        goto error;
 
478
    }
 
479
 
 
480
#ifdef INET6
 
481
    /* rfc2732 compliance */
 
482
    hname = hostname;
 
483
    if (hname != NULL && hname[0] == '[' && hname[strlen(hname) - 1] == ']') {
 
484
        hname = allocStr(hostname + 1, -1);
 
485
        hname[strlen(hname) - 1] = '\0';
 
486
        if (strspn(hname, "0123456789abcdefABCDEF:.") != strlen(hname))
 
487
            goto error;
 
488
    }
 
489
    for (af = ai_family_order_table[DNS_order];; af++) {
 
490
        memset(&hints, 0, sizeof(hints));
 
491
        hints.ai_family = *af;
 
492
        hints.ai_socktype = SOCK_STREAM;
 
493
        if (remoteport_num != 0) {
 
494
            Str portbuf = Sprintf("%d", remoteport_num);
 
495
            error = getaddrinfo(hname, portbuf->ptr, &hints, &res0);
 
496
        }
 
497
        else {
 
498
            error = -1;
 
499
        }
 
500
        if (error && remoteport_name && remoteport_name[0] != '\0') {
 
501
            /* try default port */
 
502
            error = getaddrinfo(hname, remoteport_name, &hints, &res0);
 
503
        }
 
504
        if (error) {
 
505
            if (*af == PF_UNSPEC) {
 
506
                goto error;
 
507
            }
 
508
            /* try next ai family */
 
509
            continue;
 
510
        }
 
511
        sock = -1;
 
512
        for (res = res0; res; res = res->ai_next) {
 
513
            sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
 
514
            if (sock < 0) {
 
515
                continue;
 
516
            }
 
517
            if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
 
518
                close(sock);
 
519
                sock = -1;
 
520
                continue;
 
521
            }
 
522
            break;
 
523
        }
 
524
        if (sock < 0) {
 
525
            freeaddrinfo(res0);
 
526
            if (*af == PF_UNSPEC) {
 
527
                goto error;
 
528
            }
 
529
            /* try next ai family */
 
530
            continue;
 
531
        }
 
532
        freeaddrinfo(res0);
 
533
        break;
 
534
    }
 
535
#else                           /* not INET6 */
 
536
    s_port = htons(remoteport_num);
 
537
    bzero((char *)&hostaddr, sizeof(struct sockaddr_in));
 
538
    if ((proto = getprotobyname("tcp")) == NULL) {
 
539
        /* protocol number of TCP is 6 */
 
540
        proto = New(struct protoent);
 
541
        proto->p_proto = 6;
 
542
    }
 
543
    if ((sock = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0) {
 
544
#ifdef SOCK_DEBUG
 
545
        sock_log("openSocket: socket() failed. reason: %s\n", strerror(errno));
 
546
#endif
 
547
        goto error;
 
548
    }
 
549
    regexCompile("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$", 0);
 
550
    if (regexMatch(hostname, -1, 1)) {
 
551
        sscanf(hostname, "%d.%d.%d.%d", &a1, &a2, &a3, &a4);
 
552
        adr = htonl((a1 << 24) | (a2 << 16) | (a3 << 8) | a4);
 
553
        bcopy((void *)&adr, (void *)&hostaddr.sin_addr, sizeof(long));
 
554
        hostaddr.sin_family = AF_INET;
 
555
        hostaddr.sin_port = s_port;
 
556
        if (fmInitialized) {
 
557
            message(Sprintf("Connecting to %s", hostname)->ptr, 0, 0);
 
558
            refresh();
 
559
        }
 
560
        if (connect(sock, (struct sockaddr *)&hostaddr,
 
561
                    sizeof(struct sockaddr_in)) < 0) {
 
562
#ifdef SOCK_DEBUG
 
563
            sock_log("openSocket: connect() failed. reason: %s\n",
 
564
                     strerror(errno));
 
565
#endif
 
566
            goto error;
 
567
        }
 
568
    }
 
569
    else {
 
570
        char **h_addr_list;
 
571
        int result = -1;
 
572
        if (fmInitialized) {
 
573
            message(Sprintf("Performing hostname lookup on %s", hostname)->ptr,
 
574
                    0, 0);
 
575
            refresh();
 
576
        }
 
577
        if ((entry = gethostbyname(hostname)) == NULL) {
 
578
#ifdef SOCK_DEBUG
 
579
            sock_log("openSocket: gethostbyname() failed. reason: %s\n",
 
580
                     strerror(errno));
 
581
#endif
 
582
            goto error;
 
583
        }
 
584
        hostaddr.sin_family = AF_INET;
 
585
        hostaddr.sin_port = s_port;
 
586
        for (h_addr_list = entry->h_addr_list; *h_addr_list; h_addr_list++) {
 
587
            bcopy((void *)h_addr_list[0], (void *)&hostaddr.sin_addr,
 
588
                  entry->h_length);
 
589
#ifdef SOCK_DEBUG
 
590
            adr = ntohl(*(long *)&hostaddr.sin_addr);
 
591
            sock_log("openSocket: connecting %d.%d.%d.%d\n",
 
592
                     (adr >> 24) & 0xff,
 
593
                     (adr >> 16) & 0xff, (adr >> 8) & 0xff, adr & 0xff);
 
594
#endif
 
595
            if (fmInitialized) {
 
596
                message(Sprintf("Connecting to %s", hostname)->ptr, 0, 0);
 
597
                refresh();
 
598
            }
 
599
            if ((result = connect(sock, (struct sockaddr *)&hostaddr,
 
600
                                  sizeof(struct sockaddr_in))) == 0) {
 
601
                break;
 
602
            }
 
603
#ifdef SOCK_DEBUG
 
604
            else {
 
605
                sock_log("openSocket: connect() failed. reason: %s\n",
 
606
                         strerror(errno));
 
607
            }
 
608
#endif
 
609
        }
 
610
        if (result < 0) {
 
611
            goto error;
 
612
        }
 
613
    }
 
614
#endif                          /* not INET6 */
 
615
 
 
616
    TRAP_OFF;
 
617
    return sock;
 
618
  error:
 
619
    TRAP_OFF;
 
620
    return -1;
 
621
 
 
622
}
 
623
 
 
624
 
 
625
#define COPYPATH_SPC_ALLOW 0
 
626
#define COPYPATH_SPC_IGNORE 1
 
627
#define COPYPATH_SPC_REPLACE 2
 
628
 
 
629
static char *
 
630
copyPath(char *orgpath, int length, int option)
 
631
{
 
632
    Str tmp = Strnew();
 
633
    while (*orgpath && length != 0) {
 
634
        if (IS_SPACE(*orgpath)) {
 
635
            switch (option) {
 
636
            case COPYPATH_SPC_ALLOW:
 
637
                Strcat_char(tmp, *orgpath);
 
638
                break;
 
639
            case COPYPATH_SPC_IGNORE:
 
640
                /* do nothing */
 
641
                break;
 
642
            case COPYPATH_SPC_REPLACE:
 
643
                Strcat_charp(tmp, "%20");
 
644
                break;
 
645
            }
 
646
        }
 
647
        else
 
648
            Strcat_char(tmp, *orgpath);
 
649
        orgpath++;
 
650
        length--;
 
651
    }
 
652
    return tmp->ptr;
 
653
}
 
654
 
 
655
void
 
656
parseURL(char *url, ParsedURL *p_url, ParsedURL *current)
 
657
{
 
658
    char *p, *q;
 
659
    Str tmp;
 
660
 
 
661
    url = url_quote(url);       /* quote 0x01-0x20, 0x7F-0xFF */
 
662
 
 
663
    p = url;
 
664
    p_url->scheme = SCM_MISSING;
 
665
    p_url->port = 0;
 
666
    p_url->user = NULL;
 
667
    p_url->pass = NULL;
 
668
    p_url->host = NULL;
 
669
    p_url->is_nocache = 0;
 
670
    p_url->file = NULL;
 
671
    p_url->real_file = NULL;
 
672
    p_url->query = NULL;
 
673
    p_url->label = NULL;
 
674
 
 
675
    /* RFC1808: Relative Uniform Resource Locators
 
676
     * 4.  Resolving Relative URLs
 
677
     */
 
678
    if (*url == '\0' || *url == '#') {
 
679
        if (current)
 
680
            copyParsedURL(p_url, current);
 
681
        goto do_label;
 
682
    }
 
683
#if defined( __EMX__ ) || defined( __CYGWIN__ )
 
684
    if (!strncmp(url, "file://localhost/", 17)) {
 
685
        p_url->scheme = SCM_LOCAL;
 
686
        p += 17 - 1;
 
687
        url += 17 - 1;
 
688
    }
 
689
#endif
 
690
#ifdef SUPPORT_DOS_DRIVE_PREFIX
 
691
    if (IS_ALPHA(*p) && (p[1] == ':' || p[1] == '|')) {
 
692
        p_url->scheme = SCM_LOCAL;
 
693
        goto analyze_file;
 
694
    }
 
695
#endif                          /* SUPPORT_DOS_DRIVE_PREFIX */
 
696
    /* search for scheme */
 
697
    p_url->scheme = getURLScheme(&p);
 
698
    if (p_url->scheme == SCM_MISSING) {
 
699
        /* scheme part is not found in the url. This means either
 
700
         * (a) the url is relative to the current or (b) the url
 
701
         * denotes a filename (therefore the scheme is SCM_LOCAL).
 
702
         */
 
703
        if (current) {
 
704
            switch (current->scheme) {
 
705
            case SCM_LOCAL:
 
706
            case SCM_LOCAL_CGI:
 
707
                p_url->scheme = SCM_LOCAL;
 
708
                break;
 
709
            case SCM_FTP:
 
710
            case SCM_FTPDIR:
 
711
                p_url->scheme = SCM_FTP;
 
712
                break;
 
713
#ifdef USE_NNTP
 
714
            case SCM_NNTP:
 
715
            case SCM_NNTP_GROUP:
 
716
                p_url->scheme = SCM_NNTP;
 
717
                break;
 
718
            case SCM_NEWS:
 
719
            case SCM_NEWS_GROUP:
 
720
                p_url->scheme = SCM_NEWS;
 
721
                break;
 
722
#endif
 
723
            default:
 
724
                p_url->scheme = current->scheme;
 
725
                break;
 
726
            }
 
727
        }
 
728
        else
 
729
            p_url->scheme = SCM_LOCAL;
 
730
        p = url;
 
731
        if (!strncmp(p, "//", 2)) {
 
732
            /* URL begins with // */
 
733
            /* it means that 'scheme:' is abbreviated */
 
734
            p += 2;
 
735
            goto analyze_url;
 
736
        }
 
737
        /* the url doesn't begin with '//' */
 
738
        goto analyze_file;
 
739
    }
 
740
    /* scheme part has been found */
 
741
    if (p_url->scheme == SCM_UNKNOWN) {
 
742
        p_url->file = allocStr(url, -1);
 
743
        return;
 
744
    }
 
745
    /* get host and port */
 
746
    if (p[0] != '/' || p[1] != '/') {   /* scheme:foo or scheme:/foo */
 
747
        p_url->host = NULL;
 
748
        if (p_url->scheme != SCM_UNKNOWN)
 
749
            p_url->port = DefaultPort[p_url->scheme];
 
750
        else
 
751
            p_url->port = 0;
 
752
        goto analyze_file;
 
753
    }
 
754
    /* after here, p begins with // */
 
755
    if (p_url->scheme == SCM_LOCAL) {   /* file://foo           */
 
756
#ifdef __EMX__
 
757
        p += 2;
 
758
        goto analyze_file;
 
759
#else
 
760
        if (p[2] == '/' || p[2] == '~'
 
761
            /* <A HREF="file:///foo">file:///foo</A>  or <A HREF="file://~user">file://~user</A> */
 
762
#ifdef SUPPORT_DOS_DRIVE_PREFIX
 
763
            || (IS_ALPHA(p[2]) && (p[3] == ':' || p[3] == '|'))
 
764
            /* <A HREF="file://DRIVE/foo">file://DRIVE/foo</A> */
 
765
#endif                          /* SUPPORT_DOS_DRIVE_PREFIX */
 
766
            ) {
 
767
            p += 2;
 
768
            goto analyze_file;
 
769
        }
 
770
#endif                          /* __EMX__ */
 
771
    }
 
772
    p += 2;                     /* scheme://foo         */
 
773
    /*          ^p is here  */
 
774
  analyze_url:
 
775
    q = p;
 
776
#ifdef INET6
 
777
    if (*q == '[') {            /* rfc2732,rfc2373 compliance */
 
778
        p++;
 
779
        while (IS_XDIGIT(*p) || *p == ':' || *p == '.')
 
780
            p++;
 
781
        if (*p != ']' || (*(p + 1) && strchr(":/?#", *(p + 1)) == NULL))
 
782
            p = q;
 
783
    }
 
784
#endif
 
785
    while (*p && strchr(":/@?#", *p) == NULL)
 
786
        p++;
 
787
    switch (*p) {
 
788
    case ':':
 
789
        /* scheme://user:pass@host or
 
790
         * scheme://host:port
 
791
         */
 
792
        p_url->host = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
 
793
        q = ++p;
 
794
        while (*p && strchr("@/?#", *p) == NULL)
 
795
            p++;
 
796
        if (*p == '@') {
 
797
            /* scheme://user:pass@...       */
 
798
            p_url->pass = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
 
799
            q = ++p;
 
800
            p_url->user = p_url->host;
 
801
            p_url->host = NULL;
 
802
            goto analyze_url;
 
803
        }
 
804
        /* scheme://host:port/ */
 
805
        tmp = Strnew_charp_n(q, p - q);
 
806
        p_url->port = atoi(tmp->ptr);
 
807
        /* *p is one of ['\0', '/', '?', '#'] */
 
808
        break;
 
809
    case '@':
 
810
        /* scheme://user@...            */
 
811
        p_url->user = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
 
812
        q = ++p;
 
813
        goto analyze_url;
 
814
    case '\0':
 
815
        /* scheme://host                */
 
816
    case '/':
 
817
    case '?':
 
818
    case '#':
 
819
        p_url->host = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
 
820
        p_url->port = DefaultPort[p_url->scheme];
 
821
        break;
 
822
    }
 
823
  analyze_file:
 
824
#ifndef SUPPORT_NETBIOS_SHARE
 
825
    if (p_url->scheme == SCM_LOCAL && p_url->user == NULL &&
 
826
        p_url->host != NULL && *p_url->host != '\0' &&
 
827
        strcmp(p_url->host, "localhost")) {
 
828
        /*
 
829
         * In the environments other than CYGWIN, a URL like 
 
830
         * file://host/file is regarded as ftp://host/file.
 
831
         * On the other hand, file://host/file on CYGWIN is
 
832
         * regarded as local access to the file //host/file.
 
833
         * `host' is a netbios-hostname, drive, or any other
 
834
         * name; It is CYGWIN system call who interprets that.
 
835
         */
 
836
 
 
837
        p_url->scheme = SCM_FTP;        /* ftp://host/... */
 
838
        if (p_url->port == 0)
 
839
            p_url->port = DefaultPort[SCM_FTP];
 
840
    }
 
841
#endif
 
842
    if ((*p == '\0' || *p == '#' || *p == '?') && p_url->host == NULL) {
 
843
        p_url->file = "";
 
844
        goto do_query;
 
845
    }
 
846
#ifdef SUPPORT_DOS_DRIVE_PREFIX
 
847
    if (p_url->scheme == SCM_LOCAL) {
 
848
        q = p;
 
849
        if (*q == '/')
 
850
            q++;
 
851
        if (IS_ALPHA(q[0]) && (q[1] == ':' || q[1] == '|')) {
 
852
            if (q[1] == '|') {
 
853
                p = allocStr(q, -1);
 
854
                p[1] = ':';
 
855
            }
 
856
            else
 
857
                p = q;
 
858
        }
 
859
    }
 
860
#endif
 
861
 
 
862
    q = p;
 
863
#ifdef USE_GOPHER
 
864
    if (p_url->scheme == SCM_GOPHER) {
 
865
        if (*q == '/')
 
866
            q++;
 
867
        if (*q && q[0] != '/' && q[1] != '/' && q[2] == '/')
 
868
            q++;
 
869
    }
 
870
#endif                          /* USE_GOPHER */
 
871
    if (*p == '/')
 
872
        p++;
 
873
    if (*p == '\0' || *p == '#' || *p == '?') { /* scheme://host[:port]/ */
 
874
        p_url->file = DefaultFile(p_url->scheme);
 
875
        goto do_query;
 
876
    }
 
877
#ifdef USE_GOPHER
 
878
    if (p_url->scheme == SCM_GOPHER && *p == 'R') {
 
879
        p++;
 
880
        tmp = Strnew();
 
881
        Strcat_char(tmp, *(p++));
 
882
        while (*p && *p != '/')
 
883
            p++;
 
884
        Strcat_charp(tmp, p);
 
885
        while (*p)
 
886
            p++;
 
887
        p_url->file = copyPath(tmp->ptr, -1, COPYPATH_SPC_IGNORE);
 
888
    }
 
889
    else
 
890
#endif                          /* USE_GOPHER */
 
891
    {
 
892
        char *cgi = strchr(p, '?');
 
893
      again:
 
894
        while (*p && *p != '#' && p != cgi)
 
895
            p++;
 
896
        if (*p == '#' && p_url->scheme == SCM_LOCAL) {
 
897
            /* 
 
898
             * According to RFC2396, # means the beginning of
 
899
             * URI-reference, and # should be escaped.  But,
 
900
             * if the scheme is SCM_LOCAL, the special
 
901
             * treatment will apply to # for convinience.
 
902
             */
 
903
            if (p > q && *(p - 1) == '/' && (cgi == NULL || p < cgi)) {
 
904
                /* 
 
905
                 * # comes as the first character of the file name
 
906
                 * that means, # is not a label but a part of the file
 
907
                 * name.
 
908
                 */
 
909
                p++;
 
910
                goto again;
 
911
            }
 
912
            else if (*(p + 1) == '\0') {
 
913
                /* 
 
914
                 * # comes as the last character of the file name that
 
915
                 * means, # is not a label but a part of the file
 
916
                 * name.
 
917
                 */
 
918
                p++;
 
919
            }
 
920
        }
 
921
        if (p_url->scheme == SCM_LOCAL || p_url->scheme == SCM_MISSING)
 
922
            p_url->file = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
 
923
        else
 
924
            p_url->file = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
 
925
    }
 
926
 
 
927
  do_query:
 
928
    if (*p == '?') {
 
929
        q = ++p;
 
930
        while (*p && *p != '#')
 
931
            p++;
 
932
        p_url->query = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
 
933
    }
 
934
  do_label:
 
935
    if (p_url->scheme == SCM_MISSING) {
 
936
        p_url->scheme = SCM_LOCAL;
 
937
        p_url->file = allocStr(p, -1);
 
938
        p_url->label = NULL;
 
939
    }
 
940
    else if (*p == '#')
 
941
        p_url->label = allocStr(p + 1, -1);
 
942
    else
 
943
        p_url->label = NULL;
 
944
}
 
945
 
 
946
#define initParsedURL(p) bzero(p,sizeof(ParsedURL))
 
947
#define ALLOC_STR(s) ((s)==NULL?NULL:allocStr(s,-1))
 
948
 
 
949
void
 
950
copyParsedURL(ParsedURL *p, ParsedURL *q)
 
951
{
 
952
    p->scheme = q->scheme;
 
953
    p->port = q->port;
 
954
    p->is_nocache = q->is_nocache;
 
955
    p->user = ALLOC_STR(q->user);
 
956
    p->pass = ALLOC_STR(q->pass);
 
957
    p->host = ALLOC_STR(q->host);
 
958
    p->file = ALLOC_STR(q->file);
 
959
    p->real_file = ALLOC_STR(q->real_file);
 
960
    p->label = ALLOC_STR(q->label);
 
961
    p->query = ALLOC_STR(q->query);
 
962
}
 
963
 
 
964
void
 
965
parseURL2(char *url, ParsedURL *pu, ParsedURL *current)
 
966
{
 
967
    char *p;
 
968
    Str tmp;
 
969
    int relative_uri = FALSE;
 
970
 
 
971
    parseURL(url, pu, current);
 
972
#ifndef USE_W3MMAILER
 
973
    if (pu->scheme == SCM_MAILTO)
 
974
        return;
 
975
#endif
 
976
    if (pu->scheme == SCM_DATA)
 
977
        return;
 
978
    if (pu->scheme == SCM_NEWS || pu->scheme == SCM_NEWS_GROUP) {
 
979
        if (pu->file && !strchr(pu->file, '@') &&
 
980
            (!(p = strchr(pu->file, '/')) || strchr(p + 1, '-') ||
 
981
             *(p + 1) == '\0'))
 
982
            pu->scheme = SCM_NEWS_GROUP;
 
983
        else
 
984
            pu->scheme = SCM_NEWS;
 
985
        return;
 
986
    }
 
987
    if (pu->scheme == SCM_NNTP || pu->scheme == SCM_NNTP_GROUP) {
 
988
        if (pu->file && *pu->file == '/')
 
989
            pu->file = allocStr(pu->file + 1, -1);
 
990
        if (pu->file && !strchr(pu->file, '@') &&
 
991
            (!(p = strchr(pu->file, '/')) || strchr(p + 1, '-') ||
 
992
             *(p + 1) == '\0'))
 
993
            pu->scheme = SCM_NNTP_GROUP;
 
994
        else
 
995
            pu->scheme = SCM_NNTP;
 
996
        if (current && (current->scheme == SCM_NNTP ||
 
997
                        current->scheme == SCM_NNTP_GROUP)) {
 
998
            if (pu->host == NULL) {
 
999
                pu->host = current->host;
 
1000
                pu->port = current->port;
 
1001
            }
 
1002
        }
 
1003
        return;
 
1004
    }
 
1005
    if (pu->scheme == SCM_LOCAL) {
 
1006
        char *q = expandName(file_unquote(pu->file));
 
1007
#ifdef SUPPORT_DOS_DRIVE_PREFIX
 
1008
        Str drive;
 
1009
        if (IS_ALPHA(q[0]) && q[1] == ':') {
 
1010
            drive = Strnew_charp_n(q, 2);
 
1011
            Strcat_charp(drive, file_quote(q+2));
 
1012
            pu->file = drive->ptr;
 
1013
        }
 
1014
        else
 
1015
#endif
 
1016
            pu->file = file_quote(q);
 
1017
    }
 
1018
 
 
1019
    if (current && (pu->scheme == current->scheme ||
 
1020
                    (pu->scheme == SCM_FTP && current->scheme == SCM_FTPDIR) ||
 
1021
                    (pu->scheme == SCM_LOCAL &&
 
1022
                     current->scheme == SCM_LOCAL_CGI))
 
1023
        && pu->host == NULL) {
 
1024
        /* Copy omitted element from the current URL */
 
1025
        pu->user = current->user;
 
1026
        pu->pass = current->pass;
 
1027
        pu->host = current->host;
 
1028
        pu->port = current->port;
 
1029
        if (pu->file && *pu->file) {
 
1030
#ifdef USE_EXTERNAL_URI_LOADER
 
1031
            if (pu->scheme == SCM_UNKNOWN
 
1032
                && strchr(pu->file, ':') == NULL
 
1033
                && current && (p = strchr(current->file, ':')) != NULL) {
 
1034
                pu->file = Sprintf("%s:%s",
 
1035
                                   allocStr(current->file,
 
1036
                                            p - current->file), pu->file)->ptr;
 
1037
            }
 
1038
            else
 
1039
#endif
 
1040
                if (
 
1041
#ifdef USE_GOPHER
 
1042
                       pu->scheme != SCM_GOPHER &&
 
1043
#endif                          /* USE_GOPHER */
 
1044
                       pu->file[0] != '/'
 
1045
#ifdef SUPPORT_DOS_DRIVE_PREFIX
 
1046
                       && !(pu->scheme == SCM_LOCAL && IS_ALPHA(pu->file[0])
 
1047
                            && pu->file[1] == ':')
 
1048
#endif
 
1049
                ) {
 
1050
                /* file is relative [process 1] */
 
1051
                p = pu->file;
 
1052
                if (current->file) {
 
1053
                    tmp = Strnew_charp(current->file);
 
1054
                    while (tmp->length > 0) {
 
1055
                        if (Strlastchar(tmp) == '/')
 
1056
                            break;
 
1057
                        Strshrink(tmp, 1);
 
1058
                    }
 
1059
                    Strcat_charp(tmp, p);
 
1060
                    pu->file = tmp->ptr;
 
1061
                    relative_uri = TRUE;
 
1062
                }
 
1063
            }
 
1064
#ifdef USE_GOPHER
 
1065
            else if (pu->scheme == SCM_GOPHER && pu->file[0] == '/') {
 
1066
                p = pu->file;
 
1067
                pu->file = allocStr(p + 1, -1);
 
1068
            }
 
1069
#endif                          /* USE_GOPHER */
 
1070
        }
 
1071
        else {                  /* scheme:[?query][#label] */
 
1072
            pu->file = current->file;
 
1073
            if (!pu->query)
 
1074
                pu->query = current->query;
 
1075
        }
 
1076
        /* comment: query part need not to be completed
 
1077
         * from the current URL. */
 
1078
    }
 
1079
    if (pu->file) {
 
1080
#ifdef __EMX__
 
1081
        if (pu->scheme == SCM_LOCAL) {
 
1082
            if (strncmp(pu->file, "/$LIB/", 6)) {
 
1083
                char abs[_MAX_PATH];
 
1084
 
 
1085
                _abspath(abs, file_unquote(pu->file), _MAX_PATH);
 
1086
                pu->file = file_quote(cleanupName(abs));
 
1087
            }
 
1088
        }
 
1089
#else
 
1090
        if (pu->scheme == SCM_LOCAL && pu->file[0] != '/' &&
 
1091
#ifdef SUPPORT_DOS_DRIVE_PREFIX /* for 'drive:' */
 
1092
            !(IS_ALPHA(pu->file[0]) && pu->file[1] == ':') &&
 
1093
#endif
 
1094
            strcmp(pu->file, "-")) {
 
1095
            /* local file, relative path */
 
1096
            tmp = Strnew_charp(CurrentDir);
 
1097
            if (Strlastchar(tmp) != '/')
 
1098
                Strcat_char(tmp, '/');
 
1099
            Strcat_charp(tmp, file_unquote(pu->file));
 
1100
            pu->file = file_quote(cleanupName(tmp->ptr));
 
1101
        }
 
1102
#endif
 
1103
        else if (pu->scheme == SCM_HTTP
 
1104
#ifdef USE_SSL
 
1105
                 || pu->scheme == SCM_HTTPS
 
1106
#endif
 
1107
            ) {
 
1108
            if (relative_uri) {
 
1109
                /* In this case, pu->file is created by [process 1] above.
 
1110
                 * pu->file may contain relative path (for example, 
 
1111
                 * "/foo/../bar/./baz.html"), cleanupName() must be applied.
 
1112
                 * When the entire abs_path is given, it still may contain
 
1113
                 * elements like `//', `..' or `.' in the pu->file. It is 
 
1114
                 * server's responsibility to canonicalize such path.
 
1115
                 */
 
1116
                pu->file = cleanupName(pu->file);
 
1117
            }
 
1118
        }
 
1119
        else if (
 
1120
#ifdef USE_GOPHER
 
1121
                    pu->scheme != SCM_GOPHER &&
 
1122
#endif                          /* USE_GOPHER */
 
1123
                    pu->file[0] == '/') {
 
1124
            /*
 
1125
             * this happens on the following conditions:
 
1126
             * (1) ftp scheme (2) local, looks like absolute path.
 
1127
             * In both case, there must be no side effect with
 
1128
             * cleanupName(). (I hope so...)
 
1129
             */
 
1130
            pu->file = cleanupName(pu->file);
 
1131
        }
 
1132
        if (pu->scheme == SCM_LOCAL) {
 
1133
#ifdef SUPPORT_NETBIOS_SHARE
 
1134
            if (pu->host && strcmp(pu->host, "localhost") != 0) {
 
1135
                Str tmp = Strnew_charp("//");
 
1136
                Strcat_m_charp(tmp, pu->host,
 
1137
                               cleanupName(file_unquote(pu->file)), NULL);
 
1138
                pu->real_file = tmp->ptr;
 
1139
            }
 
1140
            else
 
1141
#endif
 
1142
                pu->real_file = cleanupName(file_unquote(pu->file));
 
1143
        }
 
1144
    }
 
1145
}
 
1146
 
 
1147
static Str
 
1148
_parsedURL2Str(ParsedURL *pu, int pass)
 
1149
{
 
1150
    Str tmp;
 
1151
    static char *scheme_str[] = {
 
1152
        "http", "gopher", "ftp", "ftp", "file", "file", "exec", "nntp", "nntp",
 
1153
        "news", "news", "data", "mailto",
 
1154
#ifdef USE_SSL
 
1155
        "https",
 
1156
#endif                          /* USE_SSL */
 
1157
    };
 
1158
 
 
1159
    if (pu->scheme == SCM_MISSING) {
 
1160
        return Strnew_charp("???");
 
1161
    }
 
1162
    else if (pu->scheme == SCM_UNKNOWN) {
 
1163
        return Strnew_charp(pu->file);
 
1164
    }
 
1165
    if (pu->host == NULL && pu->file == NULL && pu->label != NULL) {
 
1166
        /* local label */
 
1167
        return Sprintf("#%s", pu->label);
 
1168
    }
 
1169
    if (pu->scheme == SCM_LOCAL && !strcmp(pu->file, "-")) {
 
1170
        tmp = Strnew_charp("-");
 
1171
        if (pu->label) {
 
1172
            Strcat_char(tmp, '#');
 
1173
            Strcat_charp(tmp, pu->label);
 
1174
        }
 
1175
        return tmp;
 
1176
    }
 
1177
    tmp = Strnew_charp(scheme_str[pu->scheme]);
 
1178
    Strcat_char(tmp, ':');
 
1179
#ifndef USE_W3MMAILER
 
1180
    if (pu->scheme == SCM_MAILTO) {
 
1181
        Strcat_charp(tmp, pu->file);
 
1182
        if (pu->query) {
 
1183
            Strcat_char(tmp, '?');
 
1184
            Strcat_charp(tmp, pu->query);
 
1185
        }
 
1186
        return tmp;
 
1187
    }
 
1188
#endif
 
1189
    if (pu->scheme == SCM_DATA) {
 
1190
        Strcat_charp(tmp, pu->file);
 
1191
        return tmp;
 
1192
    }
 
1193
#ifdef USE_NNTP
 
1194
    if (pu->scheme != SCM_NEWS && pu->scheme != SCM_NEWS_GROUP)
 
1195
#endif                          /* USE_NNTP */
 
1196
    {
 
1197
        Strcat_charp(tmp, "//");
 
1198
    }
 
1199
    if (pu->user) {
 
1200
        Strcat_charp(tmp, pu->user);
 
1201
        if (pass && pu->pass) {
 
1202
            Strcat_char(tmp, ':');
 
1203
            Strcat_charp(tmp, pu->pass);
 
1204
        }
 
1205
        Strcat_char(tmp, '@');
 
1206
    }
 
1207
    if (pu->host) {
 
1208
        Strcat_charp(tmp, pu->host);
 
1209
        if (pu->port != DefaultPort[pu->scheme]) {
 
1210
            Strcat_char(tmp, ':');
 
1211
            Strcat(tmp, Sprintf("%d", pu->port));
 
1212
        }
 
1213
    }
 
1214
    if (
 
1215
#ifdef USE_NNTP
 
1216
           pu->scheme != SCM_NEWS && pu->scheme != SCM_NEWS_GROUP &&
 
1217
#endif                          /* USE_NNTP */
 
1218
           (pu->file == NULL || (pu->file[0] != '/'
 
1219
#ifdef SUPPORT_DOS_DRIVE_PREFIX
 
1220
                                 && !(IS_ALPHA(pu->file[0])
 
1221
                                      && pu->file[1] == ':'
 
1222
                                      && pu->host == NULL)
 
1223
#endif
 
1224
            )))
 
1225
        Strcat_char(tmp, '/');
 
1226
    Strcat_charp(tmp, pu->file);
 
1227
    if (pu->scheme == SCM_FTPDIR && Strlastchar(tmp) != '/')
 
1228
        Strcat_char(tmp, '/');
 
1229
    if (pu->query) {
 
1230
        Strcat_char(tmp, '?');
 
1231
        Strcat_charp(tmp, pu->query);
 
1232
    }
 
1233
    if (pu->label) {
 
1234
        Strcat_char(tmp, '#');
 
1235
        Strcat_charp(tmp, pu->label);
 
1236
    }
 
1237
    return tmp;
 
1238
}
 
1239
 
 
1240
Str
 
1241
parsedURL2Str(ParsedURL *pu)
 
1242
{
 
1243
    return _parsedURL2Str(pu, FALSE);
 
1244
}
 
1245
 
 
1246
int
 
1247
getURLScheme(char **url)
 
1248
{
 
1249
    char *p = *url, *q;
 
1250
    int i;
 
1251
    int scheme = SCM_MISSING;
 
1252
 
 
1253
    while (*p && (IS_ALNUM(*p) || *p == '.' || *p == '+' || *p == '-'))
 
1254
        p++;
 
1255
    if (*p == ':') {            /* scheme found */
 
1256
        scheme = SCM_UNKNOWN;
 
1257
        for (i = 0; (q = schemetable[i].cmdname) != NULL; i++) {
 
1258
            int len = strlen(q);
 
1259
            if (!strncasecmp(q, *url, len) && (*url)[len] == ':') {
 
1260
                scheme = schemetable[i].cmd;
 
1261
                *url = p + 1;
 
1262
                break;
 
1263
            }
 
1264
        }
 
1265
    }
 
1266
    return scheme;
 
1267
}
 
1268
 
 
1269
static char *
 
1270
otherinfo(ParsedURL *target, ParsedURL *current, char *referer)
 
1271
{
 
1272
    Str s = Strnew();
 
1273
 
 
1274
    Strcat_charp(s, "User-Agent: ");
 
1275
    if (UserAgent == NULL || *UserAgent == '\0')
 
1276
        Strcat_charp(s, w3m_version);
 
1277
    else
 
1278
        Strcat_charp(s, UserAgent);
 
1279
    Strcat_charp(s, "\r\n");
 
1280
 
 
1281
    Strcat_m_charp(s, "Accept: ", AcceptMedia, "\r\n", NULL);
 
1282
    Strcat_m_charp(s, "Accept-Encoding: ", AcceptEncoding, "\r\n", NULL);
 
1283
    Strcat_m_charp(s, "Accept-Language: ", AcceptLang, "\r\n", NULL);
 
1284
 
 
1285
    if (target->host) {
 
1286
        Strcat_charp(s, "Host: ");
 
1287
        Strcat_charp(s, target->host);
 
1288
        if (target->port != DefaultPort[target->scheme])
 
1289
            Strcat(s, Sprintf(":%d", target->port));
 
1290
        Strcat_charp(s, "\r\n");
 
1291
    }
 
1292
    if (target->is_nocache || NoCache) {
 
1293
        Strcat_charp(s, "Pragma: no-cache\r\n");
 
1294
        Strcat_charp(s, "Cache-control: no-cache\r\n");
 
1295
    }
 
1296
    if (!NoSendReferer) {
 
1297
        if (referer == NULL && current && current->scheme != SCM_LOCAL &&
 
1298
            (current->scheme != SCM_FTP ||
 
1299
             (current->user == NULL && current->pass == NULL))) {
 
1300
            char *p = current->label;
 
1301
            Strcat_charp(s, "Referer: ");
 
1302
            current->label = NULL;
 
1303
            Strcat(s, parsedURL2Str(current));
 
1304
            current->label = p;
 
1305
            Strcat_charp(s, "\r\n");
 
1306
        }
 
1307
        else if (referer != NULL && referer != NO_REFERER) {
 
1308
            char *p = strchr(referer, '#');
 
1309
            Strcat_charp(s, "Referer: ");
 
1310
            if (p)
 
1311
                Strcat_charp_n(s, referer, p - referer);
 
1312
            else
 
1313
                Strcat_charp(s, referer);
 
1314
            Strcat_charp(s, "\r\n");
 
1315
        }
 
1316
    }
 
1317
    return s->ptr;
 
1318
}
 
1319
 
 
1320
Str
 
1321
HTTPrequestMethod(HRequest *hr)
 
1322
{
 
1323
    switch (hr->command) {
 
1324
    case HR_COMMAND_CONNECT:
 
1325
        return Strnew_charp("CONNECT");
 
1326
    case HR_COMMAND_POST:
 
1327
        return Strnew_charp("POST");
 
1328
        break;
 
1329
    case HR_COMMAND_HEAD:
 
1330
        return Strnew_charp("HEAD");
 
1331
        break;
 
1332
    case HR_COMMAND_GET:
 
1333
    default:
 
1334
        return Strnew_charp("GET");
 
1335
    }
 
1336
    return NULL;
 
1337
}
 
1338
 
 
1339
Str
 
1340
HTTPrequestURI(ParsedURL *pu, HRequest *hr)
 
1341
{
 
1342
    Str tmp = Strnew();
 
1343
    if (hr->command == HR_COMMAND_CONNECT) {
 
1344
        Strcat_charp(tmp, pu->host);
 
1345
        Strcat(tmp, Sprintf(":%d", pu->port));
 
1346
    }
 
1347
    else if (hr->flag & HR_FLAG_LOCAL) {
 
1348
        Strcat_charp(tmp, pu->file);
 
1349
        if (pu->query) {
 
1350
            Strcat_char(tmp, '?');
 
1351
            Strcat_charp(tmp, pu->query);
 
1352
        }
 
1353
    }
 
1354
    else {
 
1355
        char *save_label = pu->label;
 
1356
        pu->label = NULL;
 
1357
        Strcat(tmp, _parsedURL2Str(pu, TRUE));
 
1358
        pu->label = save_label;
 
1359
    }
 
1360
    return tmp;
 
1361
}
 
1362
 
 
1363
static Str
 
1364
HTTPrequest(ParsedURL *pu, ParsedURL *current, HRequest *hr, TextList *extra)
 
1365
{
 
1366
    Str tmp;
 
1367
    TextListItem *i;
 
1368
    int seen_www_auth = 0;
 
1369
    int seen_proxy_auth = 0;
 
1370
#ifdef USE_COOKIE
 
1371
    Str cookie;
 
1372
#endif                          /* USE_COOKIE */
 
1373
    tmp = HTTPrequestMethod(hr);
 
1374
    Strcat_charp(tmp, " ");
 
1375
    Strcat_charp(tmp, HTTPrequestURI(pu, hr)->ptr);
 
1376
    Strcat_charp(tmp, " HTTP/1.0\r\n");
 
1377
    if (hr->referer == NO_REFERER)
 
1378
        Strcat_charp(tmp, otherinfo(pu, NULL, NULL));
 
1379
    else
 
1380
        Strcat_charp(tmp, otherinfo(pu, current, hr->referer));
 
1381
    if (extra != NULL)
 
1382
        for (i = extra->first; i != NULL; i = i->next) {
 
1383
            if (strncasecmp(i->ptr, "Authorization:",
 
1384
                            sizeof("Authorization:") - 1) == 0) {
 
1385
                seen_www_auth = 1;
 
1386
#ifdef USE_SSL
 
1387
                if (hr->command == HR_COMMAND_CONNECT)
 
1388
                    continue;
 
1389
#endif
 
1390
            }
 
1391
            if (strncasecmp(i->ptr, "Proxy-Authorization:",
 
1392
                            sizeof("Proxy-Authorization:") - 1) == 0) {
 
1393
                seen_proxy_auth = 1;
 
1394
#ifdef USE_SSL
 
1395
                if (pu->scheme == SCM_HTTPS
 
1396
                    && hr->command != HR_COMMAND_CONNECT)
 
1397
                    continue;
 
1398
#endif
 
1399
            }
 
1400
            Strcat_charp(tmp, i->ptr);
 
1401
        }
 
1402
 
 
1403
#ifdef USE_COOKIE
 
1404
    if (hr->command != HR_COMMAND_CONNECT &&
 
1405
        use_cookie && (cookie = find_cookie(pu))) {
 
1406
        Strcat_charp(tmp, "Cookie: ");
 
1407
        Strcat(tmp, cookie);
 
1408
        Strcat_charp(tmp, "\r\n");
 
1409
        /* [DRAFT 12] s. 10.1 */
 
1410
        if (cookie->ptr[0] != '$')
 
1411
            Strcat_charp(tmp, "Cookie2: $Version=\"1\"\r\n");
 
1412
    }
 
1413
#endif                          /* USE_COOKIE */
 
1414
    if (hr->command == HR_COMMAND_POST) {
 
1415
        if (hr->request->enctype == FORM_ENCTYPE_MULTIPART) {
 
1416
            Strcat_charp(tmp, "Content-type: multipart/form-data; boundary=");
 
1417
            Strcat_charp(tmp, hr->request->boundary);
 
1418
            Strcat_charp(tmp, "\r\n");
 
1419
            Strcat(tmp,
 
1420
                   Sprintf("Content-length: %ld\r\n", hr->request->length));
 
1421
            Strcat_charp(tmp, "\r\n");
 
1422
        }
 
1423
        else {
 
1424
            if (!override_content_type) {
 
1425
                Strcat_charp(tmp,
 
1426
                             "Content-type: application/x-www-form-urlencoded\r\n");
 
1427
            }
 
1428
            Strcat(tmp,
 
1429
                   Sprintf("Content-length: %ld\r\n", hr->request->length));
 
1430
            if (header_string)
 
1431
                Strcat(tmp, header_string);
 
1432
            Strcat_charp(tmp, "\r\n");
 
1433
            Strcat_charp_n(tmp, hr->request->body, hr->request->length);
 
1434
            Strcat_charp(tmp, "\r\n");
 
1435
        }
 
1436
    }
 
1437
    else {
 
1438
        if (header_string)
 
1439
            Strcat(tmp, header_string);
 
1440
        Strcat_charp(tmp, "\r\n");
 
1441
    }
 
1442
#ifdef DEBUG
 
1443
    fprintf(stderr, "HTTPrequest: [ %s ]\n\n", tmp->ptr);
 
1444
#endif                          /* DEBUG */
 
1445
    return tmp;
 
1446
}
 
1447
 
 
1448
void
 
1449
init_stream(URLFile *uf, int scheme, InputStream stream)
 
1450
{
 
1451
    memset(uf, 0, sizeof(URLFile));
 
1452
    uf->stream = stream;
 
1453
    uf->scheme = scheme;
 
1454
    uf->encoding = ENC_7BIT;
 
1455
    uf->is_cgi = FALSE;
 
1456
    uf->compression = 0;
 
1457
    uf->guess_type = NULL;
 
1458
    uf->ext = NULL;
 
1459
    uf->modtime = -1;
 
1460
}
 
1461
 
 
1462
URLFile
 
1463
openURL(char *url, ParsedURL *pu, ParsedURL *current,
 
1464
        URLOption *option, FormList *request, TextList *extra_header,
 
1465
        URLFile *ouf, HRequest *hr, unsigned char *status)
 
1466
{
 
1467
    Str tmp;
 
1468
    int sock, scheme;
 
1469
    char *p, *q, *u;
 
1470
    URLFile uf;
 
1471
    HRequest hr0;
 
1472
#ifdef USE_SSL
 
1473
    SSL *sslh = NULL;
 
1474
#endif                          /* USE_SSL */
 
1475
 
 
1476
    if (hr == NULL)
 
1477
        hr = &hr0;
 
1478
 
 
1479
    if (ouf) {
 
1480
        uf = *ouf;
 
1481
    }
 
1482
    else {
 
1483
        init_stream(&uf, SCM_MISSING, NULL);
 
1484
    }
 
1485
 
 
1486
    u = url;
 
1487
    scheme = getURLScheme(&u);
 
1488
    if (current == NULL && scheme == SCM_MISSING && !ArgvIsURL)
 
1489
        u = file_to_url(url);   /* force to local file */
 
1490
    else
 
1491
        u = url;
 
1492
  retry:
 
1493
    parseURL2(u, pu, current);
 
1494
    if (pu->scheme == SCM_LOCAL && pu->file == NULL) {
 
1495
        if (pu->label != NULL) {
 
1496
            /* #hogege is not a label but a filename */
 
1497
            Str tmp2 = Strnew_charp("#");
 
1498
            Strcat_charp(tmp2, pu->label);
 
1499
            pu->file = tmp2->ptr;
 
1500
            pu->real_file = cleanupName(file_unquote(pu->file));
 
1501
            pu->label = NULL;
 
1502
        }
 
1503
        else {
 
1504
            /* given URL must be null string */
 
1505
#ifdef SOCK_DEBUG
 
1506
            sock_log("given URL must be null string\n");
 
1507
#endif
 
1508
            return uf;
 
1509
        }
 
1510
    }
 
1511
 
 
1512
    uf.scheme = pu->scheme;
 
1513
    uf.url = parsedURL2Str(pu)->ptr;
 
1514
    pu->is_nocache = (option->flag & RG_NOCACHE);
 
1515
    uf.ext = filename_extension(pu->file, 1);
 
1516
 
 
1517
    hr->command = HR_COMMAND_GET;
 
1518
    hr->flag = 0;
 
1519
    hr->referer = option->referer;
 
1520
    hr->request = request;
 
1521
 
 
1522
    switch (pu->scheme) {
 
1523
    case SCM_LOCAL:
 
1524
    case SCM_LOCAL_CGI:
 
1525
        if (request && request->body)
 
1526
            /* local CGI: POST */
 
1527
            uf.stream = newFileStream(localcgi_post(pu->real_file, pu->query,
 
1528
                                                    request, option->referer),
 
1529
                                      (void (*)())pclose);
 
1530
        else
 
1531
            /* lodal CGI: GET */
 
1532
            uf.stream = newFileStream(localcgi_get(pu->real_file, pu->query,
 
1533
                                                   option->referer),
 
1534
                                      (void (*)())pclose);
 
1535
        if (uf.stream) {
 
1536
            uf.is_cgi = TRUE;
 
1537
            uf.scheme = pu->scheme = SCM_LOCAL_CGI;
 
1538
            return uf;
 
1539
        }
 
1540
        examineFile(pu->real_file, &uf);
 
1541
        if (uf.stream == NULL) {
 
1542
            if (dir_exist(pu->real_file)) {
 
1543
                add_index_file(pu, &uf);
 
1544
                if (uf.stream == NULL)
 
1545
                    return uf;
 
1546
            }
 
1547
            else if (document_root != NULL) {
 
1548
                tmp = Strnew_charp(document_root);
 
1549
                if (Strlastchar(tmp) != '/' && pu->file[0] != '/')
 
1550
                    Strcat_char(tmp, '/');
 
1551
                Strcat_charp(tmp, pu->file);
 
1552
                p = cleanupName(tmp->ptr);
 
1553
                q = cleanupName(file_unquote(p));
 
1554
                if (dir_exist(q)) {
 
1555
                    pu->file = p;
 
1556
                    pu->real_file = q;
 
1557
                    add_index_file(pu, &uf);
 
1558
                    if (uf.stream == NULL) {
 
1559
                        return uf;
 
1560
                    }
 
1561
                }
 
1562
                else {
 
1563
                    examineFile(q, &uf);
 
1564
                    if (uf.stream) {
 
1565
                        pu->file = p;
 
1566
                        pu->real_file = q;
 
1567
                    }
 
1568
                }
 
1569
            }
 
1570
        }
 
1571
        if (uf.stream == NULL && retryAsHttp && url[0] != '/') {
 
1572
            if (scheme == SCM_MISSING || scheme == SCM_UNKNOWN) {
 
1573
                /* retry it as "http://" */
 
1574
                u = Strnew_m_charp("http://", url, NULL)->ptr;
 
1575
                goto retry;
 
1576
            }
 
1577
        }
 
1578
        return uf;
 
1579
    case SCM_FTP:
 
1580
    case SCM_FTPDIR:
 
1581
        if (pu->file == NULL)
 
1582
            pu->file = allocStr("/", -1);
 
1583
        if (non_null(FTP_proxy) &&
 
1584
            !Do_not_use_proxy &&
 
1585
            pu->host != NULL && !check_no_proxy(pu->host)) {
 
1586
            hr->flag |= HR_FLAG_PROXY;
 
1587
            sock = openSocket(FTP_proxy_parsed.host,
 
1588
                              schemetable[FTP_proxy_parsed.scheme].cmdname,
 
1589
                              FTP_proxy_parsed.port);
 
1590
            if (sock < 0)
 
1591
                return uf;
 
1592
            uf.scheme = SCM_HTTP;
 
1593
            tmp = HTTPrequest(pu, current, hr, extra_header);
 
1594
            write(sock, tmp->ptr, tmp->length);
 
1595
        }
 
1596
        else {
 
1597
            uf.stream = openFTPStream(pu, &uf);
 
1598
            uf.scheme = pu->scheme;
 
1599
            return uf;
 
1600
        }
 
1601
        break;
 
1602
    case SCM_HTTP:
 
1603
#ifdef USE_SSL
 
1604
    case SCM_HTTPS:
 
1605
#endif                          /* USE_SSL */
 
1606
        if (pu->file == NULL)
 
1607
            pu->file = allocStr("/", -1);
 
1608
        if (request && request->method == FORM_METHOD_POST && request->body)
 
1609
            hr->command = HR_COMMAND_POST;
 
1610
        if (request && request->method == FORM_METHOD_HEAD)
 
1611
            hr->command = HR_COMMAND_HEAD;
 
1612
        if ((
 
1613
#ifdef USE_SSL
 
1614
                (pu->scheme == SCM_HTTPS) ? non_null(HTTPS_proxy) :
 
1615
#endif                          /* USE_SSL */
 
1616
                non_null(HTTP_proxy)) && !Do_not_use_proxy &&
 
1617
            pu->host != NULL && !check_no_proxy(pu->host)) {
 
1618
            hr->flag |= HR_FLAG_PROXY;
 
1619
#ifdef USE_SSL
 
1620
            if (pu->scheme == SCM_HTTPS && *status == HTST_CONNECT) {
 
1621
                sock = ssl_socket_of(ouf->stream);
 
1622
                if (!(sslh = openSSLHandle(sock, pu->host,
 
1623
                                           &uf.ssl_certificate))) {
 
1624
                    *status = HTST_MISSING;
 
1625
                    return uf;
 
1626
                }
 
1627
            }
 
1628
            else if (pu->scheme == SCM_HTTPS) {
 
1629
                sock = openSocket(HTTPS_proxy_parsed.host,
 
1630
                                  schemetable[HTTPS_proxy_parsed.scheme].
 
1631
                                  cmdname, HTTPS_proxy_parsed.port);
 
1632
                sslh = NULL;
 
1633
            }
 
1634
            else {
 
1635
#endif                          /* USE_SSL */
 
1636
                sock = openSocket(HTTP_proxy_parsed.host,
 
1637
                                  schemetable[HTTP_proxy_parsed.scheme].
 
1638
                                  cmdname, HTTP_proxy_parsed.port);
 
1639
#ifdef USE_SSL
 
1640
                sslh = NULL;
 
1641
            }
 
1642
#endif                          /* USE_SSL */
 
1643
            if (sock < 0) {
 
1644
#ifdef SOCK_DEBUG
 
1645
                sock_log("Can't open socket\n");
 
1646
#endif
 
1647
                return uf;
 
1648
            }
 
1649
#ifdef USE_SSL
 
1650
            if (pu->scheme == SCM_HTTPS) {
 
1651
                if (*status == HTST_NORMAL) {
 
1652
                    hr->command = HR_COMMAND_CONNECT;
 
1653
                    tmp = HTTPrequest(pu, current, hr, extra_header);
 
1654
                    *status = HTST_CONNECT;
 
1655
                }
 
1656
                else {
 
1657
                    hr->flag |= HR_FLAG_LOCAL;
 
1658
                    tmp = HTTPrequest(pu, current, hr, extra_header);
 
1659
                    *status = HTST_NORMAL;
 
1660
                }
 
1661
            }
 
1662
            else
 
1663
#endif                          /* USE_SSL */
 
1664
            {
 
1665
                tmp = HTTPrequest(pu, current, hr, extra_header);
 
1666
                *status = HTST_NORMAL;
 
1667
            }
 
1668
        }
 
1669
        else {
 
1670
            sock = openSocket(pu->host,
 
1671
                              schemetable[pu->scheme].cmdname, pu->port);
 
1672
            if (sock < 0) {
 
1673
                *status = HTST_MISSING;
 
1674
                return uf;
 
1675
            }
 
1676
#ifdef USE_SSL
 
1677
            if (pu->scheme == SCM_HTTPS) {
 
1678
                if (!(sslh = openSSLHandle(sock, pu->host,
 
1679
                                           &uf.ssl_certificate))) {
 
1680
                    *status = HTST_MISSING;
 
1681
                    return uf;
 
1682
                }
 
1683
            }
 
1684
#endif                          /* USE_SSL */
 
1685
            hr->flag |= HR_FLAG_LOCAL;
 
1686
            tmp = HTTPrequest(pu, current, hr, extra_header);
 
1687
            *status = HTST_NORMAL;
 
1688
        }
 
1689
#ifdef USE_SSL
 
1690
        if (pu->scheme == SCM_HTTPS) {
 
1691
            uf.stream = newSSLStream(sslh, sock);
 
1692
            if (sslh)
 
1693
                SSL_write(sslh, tmp->ptr, tmp->length);
 
1694
            else
 
1695
                write(sock, tmp->ptr, tmp->length);
 
1696
#ifdef HTTP_DEBUG
 
1697
            {
 
1698
                FILE *ff = fopen("zzrequest", "a");
 
1699
                if (sslh)
 
1700
                    fputs("HTTPS: request via SSL\n", ff);
 
1701
                else
 
1702
                    fputs("HTTPS: request without SSL\n", ff);
 
1703
                fwrite(tmp->ptr, sizeof(char), tmp->length, ff);
 
1704
                fclose(ff);
 
1705
            }
 
1706
#endif                          /* HTTP_DEBUG */
 
1707
            if (hr->command == HR_COMMAND_POST &&
 
1708
                request->enctype == FORM_ENCTYPE_MULTIPART) {
 
1709
                if (sslh)
 
1710
                    SSL_write_from_file(sslh, request->body);
 
1711
                else
 
1712
                    write_from_file(sock, request->body);
 
1713
            }
 
1714
            return uf;
 
1715
        }
 
1716
        else
 
1717
#endif                          /* USE_SSL */
 
1718
        {
 
1719
            write(sock, tmp->ptr, tmp->length);
 
1720
#ifdef HTTP_DEBUG
 
1721
            {
 
1722
                FILE *ff = fopen("zzrequest", "a");
 
1723
                fwrite(tmp->ptr, sizeof(char), tmp->length, ff);
 
1724
                fclose(ff);
 
1725
            }
 
1726
#endif                          /* HTTP_DEBUG */
 
1727
            if (hr->command == HR_COMMAND_POST &&
 
1728
                request->enctype == FORM_ENCTYPE_MULTIPART)
 
1729
                write_from_file(sock, request->body);
 
1730
        }
 
1731
        break;
 
1732
#ifdef USE_GOPHER
 
1733
    case SCM_GOPHER:
 
1734
        if (non_null(GOPHER_proxy) &&
 
1735
            !Do_not_use_proxy &&
 
1736
            pu->host != NULL && !check_no_proxy(pu->host)) {
 
1737
            hr->flag |= HR_FLAG_PROXY;
 
1738
            sock = openSocket(GOPHER_proxy_parsed.host,
 
1739
                              schemetable[GOPHER_proxy_parsed.scheme].cmdname,
 
1740
                              GOPHER_proxy_parsed.port);
 
1741
            if (sock < 0)
 
1742
                return uf;
 
1743
            uf.scheme = SCM_HTTP;
 
1744
            tmp = HTTPrequest(pu, current, hr, extra_header);
 
1745
        }
 
1746
        else {
 
1747
            sock = openSocket(pu->host,
 
1748
                              schemetable[pu->scheme].cmdname, pu->port);
 
1749
            if (sock < 0)
 
1750
                return uf;
 
1751
            if (pu->file == NULL)
 
1752
                pu->file = "1";
 
1753
            tmp = Strnew_charp(file_unquote(pu->file));
 
1754
            Strcat_char(tmp, '\n');
 
1755
        }
 
1756
        write(sock, tmp->ptr, tmp->length);
 
1757
        break;
 
1758
#endif                          /* USE_GOPHER */
 
1759
#ifdef USE_NNTP
 
1760
    case SCM_NNTP:
 
1761
    case SCM_NNTP_GROUP:
 
1762
    case SCM_NEWS:
 
1763
    case SCM_NEWS_GROUP:
 
1764
        if (pu->scheme == SCM_NNTP || pu->scheme == SCM_NEWS)
 
1765
            uf.scheme = SCM_NEWS;
 
1766
        else
 
1767
            uf.scheme = SCM_NEWS_GROUP;
 
1768
        uf.stream = openNewsStream(pu);
 
1769
        return uf;
 
1770
#endif                          /* USE_NNTP */
 
1771
    case SCM_DATA:
 
1772
        if (pu->file == NULL)
 
1773
            return uf;
 
1774
        p = Strnew_charp(pu->file)->ptr;
 
1775
        q = strchr(p, ',');
 
1776
        if (q == NULL)
 
1777
            return uf;
 
1778
        *q++ = '\0';
 
1779
        tmp = Strnew_charp(q);
 
1780
        q = strrchr(p, ';');
 
1781
        if (q != NULL && !strcmp(q, ";base64")) {
 
1782
            *q = '\0';
 
1783
            uf.encoding = ENC_BASE64;
 
1784
        }
 
1785
        else
 
1786
            tmp = Str_url_unquote(tmp, FALSE, FALSE);
 
1787
        uf.stream = newStrStream(tmp);
 
1788
        uf.guess_type = (*p != '\0') ? p : "text/plain";
 
1789
        return uf;
 
1790
    case SCM_UNKNOWN:
 
1791
    default:
 
1792
        return uf;
 
1793
    }
 
1794
    uf.stream = newInputStream(sock);
 
1795
    return uf;
 
1796
}
 
1797
 
 
1798
/* add index_file if exists */
 
1799
static void
 
1800
add_index_file(ParsedURL *pu, URLFile *uf)
 
1801
{
 
1802
    char *p, *q;
 
1803
 
 
1804
    if (index_file == NULL || index_file[0] == '\0') {
 
1805
        uf->stream = NULL;
 
1806
        return;
 
1807
    }
 
1808
    p = Strnew_m_charp(pu->file, "/", file_quote(index_file), NULL)->ptr;
 
1809
    p = cleanupName(p);
 
1810
    q = cleanupName(file_unquote(p));
 
1811
    examineFile(q, uf);
 
1812
    if (uf->stream == NULL)
 
1813
        return;
 
1814
    pu->file = p;
 
1815
    pu->real_file = q;
 
1816
    return;
 
1817
}
 
1818
 
 
1819
static char *
 
1820
guessContentTypeFromTable(struct table2 *table, char *filename)
 
1821
{
 
1822
    struct table2 *t;
 
1823
    char *p;
 
1824
    if (table == NULL)
 
1825
        return NULL;
 
1826
    p = &filename[strlen(filename) - 1];
 
1827
    while (filename < p && *p != '.')
 
1828
        p--;
 
1829
    if (p == filename)
 
1830
        return NULL;
 
1831
    p++;
 
1832
    for (t = table; t->item1; t++) {
 
1833
        if (!strcmp(p, t->item1))
 
1834
            return t->item2;
 
1835
    }
 
1836
    for (t = table; t->item1; t++) {
 
1837
        if (!strcasecmp(p, t->item1))
 
1838
            return t->item2;
 
1839
    }
 
1840
    return NULL;
 
1841
}
 
1842
 
 
1843
char *
 
1844
guessContentType(char *filename)
 
1845
{
 
1846
    char *ret;
 
1847
    int i;
 
1848
 
 
1849
    if (filename == NULL)
 
1850
        return NULL;
 
1851
    if (mimetypes_list == NULL)
 
1852
        goto no_user_mimetypes;
 
1853
 
 
1854
    for (i = 0; i < mimetypes_list->nitem; i++) {
 
1855
        if ((ret =
 
1856
             guessContentTypeFromTable(UserMimeTypes[i], filename)) != NULL)
 
1857
            return ret;
 
1858
    }
 
1859
 
 
1860
  no_user_mimetypes:
 
1861
    return guessContentTypeFromTable(DefaultGuess, filename);
 
1862
}
 
1863
 
 
1864
TextList *
 
1865
make_domain_list(char *domain_list)
 
1866
{
 
1867
    char *p;
 
1868
    Str tmp;
 
1869
    TextList *domains = NULL;
 
1870
 
 
1871
    p = domain_list;
 
1872
    tmp = Strnew_size(64);
 
1873
    while (*p) {
 
1874
        while (*p && IS_SPACE(*p))
 
1875
            p++;
 
1876
        Strclear(tmp);
 
1877
        while (*p && !IS_SPACE(*p) && *p != ',')
 
1878
            Strcat_char(tmp, *p++);
 
1879
        if (tmp->length > 0) {
 
1880
            if (domains == NULL)
 
1881
                domains = newTextList();
 
1882
            pushText(domains, tmp->ptr);
 
1883
        }
 
1884
        while (*p && IS_SPACE(*p))
 
1885
            p++;
 
1886
        if (*p == ',')
 
1887
            p++;
 
1888
    }
 
1889
    return domains;
 
1890
}
 
1891
 
 
1892
static int
 
1893
domain_match(char *pat, char *domain)
 
1894
{
 
1895
    if (domain == NULL)
 
1896
        return 0;
 
1897
    if (*pat == '.')
 
1898
        pat++;
 
1899
    for (;;) {
 
1900
        if (!strcasecmp(pat, domain))
 
1901
            return 1;
 
1902
        domain = strchr(domain, '.');
 
1903
        if (domain == NULL)
 
1904
            return 0;
 
1905
        domain++;
 
1906
    }
 
1907
}
 
1908
 
 
1909
int
 
1910
check_no_proxy(char *domain)
 
1911
{
 
1912
    TextListItem *tl;
 
1913
    volatile int ret = 0;
 
1914
    MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
 
1915
 
 
1916
    if (NO_proxy_domains == NULL || NO_proxy_domains->nitem == 0 ||
 
1917
        domain == NULL)
 
1918
        return 0;
 
1919
    for (tl = NO_proxy_domains->first; tl != NULL; tl = tl->next) {
 
1920
        if (domain_match(tl->ptr, domain))
 
1921
            return 1;
 
1922
    }
 
1923
    if (!NOproxy_netaddr) {
 
1924
        return 0;
 
1925
    }
 
1926
    /* 
 
1927
     * to check noproxy by network addr
 
1928
     */
 
1929
    if (SETJMP(AbortLoading) != 0) {
 
1930
        ret = 0;
 
1931
        goto end;
 
1932
    }
 
1933
    TRAP_ON;
 
1934
    {
 
1935
#ifndef INET6
 
1936
        struct hostent *he;
 
1937
        int n;
 
1938
        unsigned char **h_addr_list;
 
1939
        char addr[4 * 16], buf[5];
 
1940
 
 
1941
        he = gethostbyname(domain);
 
1942
        if (!he) {
 
1943
            ret = 0;
 
1944
            goto end;
 
1945
        }
 
1946
        for (h_addr_list = (unsigned char **)he->h_addr_list; *h_addr_list;
 
1947
             h_addr_list++) {
 
1948
            sprintf(addr, "%d", h_addr_list[0][0]);
 
1949
            for (n = 1; n < he->h_length; n++) {
 
1950
                sprintf(buf, ".%d", h_addr_list[0][n]);
 
1951
                strcat(addr, buf);
 
1952
            }
 
1953
            for (tl = NO_proxy_domains->first; tl != NULL; tl = tl->next) {
 
1954
                if (strncmp(tl->ptr, addr, strlen(tl->ptr)) == 0) {
 
1955
                    ret = 1;
 
1956
                    goto end;
 
1957
                }
 
1958
            }
 
1959
        }
 
1960
#else                           /* INET6 */
 
1961
        int error;
 
1962
        struct addrinfo hints;
 
1963
        struct addrinfo *res, *res0;
 
1964
        char addr[4 * 16];
 
1965
        int *af;
 
1966
 
 
1967
        for (af = ai_family_order_table[DNS_order];; af++) {
 
1968
            memset(&hints, 0, sizeof(hints));
 
1969
            hints.ai_family = *af;
 
1970
            error = getaddrinfo(domain, NULL, &hints, &res0);
 
1971
            if (error) {
 
1972
                if (*af == PF_UNSPEC) {
 
1973
                    break;
 
1974
                }
 
1975
                /* try next */
 
1976
                continue;
 
1977
            }
 
1978
            for (res = res0; res != NULL; res = res->ai_next) {
 
1979
                switch (res->ai_family) {
 
1980
                case AF_INET:
 
1981
                    inet_ntop(AF_INET,
 
1982
                              &((struct sockaddr_in *)res->ai_addr)->sin_addr,
 
1983
                              addr, sizeof(addr));
 
1984
                    break;
 
1985
                case AF_INET6:
 
1986
                    inet_ntop(AF_INET6,
 
1987
                              &((struct sockaddr_in6 *)res->ai_addr)->
 
1988
                              sin6_addr, addr, sizeof(addr));
 
1989
                    break;
 
1990
                default:
 
1991
                    /* unknown */
 
1992
                    continue;
 
1993
                }
 
1994
                for (tl = NO_proxy_domains->first; tl != NULL; tl = tl->next) {
 
1995
                    if (strncmp(tl->ptr, addr, strlen(tl->ptr)) == 0) {
 
1996
                        freeaddrinfo(res0);
 
1997
                        ret = 1;
 
1998
                        goto end;
 
1999
                    }
 
2000
                }
 
2001
            }
 
2002
            freeaddrinfo(res0);
 
2003
            if (*af == PF_UNSPEC) {
 
2004
                break;
 
2005
            }
 
2006
        }
 
2007
#endif                          /* INET6 */
 
2008
    }
 
2009
  end:
 
2010
    TRAP_OFF;
 
2011
    return ret;
 
2012
}
 
2013
 
 
2014
char *
 
2015
filename_extension(char *path, int is_url)
 
2016
{
 
2017
    char *last_dot = "", *p = path;
 
2018
    int i;
 
2019
 
 
2020
    if (path == NULL)
 
2021
        return last_dot;
 
2022
    if (*p == '.')
 
2023
        p++;
 
2024
    for (; *p; p++) {
 
2025
        if (*p == '.') {
 
2026
            last_dot = p;
 
2027
        }
 
2028
        else if (is_url && *p == '?')
 
2029
            break;
 
2030
    }
 
2031
    if (*last_dot == '.') {
 
2032
        for (i = 1; last_dot[i] && i < 8; i++) {
 
2033
            if (is_url && !IS_ALNUM(last_dot[i]))
 
2034
                break;
 
2035
        }
 
2036
        return allocStr(last_dot, i);
 
2037
    }
 
2038
    else
 
2039
        return last_dot;
 
2040
}
 
2041
 
 
2042
#ifdef USE_EXTERNAL_URI_LOADER
 
2043
static struct table2 **urimethods;
 
2044
static struct table2 default_urimethods[] = {
 
2045
    {"mailto", "file:///$LIB/w3mmail.cgi?%s"},
 
2046
    {NULL, NULL}
 
2047
};
 
2048
 
 
2049
static struct table2 *
 
2050
loadURIMethods(char *filename)
 
2051
{
 
2052
    FILE *f;
 
2053
    int i, n;
 
2054
    Str tmp;
 
2055
    struct table2 *um;
 
2056
    char *up, *p;
 
2057
 
 
2058
    f = fopen(expandPath(filename), "r");
 
2059
    if (f == NULL)
 
2060
        return NULL;
 
2061
    i = 0;
 
2062
    while (tmp = Strfgets(f), tmp->length > 0) {
 
2063
        if (tmp->ptr[0] != '#')
 
2064
            i++;
 
2065
    }
 
2066
    fseek(f, 0, 0);
 
2067
    n = i;
 
2068
    um = New_N(struct table2, n + 1);
 
2069
    i = 0;
 
2070
    while (tmp = Strfgets(f), tmp->length > 0) {
 
2071
        if (tmp->ptr[0] == '#')
 
2072
            continue;
 
2073
        while (IS_SPACE(Strlastchar(tmp)))
 
2074
            Strshrink(tmp, 1);
 
2075
        for (up = p = tmp->ptr; *p != '\0'; p++) {
 
2076
            if (*p == ':') {
 
2077
                um[i].item1 = Strnew_charp_n(up, p - up)->ptr;
 
2078
                p++;
 
2079
                break;
 
2080
            }
 
2081
        }
 
2082
        if (*p == '\0')
 
2083
            continue;
 
2084
        while (*p != '\0' && IS_SPACE(*p))
 
2085
            p++;
 
2086
        um[i].item2 = Strnew_charp(p)->ptr;
 
2087
        i++;
 
2088
    }
 
2089
    um[i].item1 = NULL;
 
2090
    um[i].item2 = NULL;
 
2091
    fclose(f);
 
2092
    return um;
 
2093
}
 
2094
 
 
2095
void
 
2096
initURIMethods()
 
2097
{
 
2098
    TextList *methodmap_list = NULL;
 
2099
    TextListItem *tl;
 
2100
    int i;
 
2101
 
 
2102
    if (non_null(urimethodmap_files))
 
2103
        methodmap_list = make_domain_list(urimethodmap_files);
 
2104
    if (methodmap_list == NULL)
 
2105
        return;
 
2106
    urimethods = New_N(struct table2 *, (methodmap_list->nitem + 1));
 
2107
    for (i = 0, tl = methodmap_list->first; tl; tl = tl->next) {
 
2108
        urimethods[i] = loadURIMethods(tl->ptr);
 
2109
        if (urimethods[i])
 
2110
            i++;
 
2111
    }
 
2112
    urimethods[i] = NULL;
 
2113
}
 
2114
 
 
2115
Str
 
2116
searchURIMethods(ParsedURL *pu)
 
2117
{
 
2118
    struct table2 *ump;
 
2119
    int i;
 
2120
    Str scheme = NULL;
 
2121
    Str url;
 
2122
    char *p;
 
2123
 
 
2124
    if (pu->scheme != SCM_UNKNOWN)
 
2125
        return NULL;            /* use internal */
 
2126
    if (urimethods == NULL)
 
2127
        return NULL;
 
2128
    url = parsedURL2Str(pu);
 
2129
    for (p = url->ptr; *p != '\0'; p++) {
 
2130
        if (*p == ':') {
 
2131
            scheme = Strnew_charp_n(url->ptr, p - url->ptr);
 
2132
            break;
 
2133
        }
 
2134
    }
 
2135
    if (scheme == NULL)
 
2136
        return NULL;
 
2137
 
 
2138
    /*
 
2139
     * RFC2396 3.1. Scheme Component
 
2140
     * For resiliency, programs interpreting URI should treat upper case
 
2141
     * letters as equivalent to lower case in scheme names (e.g., allow
 
2142
     * "HTTP" as well as "http").
 
2143
     */
 
2144
    for (i = 0; (ump = urimethods[i]) != NULL; i++) {
 
2145
        for (; ump->item1 != NULL; ump++) {
 
2146
            if (strcasecmp(ump->item1, scheme->ptr) == 0) {
 
2147
                return Sprintf(ump->item2, url_quote(url->ptr));
 
2148
            }
 
2149
        }
 
2150
    }
 
2151
    for (ump = default_urimethods; ump->item1 != NULL; ump++) {
 
2152
        if (strcasecmp(ump->item1, scheme->ptr) == 0) {
 
2153
            return Sprintf(ump->item2, url_quote(url->ptr));
 
2154
        }
 
2155
    }
 
2156
    return NULL;
 
2157
}
 
2158
 
 
2159
/*
 
2160
 * RFC2396: Uniform Resource Identifiers (URI): Generic Syntax
 
2161
 * Appendix A. Collected BNF for URI
 
2162
 * uric          = reserved | unreserved | escaped
 
2163
 * reserved      = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
 
2164
 *                 "$" | ","
 
2165
 * unreserved    = alphanum | mark
 
2166
 * mark          = "-" | "_" | "." | "!" | "~" | "*" | "'" |
 
2167
 *                  "(" | ")"
 
2168
 * escaped       = "%" hex hex
 
2169
 */
 
2170
 
 
2171
#define URI_PATTERN     "([-;/?:@&=+$,a-zA-Z0-9_.!~*'()]|%[0-9A-Fa-f][0-9A-Fa-f])*"
 
2172
void
 
2173
chkExternalURIBuffer(Buffer *buf)
 
2174
{
 
2175
    int i;
 
2176
    struct table2 *ump;
 
2177
 
 
2178
    for (i = 0; (ump = urimethods[i]) != NULL; i++) {
 
2179
        for (; ump->item1 != NULL; ump++) {
 
2180
            reAnchor(buf, Sprintf("%s:%s", ump->item1, URI_PATTERN)->ptr);
 
2181
        }
 
2182
    }
 
2183
    for (ump = default_urimethods; ump->item1 != NULL; ump++) {
 
2184
        reAnchor(buf, Sprintf("%s:%s", ump->item1, URI_PATTERN)->ptr);
 
2185
    }
 
2186
}
 
2187
#endif
 
2188
 
 
2189
ParsedURL *
 
2190
schemeToProxy(int scheme)
 
2191
{
 
2192
    ParsedURL *pu = NULL;       /* for gcc */
 
2193
    switch (scheme) {
 
2194
    case SCM_HTTP:
 
2195
        pu = &HTTP_proxy_parsed;
 
2196
        break;
 
2197
#ifdef USE_SSL
 
2198
    case SCM_HTTPS:
 
2199
        pu = &HTTPS_proxy_parsed;
 
2200
        break;
 
2201
#endif
 
2202
    case SCM_FTP:
 
2203
        pu = &FTP_proxy_parsed;
 
2204
        break;
 
2205
#ifdef USE_GOPHER
 
2206
    case SCM_GOPHER:
 
2207
        pu = &GOPHER_proxy_parsed;
 
2208
        break;
 
2209
#endif
 
2210
#ifdef DEBUG
 
2211
    default:
 
2212
        abort();
 
2213
#endif
 
2214
    }
 
2215
    return pu;
 
2216
}