1955 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1955 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* $Id: url.c,v 1.22 2001/12/07 07:58:07 ukai Exp $ */
 | |
| #include "fm.h"
 | |
| #include <sys/types.h>
 | |
| #include <sys/socket.h>
 | |
| #include <netinet/in.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <netdb.h>
 | |
| 
 | |
| #include <signal.h>
 | |
| #include <setjmp.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #include <sys/stat.h>
 | |
| #ifdef __EMX__
 | |
| #include <io.h>			/* ?? */
 | |
| #endif				/* __EMX__ */
 | |
| 
 | |
| #include "html.h"
 | |
| #include "Str.h"
 | |
| #include "myctype.h"
 | |
| #include "regex.h"
 | |
| 
 | |
| #ifdef USE_SSL
 | |
| #ifndef SSLEAY_VERSION_NUMBER
 | |
| #include <crypto.h>		/* SSLEAY_VERSION_NUMBER may be here */
 | |
| #endif
 | |
| #include <err.h>
 | |
| #endif
 | |
| 
 | |
| #ifdef	__WATT32__
 | |
| #define	write(a,b,c)	write_s(a,b,c)
 | |
| #endif				/* __WATT32__ */
 | |
| 
 | |
| #ifdef INET6
 | |
| /* see rc.c, "dns_order" and dnsorders[] */
 | |
| int ai_family_order_table[3][3] = {
 | |
|     {PF_UNSPEC, PF_UNSPEC, PF_UNSPEC},	/* 0:unspec */
 | |
|     {PF_INET, PF_INET6, PF_UNSPEC},	/* 1:inet inet6 */
 | |
|     {PF_INET6, PF_INET, PF_UNSPEC}	/* 2:inet6 inet */
 | |
| };
 | |
| #endif				/* INET6 */
 | |
| 
 | |
| static JMP_BUF AbortLoading;
 | |
| 
 | |
| /* XXX: note html.h SCM_ */
 | |
| static int
 | |
|  DefaultPort[] = {
 | |
|     80,				/* http */
 | |
|     70,				/* gopher */
 | |
|     21,				/* ftp */
 | |
|     21,				/* ftpdir */
 | |
|     0,				/* local - not defined */
 | |
|     0,				/* local-CGI - not defined? */
 | |
|     0,				/* exec - not defined? */
 | |
|     119,			/* nntp */
 | |
|     119,			/* news */
 | |
|     0,				/* mailto - not defined */
 | |
| #ifdef USE_SSL
 | |
|     443,			/* https */
 | |
| #endif				/* USE_SSL */
 | |
| };
 | |
| 
 | |
| struct cmdtable schemetable[] = {
 | |
|     {"http", SCM_HTTP},
 | |
|     {"gopher", SCM_GOPHER},
 | |
|     {"ftp", SCM_FTP},
 | |
|     {"local", SCM_LOCAL},
 | |
|     {"file", SCM_LOCAL},
 | |
|     /*  {"exec", SCM_EXEC}, */
 | |
|     {"nntp", SCM_NNTP},
 | |
|     {"news", SCM_NEWS},
 | |
|     {"mailto", SCM_MAILTO},
 | |
| #ifdef USE_SSL
 | |
|     {"https", SCM_HTTPS},
 | |
| #endif				/* USE_SSL */
 | |
|     {NULL, SCM_UNKNOWN},
 | |
| };
 | |
| 
 | |
| static struct table2 DefaultGuess[] = {
 | |
|     {"html", "text/html"},
 | |
|     {"HTML", "text/html"},
 | |
|     {"htm", "text/html"},
 | |
|     {"HTM", "text/html"},
 | |
|     {"shtml", "text/html"},
 | |
|     {"SHTML", "text/html"},
 | |
|     {"gif", "image/gif"},
 | |
|     {"GIF", "image/gif"},
 | |
|     {"jpeg", "image/jpeg"},
 | |
|     {"jpg", "image/jpeg"},
 | |
|     {"JPEG", "image/jpeg"},
 | |
|     {"JPG", "image/jpeg"},
 | |
|     {"png", "image/png"},
 | |
|     {"PNG", "image/png"},
 | |
|     {"xbm", "image/xbm"},
 | |
|     {"XBM", "image/xbm"},
 | |
|     {"au", "audio/basic"},
 | |
|     {"AU", "audio/basic"},
 | |
|     {"gz", "application/x-gzip"},
 | |
|     {"Z", "application/x-compress"},
 | |
|     {"bz2", "application/x-bzip"},
 | |
|     {"tar", "application/x-tar"},
 | |
|     {"zip", "application/x-zip"},
 | |
|     {"lha", "application/x-lha"},
 | |
|     {"lzh", "application/x-lha"},
 | |
|     {"LZH", "application/x-lha"},
 | |
|     {"ps", "application/postscript"},
 | |
|     {"pdf", "application/pdf"},
 | |
|     {NULL, NULL}
 | |
| };
 | |
| 
 | |
| static void add_index_file(ParsedURL *pu, URLFile *uf);
 | |
| 
 | |
| /* #define HTTP_DEFAULT_FILE    "/index.html" */
 | |
| 
 | |
| #ifndef HTTP_DEFAULT_FILE
 | |
| #define HTTP_DEFAULT_FILE "/"
 | |
| #endif				/* not HTTP_DEFAULT_FILE */
 | |
| 
 | |
| #ifdef SOCK_DEBUG
 | |
| #include <stdarg.h>
 | |
| 
 | |
| static void
 | |
| sock_log(char *message, ...)
 | |
| {
 | |
|     FILE *f = fopen("zzzsocklog", "a");
 | |
|     va_list va;
 | |
| 
 | |
|     if (f == NULL)
 | |
| 	return;
 | |
|     va_start(va, message);
 | |
|     vfprintf(f, message, va);
 | |
|     fclose(f);
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| static TextList *mimetypes_list;
 | |
| static struct table2 **UserMimeTypes;
 | |
| 
 | |
| static struct table2 *
 | |
| loadMimeTypes(char *filename)
 | |
| {
 | |
|     FILE *f;
 | |
|     char *d, *type;
 | |
|     int i, n;
 | |
|     Str tmp;
 | |
|     struct table2 *mtypes;
 | |
| 
 | |
|     f = fopen(expandName(filename), "r");
 | |
|     if (f == NULL)
 | |
| 	return NULL;
 | |
|     n = 0;
 | |
|     while (tmp = Strfgets(f), tmp->length > 0) {
 | |
| 	d = tmp->ptr;
 | |
| 	if (d[0] != '#') {
 | |
| 	    d = strtok(d, " \t\n\r");
 | |
| 	    if (d != NULL) {
 | |
| 		d = strtok(NULL, " \t\n\r");
 | |
| 		for (i = 0; d != NULL; i++)
 | |
| 		    d = strtok(NULL, " \t\n\r");
 | |
| 		n += i;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     fseek(f, 0, 0);
 | |
|     mtypes = New_N(struct table2, n + 1);
 | |
|     i = 0;
 | |
|     while (tmp = Strfgets(f), tmp->length > 0) {
 | |
| 	d = tmp->ptr;
 | |
| 	if (d[0] == '#')
 | |
| 	    continue;
 | |
| 	type = strtok(d, " \t\n\r");
 | |
| 	if (type == NULL)
 | |
| 	    continue;
 | |
| 	while (1) {
 | |
| 	    d = strtok(NULL, " \t\n\r");
 | |
| 	    if (d == NULL)
 | |
| 		break;
 | |
| 	    mtypes[i].item1 = Strnew_charp(d)->ptr;
 | |
| 	    mtypes[i].item2 = Strnew_charp(type)->ptr;
 | |
| 	    i++;
 | |
| 	}
 | |
|     }
 | |
|     mtypes[i].item1 = NULL;
 | |
|     mtypes[i].item2 = NULL;
 | |
|     fclose(f);
 | |
|     return mtypes;
 | |
| }
 | |
| 
 | |
| void
 | |
| initMimeTypes()
 | |
| {
 | |
|     int i;
 | |
|     TextListItem *tl;
 | |
| 
 | |
|     if (non_null(mimetypes_files))
 | |
| 	mimetypes_list = make_domain_list(mimetypes_files);
 | |
|     else
 | |
| 	mimetypes_list = NULL;
 | |
|     if (mimetypes_list == NULL)
 | |
| 	return;
 | |
|     UserMimeTypes = New_N(struct table2 *, mimetypes_list->nitem);
 | |
|     for (i = 0, tl = mimetypes_list->first; tl; i++, tl = tl->next)
 | |
| 	UserMimeTypes[i] = loadMimeTypes(tl->ptr);
 | |
| }
 | |
| 
 | |
| static char *
 | |
| DefaultFile(int scheme)
 | |
| {
 | |
|     switch (scheme) {
 | |
|     case SCM_HTTP:
 | |
| #ifdef USE_SSL
 | |
|     case SCM_HTTPS:
 | |
| #endif				/* USE_SSL */
 | |
| 	return allocStr(HTTP_DEFAULT_FILE, -1);
 | |
| #ifdef USE_GOPHER
 | |
|     case SCM_GOPHER:
 | |
| 	return allocStr("1", -1);
 | |
| #endif				/* USE_GOPHER */
 | |
|     case SCM_LOCAL:
 | |
|     case SCM_LOCAL_CGI:
 | |
|     case SCM_FTP:
 | |
| 	return allocStr("/", -1);
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static MySignalHandler
 | |
| KeyAbort(SIGNAL_ARG)
 | |
| {
 | |
|     LONGJMP(AbortLoading, 1);
 | |
| }
 | |
| 
 | |
| #ifdef USE_SSL
 | |
| SSL_CTX *ssl_ctx = NULL;
 | |
| 
 | |
| void
 | |
| free_ssl_ctx()
 | |
| {
 | |
|     if (ssl_ctx != NULL)
 | |
| 	SSL_CTX_free(ssl_ctx);
 | |
| }
 | |
| 
 | |
| #if SSLEAY_VERSION_NUMBER >= 0x00905100
 | |
| #include <rand.h>
 | |
| static void
 | |
| init_PRNG()
 | |
| {
 | |
|     char buffer[256];
 | |
|     const char *file;
 | |
|     long l;
 | |
|     if (RAND_status())
 | |
| 	return;
 | |
|     if ((file = RAND_file_name(buffer, sizeof(buffer)))) {
 | |
| #ifdef USE_EGD
 | |
| 	if (RAND_egd(file) > 0)
 | |
| 	    return;
 | |
| #endif
 | |
| 	RAND_load_file(file, -1);
 | |
|     }
 | |
|     if (RAND_status())
 | |
| 	goto seeded;
 | |
|     srand48((long)time(NULL));
 | |
|     while (!RAND_status()) {
 | |
| 	l = lrand48();
 | |
| 	RAND_seed((unsigned char *)&l, sizeof(long));
 | |
|     }
 | |
|   seeded:
 | |
|     if (file)
 | |
| 	RAND_write_file(file);
 | |
| }
 | |
| #endif				/* SSLEAY_VERSION_NUMBER >= 0x00905100 */
 | |
| 
 | |
| static SSL *
 | |
| openSSLHandle(int sock)
 | |
| {
 | |
|     SSL *handle;
 | |
|     char *emsg;
 | |
|     static char *old_ssl_forbid_method = NULL;
 | |
| #ifdef USE_SSL_VERIFY
 | |
|     static int old_ssl_verify_server = -1;
 | |
| #endif
 | |
| 
 | |
|     if (!old_ssl_forbid_method || !ssl_forbid_method ||
 | |
| 	strcmp(old_ssl_forbid_method, ssl_forbid_method)) {
 | |
| 	old_ssl_forbid_method = ssl_forbid_method;
 | |
| #ifdef USE_SSL_VERIFY
 | |
| 	ssl_path_modified = 1;
 | |
| #else
 | |
| 	free_ssl_ctx();
 | |
| 	ssl_ctx = NULL;
 | |
| #endif
 | |
|     }
 | |
| #ifdef USE_SSL_VERIFY
 | |
|     if (old_ssl_verify_server != ssl_verify_server) {
 | |
| 	old_ssl_verify_server = ssl_verify_server;
 | |
| 	ssl_path_modified = 1;
 | |
|     }
 | |
|     if (ssl_path_modified) {
 | |
| 	free_ssl_ctx();
 | |
| 	ssl_ctx = NULL;
 | |
| 	ssl_path_modified = 0;
 | |
|     }
 | |
| #endif				/* defined(USE_SSL_VERIFY) */
 | |
|     if (ssl_ctx == NULL) {
 | |
| 	int option;
 | |
| #if SSLEAY_VERSION_NUMBER < 0x0800
 | |
| 	ssl_ctx = SSL_CTX_new();
 | |
| 	X509_set_default_verify_paths(ssl_ctx->cert);
 | |
| #else				/* SSLEAY_VERSION_NUMBER >= 0x0800 */
 | |
| 	SSLeay_add_ssl_algorithms();
 | |
| 	SSL_load_error_strings();
 | |
| 	if (!(ssl_ctx = SSL_CTX_new(SSLv23_client_method())))
 | |
| 	    goto eend;
 | |
| 	option = SSL_OP_ALL;
 | |
| 	if (ssl_forbid_method) {
 | |
| 	    if (strchr(ssl_forbid_method, '2'))
 | |
| 		option |= SSL_OP_NO_SSLv2;
 | |
| 	    if (strchr(ssl_forbid_method, '3'))
 | |
| 		option |= SSL_OP_NO_SSLv3;
 | |
| 	    if (strchr(ssl_forbid_method, 't'))
 | |
| 		option |= SSL_OP_NO_TLSv1;
 | |
| 	    if (strchr(ssl_forbid_method, 'T'))
 | |
| 		option |= SSL_OP_NO_TLSv1;
 | |
| 	}
 | |
| 	SSL_CTX_set_options(ssl_ctx, option);
 | |
| #ifdef USE_SSL_VERIFY
 | |
| 	/* derived from openssl-0.9.5/apps/s_{client,cb}.c */
 | |
| 	SSL_CTX_set_verify(ssl_ctx,
 | |
| 			   ssl_verify_server ? SSL_VERIFY_PEER :
 | |
| 			   SSL_VERIFY_NONE, NULL);
 | |
| 	if (ssl_cert_file != NULL && *ssl_cert_file != '\0') {
 | |
| 	    int ng = 1;
 | |
| 	    if (SSL_CTX_use_certificate_file
 | |
| 		(ssl_ctx, ssl_cert_file, SSL_FILETYPE_PEM) > 0) {
 | |
| 		char *key_file = (ssl_key_file == NULL
 | |
| 				  || *ssl_key_file ==
 | |
| 				  '\0') ? ssl_cert_file : ssl_key_file;
 | |
| 		if (SSL_CTX_use_PrivateKey_file
 | |
| 		    (ssl_ctx, key_file, SSL_FILETYPE_PEM) > 0)
 | |
| 		    if (SSL_CTX_check_private_key(ssl_ctx))
 | |
| 			ng = 0;
 | |
| 	    }
 | |
| 	    if (ng) {
 | |
| 		free_ssl_ctx();
 | |
| 		goto eend;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (SSL_CTX_load_verify_locations(ssl_ctx, ssl_ca_file, ssl_ca_path))
 | |
| #endif				/* defined(USE_SSL_VERIFY) */
 | |
| 	    SSL_CTX_set_default_verify_paths(ssl_ctx);
 | |
| #endif				/* SSLEAY_VERSION_NUMBER >= 0x0800 */
 | |
| /*** by inu
 | |
|         atexit(free_ssl_ctx);
 | |
| */
 | |
|     }
 | |
|     handle = SSL_new(ssl_ctx);
 | |
|     SSL_set_fd(handle, sock);
 | |
| #if SSLEAY_VERSION_NUMBER >= 0x00905100
 | |
|     init_PRNG();
 | |
| #endif				/* SSLEAY_VERSION_NUMBER >= 0x00905100 */
 | |
|     if (SSL_connect(handle) <= 0)
 | |
| 	goto eend;
 | |
|     return handle;
 | |
|   eend:
 | |
|     emsg =
 | |
| 	Sprintf("SSL error: %s", ERR_error_string(ERR_get_error(), NULL))->ptr;
 | |
|     disp_err_message(emsg, FALSE);
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static void
 | |
| SSL_write_from_file(SSL * ssl, char *file)
 | |
| {
 | |
|     FILE *fd;
 | |
|     int c;
 | |
|     char buf[1];
 | |
|     fd = fopen(file, "r");
 | |
|     if (fd != NULL) {
 | |
| 	while ((c = fgetc(fd)) != EOF) {
 | |
| 	    buf[0] = c;
 | |
| 	    SSL_write(ssl, buf, 1);
 | |
| 	}
 | |
| 	fclose(fd);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #endif				/* USE_SSL */
 | |
| 
 | |
| static void
 | |
| write_from_file(int sock, char *file)
 | |
| {
 | |
|     FILE *fd;
 | |
|     int c;
 | |
|     char buf[1];
 | |
|     fd = fopen(file, "r");
 | |
|     if (fd != NULL) {
 | |
| 	while ((c = fgetc(fd)) != EOF) {
 | |
| 	    buf[0] = c;
 | |
| 	    write(sock, buf, 1);
 | |
| 	}
 | |
| 	fclose(fd);
 | |
|     }
 | |
| }
 | |
| 
 | |
| ParsedURL *
 | |
| baseURL(Buffer *buf)
 | |
| {
 | |
|     if (buf->bufferprop & BP_NO_URL) {
 | |
| 	/* no URL is defined for the buffer */
 | |
| 	return NULL;
 | |
|     }
 | |
|     if (buf->baseURL != NULL) {
 | |
| 	/* <BASE> tag is defined in the document */
 | |
| 	return buf->baseURL;
 | |
|     }
 | |
|     else
 | |
| 	return &buf->currentURL;
 | |
| }
 | |
| 
 | |
| int
 | |
| openSocket(char *const hostname,
 | |
| 	   char *remoteport_name, unsigned short remoteport_num)
 | |
| {
 | |
|     volatile int sock = -1;
 | |
| #ifdef INET6
 | |
|     int *af;
 | |
|     struct addrinfo hints, *res0, *res;
 | |
|     int error;
 | |
|     char *hname;
 | |
| #else				/* not INET6 */
 | |
|     struct sockaddr_in hostaddr;
 | |
|     struct hostent *entry;
 | |
|     struct protoent *proto;
 | |
|     unsigned short s_port;
 | |
|     int a1, a2, a3, a4;
 | |
|     unsigned long adr;
 | |
| #endif				/* not INET6 */
 | |
|     MySignalHandler(*volatile trap) (SIGNAL_ARG) = NULL;
 | |
| 
 | |
|     if (fmInitialized) {
 | |
| 	message(Sprintf("Opening socket...")->ptr, 0, 0);
 | |
| 	refresh();
 | |
|     }
 | |
|     if (SETJMP(AbortLoading) != 0) {
 | |
| #ifdef SOCK_DEBUG
 | |
| 	sock_log("openSocket() failed. reason: user abort\n");
 | |
| #endif
 | |
| 	if (sock >= 0)
 | |
| 	    close(sock);
 | |
| 	goto error;
 | |
|     }
 | |
|     trap = signal(SIGINT, KeyAbort);
 | |
|     if (fmInitialized)
 | |
| 	term_cbreak();
 | |
|     if (hostname == NULL) {
 | |
| #ifdef SOCK_DEBUG
 | |
| 	sock_log("openSocket() failed. reason: Bad hostname \"%s\"\n",
 | |
| 		 hostname);
 | |
| #endif
 | |
| 	goto error;
 | |
|     }
 | |
| 
 | |
| #ifdef INET6
 | |
|     /* rfc2732 compliance */
 | |
|     hname = hostname;
 | |
|     if (hname != NULL && hname[0] == '[' && hname[strlen(hname) - 1] == ']') {
 | |
| 	hname = allocStr(hostname + 1, -1);
 | |
| 	hname[strlen(hname) - 1] = '\0';
 | |
| 	if (strspn(hname, "0123456789abcdefABCDEF:.") != strlen(hname))
 | |
| 	    goto error;
 | |
|     }
 | |
|     for (af = ai_family_order_table[DNS_order];; af++) {
 | |
| 	memset(&hints, 0, sizeof(hints));
 | |
| 	hints.ai_family = *af;
 | |
| 	hints.ai_socktype = SOCK_STREAM;
 | |
| 	if (remoteport_num != 0) {
 | |
| 	    Str portbuf = Sprintf("%d", remoteport_num);
 | |
| 	    error = getaddrinfo(hname, portbuf->ptr, &hints, &res0);
 | |
| 	}
 | |
| 	else {
 | |
| 	    error = -1;
 | |
| 	}
 | |
| 	if (error && remoteport_name && remoteport_name[0] != '\0') {
 | |
| 	    /* try default port */
 | |
| 	    error = getaddrinfo(hname, remoteport_name, &hints, &res0);
 | |
| 	}
 | |
| 	if (error) {
 | |
| 	    if (*af == PF_UNSPEC) {
 | |
| 		goto error;
 | |
| 	    }
 | |
| 	    /* try next ai family */
 | |
| 	    continue;
 | |
| 	}
 | |
| 	sock = -1;
 | |
| 	for (res = res0; res; res = res->ai_next) {
 | |
| 	    sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
 | |
| 	    if (sock < 0) {
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
 | |
| 		close(sock);
 | |
| 		sock = -1;
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    break;
 | |
| 	}
 | |
| 	if (sock < 0) {
 | |
| 	    freeaddrinfo(res0);
 | |
| 	    if (*af == PF_UNSPEC) {
 | |
| 		goto error;
 | |
| 	    }
 | |
| 	    /* try next ai family */
 | |
| 	    continue;
 | |
| 	}
 | |
| 	freeaddrinfo(res0);
 | |
| 	break;
 | |
|     }
 | |
| #else				/* not INET6 */
 | |
|     s_port = htons(remoteport_num);
 | |
|     bzero((char *)&hostaddr, sizeof(struct sockaddr_in));
 | |
|     if ((proto = getprotobyname("tcp")) == NULL) {
 | |
| 	/* protocol number of TCP is 6 */
 | |
| 	proto = New(struct protoent);
 | |
| 	proto->p_proto = 6;
 | |
|     }
 | |
|     if ((sock = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0) {
 | |
| #ifdef SOCK_DEBUG
 | |
| 	sock_log("openSocket: socket() failed. reason: %s\n", strerror(errno));
 | |
| #endif
 | |
| 	goto error;
 | |
|     }
 | |
|     regexCompile("^[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*$", 0);
 | |
|     if (regexMatch(hostname, 0, 1)) {
 | |
| 	sscanf(hostname, "%d.%d.%d.%d", &a1, &a2, &a3, &a4);
 | |
| 	adr = htonl((a1 << 24) | (a2 << 16) | (a3 << 8) | a4);
 | |
| 	bcopy((void *)&adr, (void *)&hostaddr.sin_addr, sizeof(long));
 | |
| 	hostaddr.sin_family = AF_INET;
 | |
| 	hostaddr.sin_port = s_port;
 | |
| 	if (fmInitialized) {
 | |
| 	    message(Sprintf("Connecting to %s", hostname)->ptr, 0, 0);
 | |
| 	    refresh();
 | |
| 	}
 | |
| 	if (connect(sock, (struct sockaddr *)&hostaddr,
 | |
| 		    sizeof(struct sockaddr_in)) < 0) {
 | |
| #ifdef SOCK_DEBUG
 | |
| 	    sock_log("openSocket: connect() failed. reason: %s\n",
 | |
| 		     strerror(errno));
 | |
| #endif
 | |
| 	    goto error;
 | |
| 	}
 | |
|     }
 | |
|     else {
 | |
| 	char **h_addr_list;
 | |
| 	int result = -1;
 | |
| 	if (fmInitialized) {
 | |
| 	    message(Sprintf("Performing hostname lookup on %s", hostname)->ptr,
 | |
| 		    0, 0);
 | |
| 	    refresh();
 | |
| 	}
 | |
| 	if ((entry = gethostbyname(hostname)) == NULL) {
 | |
| #ifdef SOCK_DEBUG
 | |
| 	    sock_log("openSocket: gethostbyname() failed. reason: %s\n",
 | |
| 		     strerror(errno));
 | |
| #endif
 | |
| 	    goto error;
 | |
| 	}
 | |
| 	hostaddr.sin_family = AF_INET;
 | |
| 	hostaddr.sin_port = s_port;
 | |
| 	for (h_addr_list = entry->h_addr_list; *h_addr_list; h_addr_list++) {
 | |
| 	    bcopy((void *)h_addr_list[0], (void *)&hostaddr.sin_addr,
 | |
| 		  entry->h_length);
 | |
| #ifdef SOCK_DEBUG
 | |
| 	    adr = ntohl(*(long *)&hostaddr.sin_addr);
 | |
| 	    sock_log("openSocket: connecting %d.%d.%d.%d\n",
 | |
| 		     (adr >> 24) & 0xff,
 | |
| 		     (adr >> 16) & 0xff, (adr >> 8) & 0xff, adr & 0xff);
 | |
| #endif
 | |
| 	    if (fmInitialized) {
 | |
| 		message(Sprintf("Connecting to %s", hostname)->ptr, 0, 0);
 | |
| 		refresh();
 | |
| 	    }
 | |
| 	    if ((result = connect(sock, (struct sockaddr *)&hostaddr,
 | |
| 				  sizeof(struct sockaddr_in))) == 0) {
 | |
| 		break;
 | |
| 	    }
 | |
| #ifdef SOCK_DEBUG
 | |
| 	    else {
 | |
| 		sock_log("openSocket: connect() failed. reason: %s\n",
 | |
| 			 strerror(errno));
 | |
| 	    }
 | |
| #endif
 | |
| 	}
 | |
| 	if (result < 0) {
 | |
| 	    goto error;
 | |
| 	}
 | |
|     }
 | |
| #endif				/* not INET6 */
 | |
| 
 | |
|     if (fmInitialized)
 | |
| 	term_raw();
 | |
|     signal(SIGINT, trap);
 | |
|     return sock;
 | |
|   error:
 | |
|     if (fmInitialized)
 | |
| 	term_raw();
 | |
|     signal(SIGINT, trap);
 | |
| 
 | |
|     return -1;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| #define COPYPATH_SPC_ALLOW 0
 | |
| #define COPYPATH_SPC_IGNORE 1
 | |
| #define COPYPATH_SPC_REPLACE 2
 | |
| 
 | |
| static char *
 | |
| copyPath(char *orgpath, int length, int option)
 | |
| {
 | |
|     Str tmp = Strnew();
 | |
|     while (*orgpath && length != 0) {
 | |
| 	if (IS_SPACE(*orgpath)) {
 | |
| 	    switch (option) {
 | |
| 	    case COPYPATH_SPC_ALLOW:
 | |
| 		Strcat_char(tmp, *orgpath);
 | |
| 		break;
 | |
| 	    case COPYPATH_SPC_IGNORE:
 | |
| 		/* do nothing */
 | |
| 		break;
 | |
| 	    case COPYPATH_SPC_REPLACE:
 | |
| 		Strcat_charp(tmp, "%20");
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 	else
 | |
| 	    Strcat_char(tmp, *orgpath);
 | |
| 	orgpath++;
 | |
| 	length--;
 | |
|     }
 | |
|     return tmp->ptr;
 | |
| }
 | |
| 
 | |
| void
 | |
| parseURL(char *url, ParsedURL *p_url, ParsedURL *current)
 | |
| {
 | |
|     char *p, *q;
 | |
|     Str tmp;
 | |
| 
 | |
|     url = url_quote(url);	/* quote 0x01-0x20, 0x7F-0xFF */
 | |
| 
 | |
|     p = url;
 | |
|     p_url->scheme = SCM_MISSING;
 | |
|     p_url->port = 0;
 | |
|     p_url->user = NULL;
 | |
|     p_url->pass = NULL;
 | |
|     p_url->host = NULL;
 | |
|     p_url->is_nocache = 0;
 | |
|     p_url->file = NULL;
 | |
|     p_url->real_file = NULL;
 | |
|     p_url->query = NULL;
 | |
|     p_url->label = NULL;
 | |
| 
 | |
|     if (*url == '#') {		/* label only */
 | |
| 	if (current)
 | |
| 	    copyParsedURL(p_url, current);
 | |
| 	goto do_label;
 | |
|     }
 | |
| #if defined( __EMX__ ) || defined( __CYGWIN__ )
 | |
|     if (!strncmp(url, "file://localhost/", 17)) {
 | |
| 	p_url->scheme = SCM_LOCAL;
 | |
| 	p += 17 - 1;
 | |
| 	url += 17 - 1;
 | |
|     }
 | |
| #endif
 | |
| #ifdef SUPPORT_DOS_DRIVE_PREFIX
 | |
|     if (IS_ALPHA(*p) && (p[1] == ':' || p[1] == '|')) {
 | |
| 	p_url->scheme = SCM_LOCAL;
 | |
| 	goto analyze_file;
 | |
|     }
 | |
| #endif				/* SUPPORT_DOS_DRIVE_PREFIX */
 | |
|     /* search for scheme */
 | |
|     p_url->scheme = getURLScheme(&p);
 | |
|     if (p_url->scheme == SCM_MISSING) {
 | |
| 	/* scheme part is not found in the url. This means either
 | |
| 	 * (a) the url is relative to the current or (b) the url
 | |
| 	 * denotes a filename (therefore the scheme is SCM_LOCAL).
 | |
| 	 */
 | |
| 	if (current) {
 | |
| 	    copyParsedURL(p_url, current);
 | |
| 	    if (p_url->scheme == SCM_LOCAL_CGI)
 | |
| 		p_url->scheme = SCM_LOCAL;
 | |
| 	    /* label part and query part don't inherit */
 | |
| 	    p_url->label = NULL;
 | |
| 	    p_url->query = NULL;
 | |
| 	}
 | |
| 	else
 | |
| 	    p_url->scheme = SCM_LOCAL;
 | |
| 	p = url;
 | |
| 	if (!strncmp(p, "//", 2)) {
 | |
| 	    /* URL begins with // */
 | |
| 	    /* it means that 'scheme:' is abbreviated */
 | |
| 	    p += 2;
 | |
| 	    goto analyze_url;
 | |
| 	}
 | |
| 	/* the url doesn't begin with '//' */
 | |
| 	goto analyze_file;
 | |
|     }
 | |
|     /* scheme part has been found */
 | |
|     /* get host and port */
 | |
|     if (p[0] != '/' || p[1] != '/') {	/* scheme:foo or scheme:/foo */
 | |
| 	p_url->host = NULL;
 | |
| 	if (p_url->scheme != SCM_UNKNOWN)
 | |
| 	    p_url->port = DefaultPort[p_url->scheme];
 | |
| 	else
 | |
| 	    p_url->port = 0;
 | |
| 	goto analyze_file;
 | |
|     }
 | |
|     /* after here, p begins with // */
 | |
|     if (p_url->scheme == SCM_LOCAL) {	/* file://foo           */
 | |
| #ifdef __EMX__
 | |
| 	p += 2;
 | |
| 	goto analyze_file;
 | |
| #else
 | |
| 	if (p[2] == '/' || p[2] == '~'
 | |
| 	    /* <A HREF="file:///foo">file:///foo</A>  or <A HREF="file://~user">file://~user</A> */
 | |
| #ifdef SUPPORT_DOS_DRIVE_PREFIX
 | |
| 	    || (IS_ALPHA(p[2]) && (p[3] == ':' || p[3] == '|'))
 | |
| 	    /* <A HREF="file://DRIVE/foo">file://DRIVE/foo</A> */
 | |
| #endif				/* SUPPORT_DOS_DRIVE_PREFIX */
 | |
| 	    ) {
 | |
| 	    p += 2;
 | |
| 	    goto analyze_file;
 | |
| 	}
 | |
| #endif				/* __EMX__ */
 | |
|     }
 | |
|     p += 2;			/* scheme://foo         */
 | |
|     /*          ^p is here  */
 | |
|   analyze_url:
 | |
|     q = p;
 | |
|     while (*p && strchr(":/@?#", *p) == NULL) {
 | |
| #ifdef INET6
 | |
| 	if (*p == '[') {	/* rfc2732 compliance */
 | |
| 	    char *p_colon = NULL;
 | |
| 	    do {
 | |
| 		p++;
 | |
| 		if ((p_colon == NULL) && (*p == ':'))
 | |
| 		    p_colon = p;
 | |
| 	    } while (*p && (IS_ALNUM(*p) || *p == ':' || *p == '.'));
 | |
| 	    if (*p == ']') {
 | |
| 		p++;
 | |
| 		break;
 | |
| 	    }
 | |
| 	    else if (p_colon) {
 | |
| 		p = p_colon;
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| #endif
 | |
| 	p++;
 | |
|     }
 | |
|     switch (*p) {
 | |
|     case '\0':
 | |
| 	/* scheme://host                */
 | |
| 	p_url->host = copyPath(q, -1, COPYPATH_SPC_IGNORE);
 | |
| 	p_url->port = DefaultPort[p_url->scheme];
 | |
| 	p_url->file = DefaultFile(p_url->scheme);
 | |
| 	return;
 | |
|     case ':':
 | |
| 	/* scheme://user:pass@host or
 | |
| 	 * scheme://host:port
 | |
| 	 */
 | |
| 	p_url->host = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
 | |
| 	q = ++p;
 | |
| 	while (*p && strchr("@/?#", *p) == NULL)
 | |
| 	    p++;
 | |
| 	if (*p == '@') {
 | |
| 	    /* scheme://user:pass@...       */
 | |
| 	    p_url->pass = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
 | |
| 	    q = ++p;
 | |
| 	    p_url->user = p_url->host;
 | |
| 	    p_url->host = NULL;
 | |
| 	    goto analyze_url;
 | |
| 	}
 | |
| 	/* scheme://host:port/ */
 | |
| 	tmp = Strnew_charp_n(q, p - q);
 | |
| 	p_url->port = atoi(tmp->ptr);
 | |
| 	if (*p == '\0') {
 | |
| 	    /* scheme://user@host:port      */
 | |
| 	    /* scheme://user:pass@host:port */
 | |
| 	    p_url->file = DefaultFile(p_url->scheme);
 | |
| 	    p_url->label = NULL;
 | |
| 	    return;
 | |
| 	}
 | |
| 	/* *p is one of ['/', '?', '#'] */
 | |
| 	break;
 | |
|     case '@':
 | |
| 	/* scheme://user@...            */
 | |
| 	p_url->user = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
 | |
| 	q = ++p;
 | |
| 	goto analyze_url;
 | |
|     case '/':
 | |
|     case '?':
 | |
|     case '#':
 | |
| 	p_url->host = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
 | |
| 	p_url->port = DefaultPort[p_url->scheme];
 | |
| 	break;
 | |
|     }
 | |
|   analyze_file:
 | |
| #ifndef SUPPORT_NETBIOS_SHARE
 | |
|     if (p_url->scheme == SCM_LOCAL && p_url->user == NULL &&
 | |
| 	p_url->host != NULL && strcmp(p_url->host, "localhost")) {
 | |
| 	/*
 | |
| 	 * In the environments other than CYGWIN, a URL like 
 | |
| 	 * file://host/file is regarded as ftp://host/file.
 | |
| 	 * On the other hand, file://host/file on CYGWIN is
 | |
| 	 * regarded as local access to the file //host/file.
 | |
| 	 * `host' is a netbios-hostname, drive, or any other
 | |
| 	 * name; It is CYGWIN system call who interprets that.
 | |
| 	 */
 | |
| 
 | |
| 	p_url->scheme = SCM_FTP;	/* ftp://host/... */
 | |
| 	if (p_url->port == 0)
 | |
| 	    p_url->port = DefaultPort[SCM_FTP];
 | |
|     }
 | |
| #endif
 | |
| #ifdef SUPPORT_DOS_DRIVE_PREFIX
 | |
|     if (p_url->scheme == SCM_LOCAL) {
 | |
| 	q = p;
 | |
| 	if (*q == '/')
 | |
| 	    q++;
 | |
| 	if (IS_ALPHA(q[0]) && (q[1] == ':' || q[1] == '|')) {
 | |
| 	    if (q[1] == '|') {
 | |
| 		p = allocStr(q, -1);
 | |
| 		p[1] = ':';
 | |
| 	    }
 | |
| 	    else
 | |
| 		p = q;
 | |
| 	}
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     q = p;
 | |
| #ifdef USE_GOPHER
 | |
|     if (p_url->scheme == SCM_GOPHER) {
 | |
| 	if (*q == '/')
 | |
| 	    q++;
 | |
| 	if (*q && q[0] != '/' && q[1] != '/' && q[2] == '/')
 | |
| 	    q++;
 | |
|     }
 | |
| #endif				/* USE_GOPHER */
 | |
|     if (*p == '/')
 | |
| 	p++;
 | |
|     if (*p == '\0') {		/* scheme://host[:port]/ */
 | |
| 	p_url->file = DefaultFile(p_url->scheme);
 | |
| 	p_url->label = NULL;
 | |
| 	return;
 | |
|     }
 | |
| #ifdef USE_GOPHER
 | |
|     if (p_url->scheme == SCM_GOPHER && *p == 'R') {
 | |
| 	p++;
 | |
| 	tmp = Strnew();
 | |
| 	Strcat_char(tmp, *(p++));
 | |
| 	while (*p && *p != '/')
 | |
| 	    p++;
 | |
| 	Strcat_charp(tmp, p);
 | |
| 	while (*p)
 | |
| 	    p++;
 | |
| 	p_url->file = copyPath(tmp->ptr, -1, COPYPATH_SPC_IGNORE);
 | |
|     }
 | |
|     else
 | |
| #endif				/* USE_GOPHER */
 | |
|     {
 | |
| 	char *cgi = strchr(p, '?');
 | |
|       again:
 | |
| 	while (*p && *p != '#' && p != cgi)
 | |
| 	    p++;
 | |
| 	if (*p == '#' && p_url->scheme == SCM_LOCAL) {
 | |
| 	    /* 
 | |
| 	     * According to RFC2396, # means the beginning of
 | |
| 	     * URI-reference, and # should be escaped.  But,
 | |
| 	     * if the scheme is SCM_LOCAL, the special
 | |
| 	     * treatment will apply to # for convinience.
 | |
| 	     */
 | |
| 	    if (p > q && *(p - 1) == '/' && (cgi == NULL || p < cgi)) {
 | |
| 		/* 
 | |
| 		 * # comes as the first character of the file name
 | |
| 		 * that means, # is not a label but a part of the file
 | |
| 		 * name.
 | |
| 		 */
 | |
| 		p++;
 | |
| 		goto again;
 | |
| 	    }
 | |
| 	    else if (*(p + 1) == '\0') {
 | |
| 		/* 
 | |
| 		 * # comes as the last character of the file name that
 | |
| 		 * means, # is not a label but a part of the file
 | |
| 		 * name.
 | |
| 		 */
 | |
| 		p++;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (p_url->scheme == SCM_LOCAL || p_url->scheme == SCM_MISSING)
 | |
| 	    p_url->file = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
 | |
| 	else
 | |
| 	    p_url->file = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
 | |
|     }
 | |
| 
 | |
|     if (*p == '?') {
 | |
| 	q = ++p;
 | |
| 	while (*p && *p != '#')
 | |
| 	    p++;
 | |
| 	p_url->query = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
 | |
|     }
 | |
|   do_label:
 | |
|     if (p_url->scheme == SCM_MISSING) {
 | |
| 	p_url->scheme = SCM_LOCAL;
 | |
| 	p_url->file = allocStr(p, -1);
 | |
| 	p_url->label = NULL;
 | |
|     }
 | |
|     else if (*p == '#')
 | |
| 	p_url->label = allocStr(p + 1, -1);
 | |
|     else
 | |
| 	p_url->label = NULL;
 | |
| }
 | |
| 
 | |
| #define initParsedURL(p) bzero(p,sizeof(ParsedURL))
 | |
| #define ALLOC_STR(s) ((s)==NULL?NULL:allocStr(s,-1))
 | |
| 
 | |
| void
 | |
| copyParsedURL(ParsedURL *p, ParsedURL *q)
 | |
| {
 | |
|     p->scheme = q->scheme;
 | |
|     p->port = q->port;
 | |
|     p->is_nocache = q->is_nocache;
 | |
|     p->user = ALLOC_STR(q->user);
 | |
|     p->pass = ALLOC_STR(q->pass);
 | |
|     p->host = ALLOC_STR(q->host);
 | |
|     p->file = ALLOC_STR(q->file);
 | |
|     p->real_file = ALLOC_STR(q->real_file);
 | |
|     p->label = ALLOC_STR(q->label);
 | |
|     p->query = ALLOC_STR(q->query);
 | |
| }
 | |
| 
 | |
| void
 | |
| parseURL2(char *url, ParsedURL *pu, ParsedURL *current)
 | |
| {
 | |
|     char *p;
 | |
|     Str tmp;
 | |
|     int relative_uri = FALSE;
 | |
| 
 | |
|     parseURL(url, pu, current);
 | |
|     if (pu->scheme == SCM_MAILTO)
 | |
| 	return;
 | |
| 
 | |
|     if (pu->scheme == SCM_LOCAL)
 | |
| 	pu->file = expandName(pu->file);
 | |
| 
 | |
|     if (current && pu->scheme == current->scheme) {
 | |
| 	/* Copy omitted element from the current URL */
 | |
| 	if (pu->user == NULL) {
 | |
| 	    pu->user = current->user;
 | |
| 	}
 | |
| 	if (pu->pass == NULL) {
 | |
| 	    pu->pass = current->pass;
 | |
| 	}
 | |
| 	if (pu->host == NULL) {
 | |
| 	    pu->host = current->host;
 | |
| 	}
 | |
| 	if (pu->file) {
 | |
| 	    if (
 | |
| #ifdef USE_GOPHER
 | |
| 		   pu->scheme != SCM_GOPHER &&
 | |
| #endif				/* USE_GOPHER */
 | |
| #ifdef USE_NNTP
 | |
| 		   pu->scheme != SCM_NEWS &&
 | |
| #endif				/* USE_NNTP */
 | |
| 		   pu->file[0] != '/'
 | |
| #ifdef SUPPORT_DOS_DRIVE_PREFIX
 | |
| 		   && !(pu->scheme == SCM_LOCAL && IS_ALPHA(pu->file[0])
 | |
| 			&& pu->file[1] == ':')
 | |
| #endif
 | |
| 		) {
 | |
| 		/* file is relative [process 1] */
 | |
| 		p = pu->file;
 | |
| 		if (current->file) {
 | |
| 		    tmp = Strnew_charp(current->file);
 | |
| 		    while (tmp->length > 0) {
 | |
| 			if (Strlastchar(tmp) == '/')
 | |
| 			    break;
 | |
| 			Strshrink(tmp, 1);
 | |
| 		    }
 | |
| 		    Strcat_charp(tmp, p);
 | |
| 		    pu->file = tmp->ptr;
 | |
| 		    relative_uri = TRUE;
 | |
| 		}
 | |
| 	    }
 | |
| #ifdef USE_GOPHER
 | |
| 	    else if (pu->scheme == SCM_GOPHER && pu->file[0] == '/') {
 | |
| 		p = pu->file;
 | |
| 		pu->file = allocStr(p + 1, -1);
 | |
| 	    }
 | |
| #endif				/* USE_GOPHER */
 | |
| 	}
 | |
| 	else if (pu->label) {
 | |
| 	    /* pu has only label */
 | |
| 	    pu->file = current->file;
 | |
| 	}
 | |
| 	/* comment: query part need not to be completed
 | |
| 	 * from the current URL. */
 | |
|     }
 | |
|     if (pu->file) {
 | |
| #ifdef __EMX__
 | |
| 	if (pu->scheme == SCM_LOCAL) {
 | |
| 	    if (strncmp(pu->file, "/$LIB/", 6)) {
 | |
| 		char abs[_MAX_PATH];
 | |
| 
 | |
| 		_abspath(abs, pu->file, _MAX_PATH);
 | |
| 		pu->file = file_quote(cleanupName(abs));
 | |
| 	    }
 | |
| 	}
 | |
| #else
 | |
| 	if (pu->scheme == SCM_LOCAL && pu->file[0] != '/' &&
 | |
| #ifdef SUPPORT_DOS_DRIVE_PREFIX	/* for 'drive:' */
 | |
| 	    !(IS_ALPHA(pu->file[0]) && pu->file[1] == ':') &&
 | |
| #endif
 | |
| 	    strcmp(pu->file, "-")) {
 | |
| 	    /* local file, relative path */
 | |
| 	    tmp = Strnew_charp(CurrentDir);
 | |
| 	    if (Strlastchar(tmp) != '/')
 | |
| 		Strcat_char(tmp, '/');
 | |
| 	    Strcat_charp(tmp, pu->file);
 | |
| 	    pu->file = file_quote(cleanupName(tmp->ptr));
 | |
| 	}
 | |
| #endif
 | |
| 	else if (pu->scheme == SCM_HTTP
 | |
| #ifdef USE_SSL
 | |
| 		 || pu->scheme == SCM_HTTPS
 | |
| #endif
 | |
| 	    ) {
 | |
| 	    if (relative_uri) {
 | |
| 		/* In this case, pu->file is created by [process 1] above.
 | |
| 		 * pu->file may contain relative path (for example, 
 | |
| 		 * "/foo/../bar/./baz.html"), cleanupName() must be applied.
 | |
| 		 * When the entire abs_path is given, it still may contain
 | |
| 		 * elements like `//', `..' or `.' in the pu->file. It is 
 | |
| 		 * server's responsibility to canonicalize such path.
 | |
| 		 */
 | |
| 		pu->file = cleanupName(pu->file);
 | |
| 	    }
 | |
| 	}
 | |
| 	else if (
 | |
| #ifdef USE_GOPHER
 | |
| 		    pu->scheme != SCM_GOPHER &&
 | |
| #endif				/* USE_GOPHER */
 | |
| #ifdef USE_NNTP
 | |
| 		    pu->scheme != SCM_NEWS &&
 | |
| #endif				/* USE_NNTP */
 | |
| 		    pu->file[0] == '/') {
 | |
| 	    /*
 | |
| 	     * this happens on the following conditions:
 | |
| 	     * (1) ftp scheme (2) local, looks like absolute path.
 | |
| 	     * In both case, there must be no side effect with
 | |
| 	     * cleanupName(). (I hope so...)
 | |
| 	     */
 | |
| 	    pu->file = cleanupName(pu->file);
 | |
| 	}
 | |
| 	if (pu->scheme == SCM_LOCAL) {
 | |
| #ifdef SUPPORT_NETBIOS_SHARE
 | |
| 	    if (pu->host && strcmp(pu->host, "localhost") != 0) {
 | |
| 		Str tmp = Strnew_charp("//");
 | |
| 		Strcat_m_charp(tmp, pu->host,
 | |
| 			       cleanupName(file_unquote(pu->file)), NULL);
 | |
| 		pu->real_file = tmp->ptr;
 | |
| 	    }
 | |
| 	    else
 | |
| #endif
 | |
| 		pu->real_file = cleanupName(file_unquote(pu->file));
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| static Str
 | |
| _parsedURL2Str(ParsedURL *pu, int pass)
 | |
| {
 | |
|     Str tmp;
 | |
|     static char *scheme_str[] = {
 | |
| 	"http", "gopher", "ftp", "ftp", "file", "file", "exec", "nntp", "news",
 | |
| 	"mailto",
 | |
| #ifdef USE_SSL
 | |
| 	"https",
 | |
| #endif				/* USE_SSL */
 | |
|     };
 | |
| 
 | |
|     if (pu->scheme == SCM_UNKNOWN || pu->scheme == SCM_MISSING) {
 | |
| 	return Strnew_charp("???");
 | |
|     }
 | |
|     if (pu->host == NULL && pu->file == NULL && pu->label != NULL) {
 | |
| 	/* local label */
 | |
| 	return Sprintf("#%s", pu->label);
 | |
|     }
 | |
|     if (pu->scheme == SCM_LOCAL && !strcmp(pu->file, "-")) {
 | |
| 	tmp = Strnew_charp("-");
 | |
| 	if (pu->label) {
 | |
| 	    Strcat_char(tmp, '#');
 | |
| 	    Strcat_charp(tmp, pu->label);
 | |
| 	}
 | |
| 	return tmp;
 | |
|     }
 | |
|     tmp = Strnew_charp(scheme_str[pu->scheme]);
 | |
|     Strcat_char(tmp, ':');
 | |
|     if (pu->scheme == SCM_MAILTO) {
 | |
| 	Strcat_charp(tmp, pu->file);
 | |
| 	return tmp;
 | |
|     }
 | |
| #ifdef USE_NNTP
 | |
|     if (pu->scheme != SCM_NEWS)
 | |
| #endif				/* USE_NNTP */
 | |
|     {
 | |
| 	Strcat_charp(tmp, "//");
 | |
|     }
 | |
|     if (pu->user) {
 | |
| 	Strcat_charp(tmp, pu->user);
 | |
| 	if (pass && pu->pass) {
 | |
| 	    Strcat_char(tmp, ':');
 | |
| 	    Strcat_charp(tmp, pu->pass);
 | |
| 	}
 | |
| 	Strcat_char(tmp, '@');
 | |
|     }
 | |
|     if (pu->host) {
 | |
| 	Strcat_charp(tmp, pu->host);
 | |
| 	if (pu->port != DefaultPort[pu->scheme]) {
 | |
| 	    Strcat_char(tmp, ':');
 | |
| 	    Strcat(tmp, Sprintf("%d", pu->port));
 | |
| 	}
 | |
|     }
 | |
|     if (
 | |
| #ifdef USE_NNTP
 | |
| 	   pu->scheme != SCM_NEWS &&
 | |
| #endif				/* USE_NNTP */
 | |
| 	   (pu->file == NULL || (pu->file[0] != '/'
 | |
| #ifdef SUPPORT_DOS_DRIVE_PREFIX
 | |
| 				 && !(IS_ALPHA(pu->file[0])
 | |
| 				      && pu->file[1] == ':'
 | |
| 				      && pu->host == NULL)
 | |
| #endif
 | |
| 	    )))
 | |
| 	Strcat_char(tmp, '/');
 | |
|     Strcat_charp(tmp, pu->file);
 | |
|     if (pu->query) {
 | |
| 	Strcat_char(tmp, '?');
 | |
| 	Strcat_charp(tmp, pu->query);
 | |
|     }
 | |
|     if (pu->label) {
 | |
| 	Strcat_char(tmp, '#');
 | |
| 	Strcat_charp(tmp, pu->label);
 | |
|     }
 | |
|     return tmp;
 | |
| }
 | |
| 
 | |
| Str
 | |
| parsedURL2Str(ParsedURL *pu)
 | |
| {
 | |
|     return _parsedURL2Str(pu, FALSE);
 | |
| }
 | |
| 
 | |
| int
 | |
| getURLScheme(char **url)
 | |
| {
 | |
|     char *p = *url, *q;
 | |
|     int i;
 | |
|     int scheme = SCM_MISSING;
 | |
| 
 | |
|     while (*p && (IS_ALPHA(*p) || *p == '.' || *p == '+' || *p == '-'))
 | |
| 	p++;
 | |
|     if (*p == ':') {		/* scheme found */
 | |
| 	scheme = SCM_UNKNOWN;
 | |
| 	for (i = 0; (q = schemetable[i].cmdname) != NULL; i++) {
 | |
| 	    int len = strlen(q);
 | |
| 	    if (!strncasecmp(q, *url, len) && (*url)[len] == ':') {
 | |
| 		scheme = schemetable[i].cmd;
 | |
| 		*url = p + 1;
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     return scheme;
 | |
| }
 | |
| 
 | |
| static char *
 | |
| otherinfo(ParsedURL *target, ParsedURL *current, char *referer)
 | |
| {
 | |
|     Str s = Strnew();
 | |
| 
 | |
|     Strcat_charp(s, "User-Agent: ");
 | |
|     if (UserAgent == NULL || *UserAgent == '\0')
 | |
| 	Strcat_charp(s, w3m_version);
 | |
|     else
 | |
| 	Strcat_charp(s, UserAgent);
 | |
|     Strcat_charp(s, "\r\n");
 | |
| 
 | |
|     Strcat_m_charp(s, "Accept: ", AcceptMedia, "\r\n", NULL);
 | |
|     Strcat_m_charp(s, "Accept-Encoding: ", AcceptEncoding, "\r\n", NULL);
 | |
|     Strcat_m_charp(s, "Accept-Language: ", AcceptLang, "\r\n", NULL);
 | |
| 
 | |
|     if (target->host) {
 | |
| 	Strcat_charp(s, "Host: ");
 | |
| 	Strcat_charp(s, target->host);
 | |
| 	if (target->port != DefaultPort[target->scheme])
 | |
| 	    Strcat(s, Sprintf(":%d", target->port));
 | |
| 	Strcat_charp(s, "\r\n");
 | |
|     }
 | |
|     if (target->is_nocache || NoCache) {
 | |
| 	Strcat_charp(s, "Pragma: no-cache\r\n");
 | |
| 	Strcat_charp(s, "Cache-control: no-cache\r\n");
 | |
|     }
 | |
|     if (!NoSendReferer) {
 | |
| 	if (referer == NULL && current && current->scheme != SCM_LOCAL &&
 | |
| 	    (current->scheme != SCM_FTP ||
 | |
| 	     (current->user == NULL && current->pass == NULL))) {
 | |
| 	    Strcat_charp(s, "Referer: ");
 | |
| 	    Strcat(s, parsedURL2Str(current));
 | |
| 	    Strcat_charp(s, "\r\n");
 | |
| 	}
 | |
| 	else if (referer != NULL && referer != NO_REFERER) {
 | |
| 	    Strcat_charp(s, "Referer: ");
 | |
| 	    Strcat_charp(s, referer);
 | |
| 	    Strcat_charp(s, "\r\n");
 | |
| 	}
 | |
|     }
 | |
|     return s->ptr;
 | |
| }
 | |
| 
 | |
| static Str
 | |
| HTTPrequest(ParsedURL *pu, ParsedURL *current, HRequest *hr, TextList *extra)
 | |
| {
 | |
|     Str tmp;
 | |
|     TextListItem *i;
 | |
| #ifdef USE_COOKIE
 | |
|     Str cookie;
 | |
| #endif				/* USE_COOKIE */
 | |
|     switch (hr->command) {
 | |
|     case HR_COMMAND_CONNECT:
 | |
| 	tmp = Strnew_charp("CONNECT ");
 | |
| 	break;
 | |
|     case HR_COMMAND_POST:
 | |
| 	tmp = Strnew_charp("POST ");
 | |
| 	break;
 | |
|     case HR_COMMAND_HEAD:
 | |
| 	tmp = Strnew_charp("HEAD ");
 | |
| 	break;
 | |
|     case HR_COMMAND_GET:
 | |
|     default:
 | |
| 	tmp = Strnew_charp("GET ");
 | |
|     }
 | |
|     if (hr->command == HR_COMMAND_CONNECT) {
 | |
| 	Strcat_charp(tmp, pu->host);
 | |
| 	Strcat(tmp, Sprintf(":%d", pu->port));
 | |
|     }
 | |
|     else if (hr->flag & HR_FLAG_LOCAL) {
 | |
| 	Strcat_charp(tmp, pu->file);
 | |
| 	if (pu->query) {
 | |
| 	    Strcat_char(tmp, '?');
 | |
| 	    Strcat_charp(tmp, pu->query);
 | |
| 	}
 | |
|     }
 | |
|     else {
 | |
| 	Strcat(tmp, _parsedURL2Str(pu, TRUE));
 | |
|     }
 | |
|     Strcat_charp(tmp, " HTTP/1.0\r\n");
 | |
|     if (hr->referer == NO_REFERER)
 | |
| 	Strcat_charp(tmp, otherinfo(pu, NULL, NULL));
 | |
|     else
 | |
| 	Strcat_charp(tmp, otherinfo(pu, current, hr->referer));
 | |
|     if (extra != NULL)
 | |
| 	for (i = extra->first; i != NULL; i = i->next)
 | |
| 	    Strcat_charp(tmp, i->ptr);
 | |
| #ifdef USE_COOKIE
 | |
|     if (hr->command != HR_COMMAND_CONNECT &&
 | |
| 	use_cookie && (cookie = find_cookie(pu))) {
 | |
| 	Strcat_charp(tmp, "Cookie: ");
 | |
| 	Strcat(tmp, cookie);
 | |
| 	Strcat_charp(tmp, "\r\n");
 | |
| 	/* [DRAFT 12] s. 10.1 */
 | |
| 	if (cookie->ptr[0] != '$')
 | |
| 	    Strcat_charp(tmp, "Cookie2: $Version=\"1\"\r\n");
 | |
|     }
 | |
| #endif				/* USE_COOKIE */
 | |
|     if (hr->command == HR_COMMAND_POST) {
 | |
| 	if (hr->request->enctype == FORM_ENCTYPE_MULTIPART) {
 | |
| 	    Strcat_charp(tmp, "Content-type: multipart/form-data; boundary=");
 | |
| 	    Strcat_charp(tmp, hr->request->boundary);
 | |
| 	    Strcat_charp(tmp, "\r\n");
 | |
| 	    Strcat(tmp,
 | |
| 		   Sprintf("Content-length: %ld\r\n", hr->request->length));
 | |
| 	    Strcat_charp(tmp, "\r\n");
 | |
| 	}
 | |
| 	else {
 | |
| 	    if (!override_content_type) {
 | |
| 		Strcat_charp(tmp,
 | |
| 			     "Content-type: application/x-www-form-urlencoded\r\n");
 | |
| 	    }
 | |
| 	    Strcat(tmp,
 | |
| 		   Sprintf("Content-length: %ld\r\n", hr->request->length));
 | |
| 	    if (header_string)
 | |
| 		Strcat(tmp, header_string);
 | |
| 	    Strcat_charp(tmp, "\r\n");
 | |
| 	    Strcat_charp_n(tmp, hr->request->body, hr->request->length);
 | |
| 	    Strcat_charp(tmp, "\r\n");
 | |
| 	}
 | |
|     }
 | |
|     else {
 | |
| 	if (header_string)
 | |
| 	    Strcat(tmp, header_string);
 | |
| 	Strcat_charp(tmp, "\r\n");
 | |
|     }
 | |
| #ifdef DEBUG
 | |
|     fprintf(stderr, "HTTPrequest: [ %s ]\n\n", tmp->ptr);
 | |
| #endif				/* DEBUG */
 | |
|     return tmp;
 | |
| }
 | |
| 
 | |
| void
 | |
| init_stream(URLFile *uf, int scheme, InputStream stream)
 | |
| {
 | |
|     uf->stream = stream;
 | |
|     uf->scheme = scheme;
 | |
|     uf->encoding = ENC_7BIT;
 | |
|     uf->is_cgi = FALSE;
 | |
|     uf->compression = 0;
 | |
|     uf->guess_type = NULL;
 | |
|     uf->ext = NULL;
 | |
| }
 | |
| 
 | |
| static InputStream
 | |
| openFTPStream(ParsedURL *pu)
 | |
| {
 | |
|     return newFileStream(openFTP(pu), closeFTP);
 | |
| }
 | |
| 
 | |
| URLFile
 | |
| openURL(char *url, ParsedURL *pu, ParsedURL *current,
 | |
| 	URLOption *option, FormList *request, TextList *extra_header,
 | |
| 	URLFile *ouf, unsigned char *status)
 | |
| {
 | |
|     Str tmp;
 | |
|     int i, sock, scheme;
 | |
|     char *p, *q, *u;
 | |
|     URLFile uf;
 | |
|     HRequest hr;
 | |
| #ifdef USE_SSL
 | |
|     SSL *sslh = NULL;
 | |
| #endif				/* USE_SSL */
 | |
| #ifdef USE_NNTP
 | |
|     FILE *fw;
 | |
|     char *r;
 | |
|     InputStream stream;
 | |
| #endif				/* USE_NNTP */
 | |
|     int extlen = strlen(CGI_EXTENSION);
 | |
| 
 | |
|     if (ouf) {
 | |
| 	uf = *ouf;
 | |
|     }
 | |
|     else {
 | |
| 	init_stream(&uf, SCM_MISSING, NULL);
 | |
|     }
 | |
| 
 | |
|     u = url;
 | |
|     scheme = getURLScheme(&u);
 | |
|     if (current == NULL && scheme == SCM_MISSING && !ArgvIsURL)
 | |
| 	u = file_to_url(url);	/* force to local file */
 | |
|     else
 | |
| 	u = url;
 | |
|   retry:
 | |
|     parseURL2(u, pu, current);
 | |
|     if (pu->scheme == SCM_LOCAL && pu->file == NULL) {
 | |
| 	if (pu->label != NULL) {
 | |
| 	    /* #hogege is not a label but a filename */
 | |
| 	    Str tmp2 = Strnew_charp("#");
 | |
| 	    Strcat_charp(tmp2, pu->label);
 | |
| 	    pu->file = tmp2->ptr;
 | |
| 	    pu->real_file = cleanupName(file_unquote(pu->file));
 | |
| 	    pu->label = NULL;
 | |
| 	}
 | |
| 	else {
 | |
| 	    /* given URL must be null string */
 | |
| #ifdef SOCK_DEBUG
 | |
| 	    sock_log("given URL must be null string\n");
 | |
| #endif
 | |
| 	    return uf;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     uf.scheme = pu->scheme;
 | |
|     pu->is_nocache = (option->flag & RG_NOCACHE);
 | |
|     uf.ext = filename_extension(pu->file, 1);
 | |
| 
 | |
|     hr.command = HR_COMMAND_GET;
 | |
|     hr.flag = 0;
 | |
|     hr.referer = option->referer;
 | |
|     hr.request = request;
 | |
| 
 | |
|     switch (pu->scheme) {
 | |
|     case SCM_LOCAL:
 | |
|     case SCM_LOCAL_CGI:
 | |
| 	if (request && request->body) {
 | |
| 	    /* local CGI: POST */
 | |
| 	    if ((q = strchr(pu->file, '?')) != NULL) {
 | |
| 		p = Strnew_charp_n(pu->file, (int)(q - pu->file))->ptr;
 | |
| 		pu->real_file = cleanupName(file_unquote(p));
 | |
| 		q++;
 | |
| 	    }
 | |
| 	    uf.stream = newFileStream(localcgi_post(pu->real_file,
 | |
| 						    pu->query,
 | |
| 						    request,
 | |
| 						    option->referer),
 | |
| 				      (void (*)())pclose);
 | |
| 	    if (uf.stream == NULL)
 | |
| 		goto ordinary_local_file;
 | |
| 	    uf.is_cgi = TRUE;
 | |
| 	    uf.scheme = pu->scheme = SCM_LOCAL_CGI;
 | |
| 	}
 | |
| 	else if (pu->query != NULL) {
 | |
| 	    /* lodal CGI: GET */
 | |
| 	    uf.stream = newFileStream(localcgi_get(pu->real_file, pu->query,
 | |
| 						   option->referer),
 | |
| 				      (void (*)())pclose);
 | |
| 	    if (uf.stream == NULL) {
 | |
| 		goto ordinary_local_file;
 | |
| 	    }
 | |
| 	    uf.is_cgi = TRUE;
 | |
| 	    uf.scheme = pu->scheme = SCM_LOCAL_CGI;
 | |
| 	}
 | |
| 	else if ((i = strlen(pu->file)) > extlen &&
 | |
| 		 !strncmp(pu->file + i - extlen, CGI_EXTENSION, extlen)) {
 | |
| 	    /* lodal CGI: GET */
 | |
| 	    uf.stream = newFileStream(localcgi_get(pu->real_file, NULL,
 | |
| 						   option->referer),
 | |
| 				      (void (*)())pclose);
 | |
| 	    if (uf.stream == NULL)
 | |
| 		goto ordinary_local_file;
 | |
| 	    uf.is_cgi = TRUE;
 | |
| 	    uf.scheme = pu->scheme = SCM_LOCAL_CGI;
 | |
| 	}
 | |
| 	else {
 | |
| 	  ordinary_local_file:
 | |
| 	    examineFile(pu->real_file, &uf);
 | |
| 	}
 | |
| 	if (uf.stream == NULL) {
 | |
| 	    if (dir_exist(pu->real_file)) {
 | |
| 		add_index_file(pu, &uf);
 | |
| 		if (uf.stream == NULL)
 | |
| 		    return uf;
 | |
| 	    }
 | |
| 	    else if (document_root != NULL) {
 | |
| 		tmp = Strnew_charp(document_root);
 | |
| 		if (Strlastchar(tmp) != '/' && pu->file[0] != '/')
 | |
| 		    Strcat_char(tmp, '/');
 | |
| 		Strcat_charp(tmp, pu->file);
 | |
| 		p = cleanupName(tmp->ptr);
 | |
| 		q = cleanupName(file_unquote(p));
 | |
| 		if (dir_exist(q)) {
 | |
| 		    pu->file = p;
 | |
| 		    pu->real_file = q;
 | |
| 		    add_index_file(pu, &uf);
 | |
| 		    if (uf.stream == NULL) {
 | |
| 			return uf;
 | |
| 		    }
 | |
| 		}
 | |
| 		else {
 | |
| 		    examineFile(q, &uf);
 | |
| 		    if (uf.stream) {
 | |
| 			pu->file = p;
 | |
| 			pu->real_file = q;
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	if (uf.stream == NULL && retryAsHttp && url[0] != '/') {
 | |
| 	    if (scheme == SCM_MISSING || scheme == SCM_UNKNOWN) {
 | |
| 		/* retry it as "http://" */
 | |
| 		u = Strnew_m_charp("http://", url, NULL)->ptr;
 | |
| 		goto retry;
 | |
| 	    }
 | |
| 	}
 | |
| 	return uf;
 | |
|     case SCM_FTP:
 | |
| 	if (pu->file == NULL)
 | |
| 	    pu->file = allocStr("/", -1);
 | |
| 	if (non_null(FTP_proxy) &&
 | |
| 	    !Do_not_use_proxy &&
 | |
| 	    pu->host != NULL && !check_no_proxy(pu->host)) {
 | |
| 	    sock = openSocket(FTP_proxy_parsed.host,
 | |
| 			      schemetable[FTP_proxy_parsed.scheme].cmdname,
 | |
| 			      FTP_proxy_parsed.port);
 | |
| 	    if (sock < 0)
 | |
| 		return uf;
 | |
| 	    uf.scheme = SCM_HTTP;
 | |
| 	    tmp = HTTPrequest(pu, current, &hr, extra_header);
 | |
| 	    write(sock, tmp->ptr, tmp->length);
 | |
| 	}
 | |
| 	else {
 | |
| 	    uf.stream = openFTPStream(pu);
 | |
| 	    uf.scheme = pu->scheme;
 | |
| 	    return uf;
 | |
| 	}
 | |
| 	break;
 | |
|     case SCM_HTTP:
 | |
| #ifdef USE_SSL
 | |
|     case SCM_HTTPS:
 | |
| #endif				/* USE_SSL */
 | |
| 	if (pu->file == NULL)
 | |
| 	    pu->file = allocStr("/", -1);
 | |
| 	if (request && request->method == FORM_METHOD_POST && request->body)
 | |
| 	    hr.command = HR_COMMAND_POST;
 | |
| 	if (request && request->method == FORM_METHOD_HEAD)
 | |
| 	    hr.command = HR_COMMAND_HEAD;
 | |
| 	if (non_null(HTTP_proxy) &&
 | |
| 	    !Do_not_use_proxy &&
 | |
| 	    pu->host != NULL && !check_no_proxy(pu->host)) {
 | |
| 	    char *save_label;
 | |
| #ifdef USE_SSL
 | |
| 	    if (pu->scheme == SCM_HTTPS && *status == HTST_CONNECT) {
 | |
| 		sock = ssl_socket_of(ouf->stream);
 | |
| 		if (!(sslh = openSSLHandle(sock))) {
 | |
| 		    *status = HTST_MISSING;
 | |
| 		    return uf;
 | |
| 		}
 | |
| 	    }
 | |
| 	    else {
 | |
| 		sock = openSocket(HTTP_proxy_parsed.host,
 | |
| 				  schemetable[HTTP_proxy_parsed.scheme].
 | |
| 				  cmdname, HTTP_proxy_parsed.port);
 | |
| 		sslh = NULL;
 | |
| 	    }
 | |
| #else
 | |
| 	    sock = openSocket(HTTP_proxy_parsed.host,
 | |
| 			      schemetable[HTTP_proxy_parsed.scheme].cmdname,
 | |
| 			      HTTP_proxy_parsed.port);
 | |
| #endif
 | |
| 	    if (sock < 0) {
 | |
| #ifdef SOCK_DEBUG
 | |
| 		sock_log("Can't open socket\n");
 | |
| #endif
 | |
| 		return uf;
 | |
| 	    }
 | |
| 	    save_label = pu->label;
 | |
| 	    pu->label = NULL;
 | |
| #ifdef USE_SSL
 | |
| 	    if (pu->scheme == SCM_HTTPS) {
 | |
| 		if (*status == HTST_NORMAL) {
 | |
| 		    hr.command = HR_COMMAND_CONNECT;
 | |
| 		    tmp = HTTPrequest(pu, current, &hr, NULL);
 | |
| 		    *status = HTST_CONNECT;
 | |
| 		}
 | |
| 		else {
 | |
| 		    hr.flag |= HR_FLAG_LOCAL;
 | |
| 		    tmp = HTTPrequest(pu, current, &hr, extra_header);
 | |
| 		    *status = HTST_NORMAL;
 | |
| 		}
 | |
| 	    }
 | |
| 	    else
 | |
| #endif				/* USE_SSL */
 | |
| 	    {
 | |
| 		tmp = HTTPrequest(pu, current, &hr, extra_header);
 | |
| 		*status = HTST_NORMAL;
 | |
| 		pu->label = save_label;
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    sock = openSocket(pu->host,
 | |
| 			      schemetable[pu->scheme].cmdname, pu->port);
 | |
| 	    if (sock < 0) {
 | |
| 		*status = HTST_MISSING;
 | |
| 		return uf;
 | |
| 	    }
 | |
| #ifdef USE_SSL
 | |
| 	    if (pu->scheme == SCM_HTTPS) {
 | |
| 		if (!(sslh = openSSLHandle(sock))) {
 | |
| 		    *status = HTST_MISSING;
 | |
| 		    return uf;
 | |
| 		}
 | |
| 	    }
 | |
| #endif				/* USE_SSL */
 | |
| 	    hr.flag |= HR_FLAG_LOCAL;
 | |
| 	    tmp = HTTPrequest(pu, current, &hr, extra_header);
 | |
| 	    *status = HTST_NORMAL;
 | |
| 	}
 | |
| #ifdef USE_SSL
 | |
| 	if (pu->scheme == SCM_HTTPS) {
 | |
| 	    uf.stream = newSSLStream(sslh, sock);
 | |
| 	    if (sslh)
 | |
| 		SSL_write(sslh, tmp->ptr, tmp->length);
 | |
| 	    else
 | |
| 		write(sock, tmp->ptr, tmp->length);
 | |
| 	    if (hr.command == HR_COMMAND_POST &&
 | |
| 		request->enctype == FORM_ENCTYPE_MULTIPART) {
 | |
| 		if (sslh)
 | |
| 		    SSL_write_from_file(sslh, request->body);
 | |
| 		else
 | |
| 		    write_from_file(sock, request->body);
 | |
| 	    }
 | |
| 	    return uf;
 | |
| 	}
 | |
| 	else
 | |
| #endif				/* USE_SSL */
 | |
| 	{
 | |
| 	    write(sock, tmp->ptr, tmp->length);
 | |
| #ifdef HTTP_DEBUG
 | |
| 	    {
 | |
| 		FILE *ff = fopen("zzrequest", "a");
 | |
| 		fwrite(tmp->ptr, sizeof(char), tmp->length, ff);
 | |
| 		fclose(ff);
 | |
| 	    }
 | |
| #endif				/* HTTP_DEBUG */
 | |
| 	    if (hr.command == HR_COMMAND_POST &&
 | |
| 		request->enctype == FORM_ENCTYPE_MULTIPART)
 | |
| 		write_from_file(sock, request->body);
 | |
| 	}
 | |
| 	break;
 | |
| #ifdef USE_GOPHER
 | |
|     case SCM_GOPHER:
 | |
| 	if (non_null(GOPHER_proxy) &&
 | |
| 	    !Do_not_use_proxy &&
 | |
| 	    pu->host != NULL && !check_no_proxy(pu->host)) {
 | |
| 	    sock = openSocket(GOPHER_proxy_parsed.host,
 | |
| 			      schemetable[GOPHER_proxy_parsed.scheme].cmdname,
 | |
| 			      GOPHER_proxy_parsed.port);
 | |
| 	    if (sock < 0)
 | |
| 		return uf;
 | |
| 	    uf.scheme = SCM_HTTP;
 | |
| 	    tmp = HTTPrequest(pu, current, &hr, extra_header);
 | |
| 	}
 | |
| 	else {
 | |
| 	    sock = openSocket(pu->host,
 | |
| 			      schemetable[pu->scheme].cmdname, pu->port);
 | |
| 	    if (sock < 0)
 | |
| 		return uf;
 | |
| 	    if (pu->file == NULL)
 | |
| 		pu->file = "1";
 | |
| 	    tmp = Strnew_charp(pu->file);
 | |
| 	    Strcat_char(tmp, '\n');
 | |
| 	}
 | |
| 	write(sock, tmp->ptr, tmp->length);
 | |
| 	break;
 | |
| #endif				/* USE_GOPHER */
 | |
| #ifdef USE_NNTP
 | |
|     case SCM_NNTP:
 | |
| 	/* nntp://<host>:<port>/<newsgroup-name>/<article-number> */
 | |
|     case SCM_NEWS:
 | |
| 	if (pu->scheme == SCM_NNTP) {
 | |
| 	    p = pu->host;
 | |
| 	}
 | |
| 	else {
 | |
| 	    p = getenv("NNTPSERVER");
 | |
| 	}
 | |
| 	r = getenv("NNTPMODE");
 | |
| 	if (p == NULL)
 | |
| 	    return uf;
 | |
| 	sock = openSocket(p, "nntp", pu->port);
 | |
| 	if (sock < 0)
 | |
| 	    return uf;
 | |
| 	stream = newInputStream(sock);
 | |
| 	fw = fdopen(sock, "wb");
 | |
| 	if (stream == NULL || fw == NULL)
 | |
| 	    return uf;
 | |
| 	tmp = StrISgets(stream);
 | |
| 	if (tmp->length == 0)
 | |
| 	    goto nntp_error;
 | |
| 	sscanf(tmp->ptr, "%d", &i);
 | |
| 	if (i != 200 && i != 201)
 | |
| 	    goto nntp_error;
 | |
| 	if (r && *r != '\0') {
 | |
| 	    fprintf(fw, "MODE %s\r\n", r);
 | |
| 	    fflush(fw);
 | |
| 	    tmp = StrISgets(stream);
 | |
| 	    if (tmp->length == 0)
 | |
| 		goto nntp_error;
 | |
| 	    sscanf(tmp->ptr, "%d", &i);
 | |
| 	    if (i != 200 && i != 201)
 | |
| 		goto nntp_error;
 | |
| 	}
 | |
| 	if (pu->scheme == SCM_NNTP) {
 | |
| 	    char *group;
 | |
| 	    if (pu->file == NULL || *pu->file == '\0')
 | |
| 		goto nntp_error;
 | |
| 	    /* first char of pu->file is '/' */
 | |
| 	    group = url_unquote(Strnew_charp(pu->file + 1)->ptr);
 | |
| 	    p = strchr(group, '/');
 | |
| 	    if (p == NULL)
 | |
| 		goto nntp_error;
 | |
| 	    *p++ = '\0';
 | |
| 	    fprintf(fw, "GROUP %s\r\n", group);
 | |
| 	    fflush(fw);
 | |
| 	    tmp = StrISgets(stream);
 | |
| 	    if (tmp->length == 0) {
 | |
| 		goto nntp_error;
 | |
| 	    }
 | |
| 	    sscanf(tmp->ptr, "%d", &i);
 | |
| 	    if (i != 211)
 | |
| 		goto nntp_error;
 | |
| 	    fprintf(fw, "ARTICLE %s\r\n", p);
 | |
| 	}
 | |
| 	else {
 | |
| 	    fprintf(fw, "ARTICLE <%s>\r\n", url_unquote(pu->file));
 | |
| 	}
 | |
| 	fflush(fw);
 | |
| 	tmp = StrISgets(stream);
 | |
| 	if (tmp->length == 0)
 | |
| 	    goto nntp_error;
 | |
| 	sscanf(tmp->ptr, "%d", &i);
 | |
| 	if (i != 220)
 | |
| 	    goto nntp_error;
 | |
| 	uf.scheme = SCM_NEWS;	/* XXX */
 | |
| 	uf.stream = stream;
 | |
| 	return uf;
 | |
|       nntp_error:
 | |
| 	ISclose(stream);
 | |
| 	fclose(fw);
 | |
| 	return uf;
 | |
| #endif				/* USE_NNTP */
 | |
|     case SCM_UNKNOWN:
 | |
|     default:
 | |
| 	return uf;
 | |
|     }
 | |
|     uf.stream = newInputStream(sock);
 | |
|     return uf;
 | |
| }
 | |
| 
 | |
| /* add index_file if exists */
 | |
| static void
 | |
| add_index_file(ParsedURL *pu, URLFile *uf)
 | |
| {
 | |
|     char *p, *q;
 | |
| 
 | |
|     if (index_file == NULL || index_file[0] == '\0') {
 | |
| 	uf->stream = NULL;
 | |
| 	return;
 | |
|     }
 | |
|     p = Strnew_m_charp(pu->file, "/", file_quote(index_file), NULL)->ptr;
 | |
|     p = cleanupName(p);
 | |
|     q = cleanupName(file_unquote(p));
 | |
|     examineFile(q, uf);
 | |
|     if (uf->stream == NULL)
 | |
| 	return;
 | |
|     pu->file = p;
 | |
|     pu->real_file = q;
 | |
|     return;
 | |
| }
 | |
| 
 | |
| static char *
 | |
| guessContentTypeFromTable(struct table2 *table, char *filename)
 | |
| {
 | |
|     char *p;
 | |
|     if (table == NULL)
 | |
| 	return NULL;
 | |
|     p = &filename[strlen(filename) - 1];
 | |
|     while (filename < p && *p != '.')
 | |
| 	p--;
 | |
|     if (p == filename)
 | |
| 	return NULL;
 | |
|     p++;
 | |
|     while (table->item1) {
 | |
| 	if (!strcmp(p, table->item1))
 | |
| 	    return table->item2;
 | |
| 	table++;
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| char *
 | |
| guessContentType(char *filename)
 | |
| {
 | |
|     char *ret;
 | |
|     int i;
 | |
| 
 | |
|     if (filename == NULL)
 | |
| 	return NULL;
 | |
|     if (mimetypes_list == NULL)
 | |
| 	goto no_user_mimetypes;
 | |
| 
 | |
|     for (i = 0; i < mimetypes_list->nitem; i++) {
 | |
| 	if ((ret =
 | |
| 	     guessContentTypeFromTable(UserMimeTypes[i], filename)) != NULL)
 | |
| 	    return ret;
 | |
|     }
 | |
| 
 | |
|   no_user_mimetypes:
 | |
|     return guessContentTypeFromTable(DefaultGuess, filename);
 | |
| }
 | |
| 
 | |
| TextList *
 | |
| make_domain_list(char *domain_list)
 | |
| {
 | |
|     char *p;
 | |
|     Str tmp;
 | |
|     TextList *domains = NULL;
 | |
| 
 | |
|     p = domain_list;
 | |
|     tmp = Strnew_size(64);
 | |
|     while (*p) {
 | |
| 	while (*p && IS_SPACE(*p))
 | |
| 	    p++;
 | |
| 	Strclear(tmp);
 | |
| 	while (*p && !IS_SPACE(*p) && *p != ',')
 | |
| 	    Strcat_char(tmp, *p++);
 | |
| 	if (tmp->length > 0) {
 | |
| 	    if (domains == NULL)
 | |
| 		domains = newTextList();
 | |
| 	    pushText(domains, tmp->ptr);
 | |
| 	}
 | |
| 	while (*p && IS_SPACE(*p))
 | |
| 	    p++;
 | |
| 	if (*p == ',')
 | |
| 	    p++;
 | |
|     }
 | |
|     return domains;
 | |
| }
 | |
| 
 | |
| static int
 | |
| domain_match(char *pat, char *domain)
 | |
| {
 | |
|     if (domain == NULL)
 | |
| 	return 0;
 | |
|     if (*pat == '.')
 | |
| 	pat++;
 | |
|     for (;;) {
 | |
| 	if (!strcasecmp(pat, domain))
 | |
| 	    return 1;
 | |
| 	domain = strchr(domain, '.');
 | |
| 	if (domain == NULL)
 | |
| 	    return 0;
 | |
| 	domain++;
 | |
|     }
 | |
| }
 | |
| 
 | |
| int
 | |
| check_no_proxy(char *domain)
 | |
| {
 | |
|     TextListItem *tl;
 | |
| 
 | |
|     if (NO_proxy_domains == NULL || NO_proxy_domains->nitem == 0 ||
 | |
| 	domain == NULL)
 | |
| 	return 0;
 | |
|     for (tl = NO_proxy_domains->first; tl != NULL; tl = tl->next) {
 | |
| 	if (domain_match(tl->ptr, domain))
 | |
| 	    return 1;
 | |
|     }
 | |
|     if (!NOproxy_netaddr) {
 | |
| 	return 0;
 | |
|     }
 | |
|     /* 
 | |
|      * to check noproxy by network addr
 | |
|      */
 | |
|     {
 | |
| #ifndef INET6
 | |
| 	struct hostent *he;
 | |
| 	int n;
 | |
| 	unsigned char **h_addr_list;
 | |
| 	char addr[4 * 16], buf[5];
 | |
| 
 | |
| 	he = gethostbyname(domain);
 | |
| 	if (!he)
 | |
| 	    return (0);
 | |
| 	for (h_addr_list = (unsigned char **)he->h_addr_list; *h_addr_list;
 | |
| 	     h_addr_list++) {
 | |
| 	    sprintf(addr, "%d", h_addr_list[0][0]);
 | |
| 	    for (n = 1; n < he->h_length; n++) {
 | |
| 		sprintf(buf, ".%d", h_addr_list[0][n]);
 | |
| 		strcat(addr, buf);
 | |
| 	    }
 | |
| 	    for (tl = NO_proxy_domains->first; tl != NULL; tl = tl->next) {
 | |
| 		if (strncmp(tl->ptr, addr, strlen(tl->ptr)) == 0)
 | |
| 		    return (1);
 | |
| 	    }
 | |
| 	}
 | |
| #else				/* INET6 */
 | |
| 	int error;
 | |
| 	struct addrinfo hints;
 | |
| 	struct addrinfo *res, *res0;
 | |
| 	char addr[4 * 16];
 | |
| 	int *af;
 | |
| 
 | |
| 	for (af = ai_family_order_table[DNS_order];; af++) {
 | |
| 	    memset(&hints, 0, sizeof(hints));
 | |
| 	    hints.ai_family = *af;
 | |
| 	    error = getaddrinfo(domain, NULL, &hints, &res0);
 | |
| 	    if (error) {
 | |
| 		if (*af == PF_UNSPEC) {
 | |
| 		    break;
 | |
| 		}
 | |
| 		/* try next */
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    for (res = res0; res != NULL; res = res->ai_next) {
 | |
| 		switch (res->ai_family) {
 | |
| 		case AF_INET:
 | |
| 		    inet_ntop(AF_INET,
 | |
| 			      &((struct sockaddr_in *)res->ai_addr)->sin_addr,
 | |
| 			      addr, sizeof(addr));
 | |
| 		    break;
 | |
| 		case AF_INET6:
 | |
| 		    inet_ntop(AF_INET6,
 | |
| 			      &((struct sockaddr_in6 *)res->ai_addr)->
 | |
| 			      sin6_addr, addr, sizeof(addr));
 | |
| 		    break;
 | |
| 		default:
 | |
| 		    /* unknown */
 | |
| 		    continue;
 | |
| 		}
 | |
| 		for (tl = NO_proxy_domains->first; tl != NULL; tl = tl->next) {
 | |
| 		    if (strncmp(tl->ptr, addr, strlen(tl->ptr)) == 0) {
 | |
| 			freeaddrinfo(res0);
 | |
| 			return 1;
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	    freeaddrinfo(res0);
 | |
| 	    if (*af == PF_UNSPEC) {
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| #endif				/* INET6 */
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| char *
 | |
| filename_extension(char *path, int is_url)
 | |
| {
 | |
|     char *last_dot = "", *p = path;
 | |
|     int i;
 | |
| 
 | |
|     if (path == NULL)
 | |
| 	return last_dot;
 | |
|     if (*p == '.')
 | |
| 	p++;
 | |
|     for (; *p; p++) {
 | |
| 	if (*p == '.') {
 | |
| 	    last_dot = p;
 | |
| 	}
 | |
| 	else if (is_url && *p == '?')
 | |
| 	    break;
 | |
|     }
 | |
|     if (*last_dot == '.') {
 | |
| 	for (i = 1; last_dot[i] && i < 8; i++) {
 | |
| 	    if (is_url && !IS_ALNUM(last_dot[i]))
 | |
| 		break;
 | |
| 	}
 | |
| 	return allocStr(last_dot, i);
 | |
|     }
 | |
|     else
 | |
| 	return last_dot;
 | |
| }
 |