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