Logo Search packages:      
Sourcecode: w3mmee version File versions  Download package

url.c

#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;

static struct cmdtable schemetable[] = {
#undef def_scheme
#define def_scheme(name, cmd, real_name, default_port, internal) {name, real_name, cmd, default_port, internal},
#include "scheme.h"
};

static btri_string_tab_t schemetab[] = {
#include "schemetab.h"
};

btri_string_tab_t DefaultGuess[] =
{
#include "defaultguess.h"
};

static void add_index_file(ParsedURL *pu, URLFile *uf, Phase0Env *p0env);

/* #define HTTP_DEFAULT_FILE    "/index.html" */

#ifndef HTTP_DEFAULT_FILE
#define HTTP_DEFAULT_FILE "/"
#endif                        /* not HTTP_DEFAULT_FILE */

#ifndef SOCK_DEBUG
#define SOCK_DEBUG
#endif

#ifdef SOCK_DEBUG
#include <stdarg.h>

static void
sock_log(char *message, ...)
{
    va_list va;
    Str emsg;

    va_start(va, message);
    emsg = VSprintf(message, va);
    va_end(va);
    disp_err_message(emsg->ptr, FALSE);
}
#endif

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);
    SIGNAL_RETURN;
}

#ifdef USE_SSL
SSL_CTX *ssl_ctx = NULL;

void
free_ssl_ctx(void)
{
    if (ssl_ctx != NULL)
      SSL_CTX_free(ssl_ctx);
    ssl_ctx = NULL;
    ssl_accept_this_site(NULL);
}

#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, char *hostname, char **p_cert)
{
    SSL *handle = NULL;
    static char *old_ssl_forbid_method = NULL;
#ifdef USE_SSL_VERIFY
    static int old_ssl_verify_server = -1;
#endif
    Str emsg;

    if (ssl_forbid_method != old_ssl_forbid_method &&
      (!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();
#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_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 */
#if 1                   /* use SSL_get_verify_result() to verify cert */
      SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
#else
      SSL_CTX_set_verify(ssl_ctx,
                     ssl_verify_server ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
                     NULL);
#endif
      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 */
    }
    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) {
      Str serv_cert = ssl_get_certificate(handle, hostname);
      if (serv_cert) {
          *p_cert = serv_cert->ptr;
          return handle;
      }
      close(sock);
      SSL_free(handle);
      return NULL;
    }
eend:
    if (sock == kept_sock)
      discardKeptSock();
    close(sock);
    if (handle)
      SSL_free(handle);
    emsg = Sprintf("SSL error: %s",
               ERR_error_string(ERR_get_error(), NULL));
    disp_err_message(emsg->ptr, FALSE);
    return NULL;
}

static int
SSL_safe_write(SSL *ssl, char *buf, int len)
{
    char *p;
    int n;

    for (p = buf ; len > 0 ;)
      if ((n = SSL_write(ssl, p, len)) > 0) {
          if ((len -= n) <= 0)
            break;

          p += n;
      }
      else
          switch ((n = SSL_get_error(ssl, n))) {
          case SSL_ERROR_WANT_READ:
          case SSL_ERROR_WANT_WRITE:
            continue;
          default:
            return SSL_ERROR_SYSCALL;
          }

    return SSL_ERROR_NONE;
}

static int
SSL_write_from_file(SSL *ssl, char *file)
{
    FILE *fp;
    int n, err;
    char buf[BUFSIZ];

    if ((fp = fopen(file, "r"))) {
      while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
          if ((err = SSL_safe_write(ssl, buf, n)) != SSL_ERROR_NONE)
            return err;

      fclose(fp);
    }

    return SSL_ERROR_NONE;
}
#endif                        /* USE_SSL */

static int
my_safe_write(int sock, char *buf, int len)
{
    char *p;
    int n;

    for (p = buf ; len > 0 ;)
      if ((n = write(sock, p, len)) > 0) {
          if ((len -= n) <= 0)
            break;

          p += n;
      }
      else
          switch (errno) {
          case EINTR:
          case EAGAIN:
            continue;
          default:
            return -1;
          }

    return 0;
}

static int
write_from_file(int sock, char *file)
{
    FILE *fp;
    int n, err;
    char buf[BUFSIZ];

    if ((fp = fopen(file, "r"))) {
      while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
          if ((err = my_safe_write(sock, buf, n)))
            return err;

      fclose(fp);
    }

    return 0;
}

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;
}

static char *kept_host;
static unsigned short kept_port;

int
keepSock(int sock)
{
    Str l;

    if (kept_sock < 0) {
      if (!kept_host)
          kept_host = "";

      fprintf(urgent_out, "%X %s:%X\n", urgent_keep_alive, kept_host, (unsigned int)kept_port);
      fflush(urgent_out);
      l = Strfgets(stdin);

      if (l->length && (l->ptr[0] == 'y')) {
          kept_sock = sock;
          return TRUE;
      }

      return FALSE;
    }

    return TRUE;
}

int
getKeptSock(char *host, unsigned short port)
{
    return (kept_host && !strcasecmp(kept_host, host) && kept_port == port) ? kept_sock : -1;
}

void
discardKeptSock(void)
{
    kept_host = NULL;
    kept_port = 0;
    kept_sock = -1;
}

int
openSocket(char * volatile 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;
    Str msg;

    if (remoteport_name && *remoteport_name && kept_sock >= 0) {
      if (!strcasecmp(kept_host, hostname) && kept_port == remoteport_num)
          return kept_sock;

      return -2;
    }
    msg = Sprintf("Opening socket...");
    if (w3m_backend >= BACKEND_VERBOSE)
        backend_message(msg->ptr, 1);
    else if (fmInitialized) {
        message(msg->ptr);
        refresh();
    }
    if (hostname == NULL) {
#ifdef SOCK_DEBUG
      sock_log("openSocket(NULL, %d) failed. reason: Bad hostname\n",
             (int)remoteport_num);
#endif
      goto error;
    }
    if (SETJMP(AbortLoading) != 0) {
#ifdef SOCK_DEBUG
      sock_log("openSocket(\"%s\", %d) failed. reason: user abort\n",
             hostname, (int)remoteport_num);
#endif
      if (sock >= 0)
          close(sock);
      goto error;
    }
    trap = signal(SIGINT, KeyAbort);
    if (fmInitialized)
      term_cbreak();

#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(\"%s\", %d): socket() failed. reason: %s\n",
             hostname, (int)remoteport_num, strerror(errno));
#endif
      goto error;
    }
    regexCompile("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$", 0);
    if (regexMatch(hostname, -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;
      msg = Sprintf("Connecting to %s", hostname);
      if (w3m_backend >= BACKEND_VERBOSE)
          backend_message(msg->ptr, 1);
      else if (fmInitialized) { 
          message(msg->ptr);
          refresh();
      }
      if (connect(sock, (struct sockaddr *)&hostaddr,
                sizeof(struct sockaddr_in)) < 0) {
#ifdef SOCK_DEBUG
          sock_log("openSocket(\"%s\", %d): connect() failed. reason: %s\n",
                 hostname, (int)remoteport_num, strerror(errno));
#endif
          goto error;
      }
    }
    else {
      char **h_addr_list;
      int result = -1;
      msg = Sprintf("Performing hostname lookup on %s", hostname);
      if (w3m_backend >= BACKEND_VERBOSE)
          backend_message(msg->ptr, 1);
      else if (fmInitialized) {
          message(msg->ptr);
          refresh();
      }
      if ((entry = gethostbyname(hostname)) == NULL) {
#ifdef SOCK_DEBUG
          sock_log("openSocket(\"%s\", %d): gethostbyname() failed. reason: %s\n",
                 hostname, (int)remoteport_num, 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);
#if 0 && defined(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
          msg = Sprintf("Connecting to %s", hostname);
          if (w3m_backend >= BACKEND_VERBOSE)
            backend_message(msg->ptr, 1);
          else if (fmInitialized) {
            message(msg->ptr);
            refresh();
          }
          if ((result = connect(sock, (struct sockaddr *)&hostaddr,
                          sizeof(struct sockaddr_in))) == 0) {
            break;
          }
#ifdef SOCK_DEBUG
          else {
            sock_log("openSocket(\"%s\", %d): connect() failed. reason: %s\n",
                   hostname, (int)remoteport_num, strerror(errno));
          }
#endif
      }
      if (result < 0) {
          goto error;
      }
    }
#endif                        /* not INET6 */

    if (sock >= 0 && remoteport_name && *remoteport_name) {
      kept_host = hostname;
      kept_port = remoteport_num;
    }

    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->scheme_charp = NULL;
    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 */
    q = p;
    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;
    }
    else if (p_url->scheme == SCM_UNKNOWN) {
      if (q < p) {
          Str s;

          s = Strnew_charp_n(q, p - 1 - q);
          Strlower(s);
          p_url->scheme_charp = s->ptr;
      }
      else
          p = q;
    }

    /* 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 = schemetable[p_url->scheme].port;
      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 ':':
      /* 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);
      /* *p is one of ['\0', '/', '?', '#'] */
      break;
    case '@':
      /* scheme://user@...            */
      p_url->user = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
      q = ++p;
      goto analyze_url;
    case '\0':
      /* scheme://host                */
    case '/':
    case '?':
    case '#':
      p_url->host = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
      p_url->port = schemetable[p_url->scheme].port;
      break;
    }
analyze_file:
#ifdef INET6
    /* rfc2732 compliance */
    if (p_url->host != NULL && p_url->host[0] == '[' &&
      p_url->host[strlen(p_url->host)-1] == ']' ) {
      p_url->host[strlen(p_url->host)-1] = '\0';
      ++(p_url->host);
    }
#endif
#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 = schemetable[SCM_FTP].port;
    }
#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;
    if (q->scheme_charp)
      p->scheme_charp = allocStr(q->scheme_charp, -1);
    else
      p->scheme_charp = NULL;
    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, file_unquote(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, file_unquote(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));
      }
    }
}

char *
getParsedURLScheme(ParsedURL *pu)
{
    return (((pu->scheme == SCM_UNKNOWN && !pu->scheme_charp) || pu->scheme == SCM_MISSING) ? NULL :
          pu->scheme == SCM_UNKNOWN ? pu->scheme_charp :
          schemetable[pu->scheme].realname);
}

static Str
_parsedURL2Str(ParsedURL *pu, int pass)
{
    Str tmp;
    char *scheme;

    if (!(scheme = getParsedURLScheme(pu))) {
      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);
    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 != schemetable[pu->scheme].port) {
          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->scheme == SCM_FTPDIR && Strlastchar(tmp) != '/')
      Strcat_char(tmp, '/');
    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;
    int scheme = SCM_MISSING;

    while (*p && (IS_ALPHA(*p) || *p == '.' || *p == '+' || *p == '-'))
      p++;
    if (*p == ':' && p > *url) { /* scheme found */
      void *pcmd;

      if (btri_fast_ci_search_mem(*url, p - *url, schemetab, &pcmd) != bt_failure
          && !((struct cmdtable *)pcmd)->internal)
          scheme = ((struct cmdtable *)pcmd)->cmd;
      else
          scheme = SCM_UNKNOWN;

      *url = p + 1;
    }
    return scheme;
}


static char *
otherinfo(ParsedURL *target, ParsedURL *current, char *referer, int proxy_p)
{
    Str s = Strnew();
    TextList *tls[] = {&HTTPRequestHeaderList, &ExtraHTTPRequestHeaderList, NULL}, **p;
    TextListItem *ti;

    Strcat_m_charp(s,
               "User-Agent: ", UserAgent, "\r\n",
               "Accept: ", AcceptMedia, "\r\n",
               NULL);
    if (AcceptEncoding && AcceptEncoding[0])
      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 != schemetable[target->scheme].port)
          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");
      }
      else if (UserSpecifiedReferer && *UserSpecifiedReferer)
          Strcat_m_charp(s, "Referer: ", UserSpecifiedReferer, "\r\n", NULL);
    }

    for (p = tls ; *p ; ++p)
      for (ti = (*p)->first ; ti ; ti = ti->next)
          if (proxy_p ?
            strncasecmp(ti->ptr, "Authorization:", sizeof("Authorization:") - 1) :
            strncasecmp(ti->ptr, "Proxy-Authorization:", sizeof("Proxy-Authorization:") - 1))
            Strcat_m_charp(s, ti->ptr, "\r\n", NULL);

    return s->ptr;
}

Str
HTTPrequestMethod(HRequest *hr)
{
    switch (hr->command) {
    case HR_COMMAND_CONNECT:
      return Strnew_charp("CONNECT");
    case HR_COMMAND_POST:
      return Strnew_charp("POST");
    case HR_COMMAND_HEAD:
      return Strnew_charp("HEAD");
    case HR_COMMAND_GET:
    default:
      return (hr->request && hr->request->method_str) ? Strdup(hr->request->method_str) : Strnew_charp("GET");
    }
}

Str
HTTPrequestURI(ParsedURL *pu, HRequest *hr)
{
    Str tmp = Strnew();
    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));
    }
    return tmp;
}

static Str
HTTPrequest(ParsedURL *pu, ParsedURL *current, HRequest *hr, TextList *extra, Phase0Env *p0env, int proxy_p)
{
    Str tmp;
    TextListItem *i;
    char *ct;
#ifdef USE_COOKIE
    Str cookie;
#endif                        /* USE_COOKIE */
    ct = (UserSpecifiedExtraContentType ? UserSpecifiedExtraContentType :
        UserSpecifiedContentType ? UserSpecifiedContentType : NULL);
    tmp = HTTPrequestMethod(hr);
    Strcat_charp(tmp, " ");
    Strcat_charp(tmp, HTTPrequestURI(pu, hr)->ptr);
    Strcat_m_charp(tmp, " HTTP/", HTTPVersion, "\r\n", NULL);
    if (hr->referer == NO_REFERER)
      Strcat_charp(tmp, otherinfo(pu, NULL, NULL, proxy_p));
    else
      Strcat_charp(tmp, otherinfo(pu, current, hr->referer, proxy_p));
    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) {
      if ((p0env->flag & RG_PROC_MASK) == RG_PROC_SUB) {
          fprintf(urgent_out, "%X %s\n", urgent_find_cookie, parsedURL2Str(pu)->ptr);
          fflush(urgent_out);
          cookie = Strfgets(stdin);
          Strchop(cookie);
          cookie = halfdump_buffer_unquote_to_str(cookie->ptr);
      }
      else
          cookie = find_cookie(pu);

      if (cookie) {
          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 ||
      (hr->command == HR_COMMAND_GET && hr->request && hr->request->method_str)) {
      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 {
          Strcat_m_charp(tmp, "Content-Type: ", ct ? ct : "application/x-www-form-urlencoded",
                     "\r\n", NULL);
          Strcat(tmp, Sprintf("Content-Length: %ld\r\n", hr->request->length));
          Strcat_charp(tmp, "\r\n");
          Strcat_charp_n(tmp, hr->request->body, hr->request->length);
          Strcat_charp(tmp, "\r\n");
      }
    }
    else {
      if (ct)
          Strcat_m_charp(tmp, "Content-Type: ", ct, "\r\n", NULL);
      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->redirected = FALSE;
    uf->content_encoding = NULL;
    uf->guess_type = NULL;
    uf->ext = NULL;
#ifdef USE_SSL
    uf->ssl_certificate = NULL;
#endif
}

URLFile
openURL(char *url, ParsedURL *pu, ParsedURL *current,
      URLOption *option, FormList *request, TextList *extra_header,
      URLFile *ouf, HRequest *hr, unsigned char *status,
      Phase0Env *p0env)
{
    Str tmp;
    int i, sock, scheme;
    char *p = NULL, *q, *u;
    URLFile uf;
    HRequest hr0;
#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 (hr == NULL)
      hr = &hr0;

    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;
    uf.url = parsedURL2Str(pu)->ptr;
    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 = newInputStream(localcgi_post(pu->real_file,
                                         pu->query,
                                         request,
                                         option->referer));
          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 = newInputStream(localcgi_get(pu->real_file, pu->query,
                                        option->referer));
          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 = newInputStream(localcgi_get(pu->real_file, NULL,
                                        option->referer));
          if (uf.stream == NULL)
            goto ordinary_local_file;
          uf.is_cgi = TRUE;
          uf.scheme = pu->scheme = SCM_LOCAL_CGI;
      }
      else {
      ordinary_local_file:
          examineFileExt(pu->real_file, &uf, p0env);
      }
      if (uf.stream == NULL) {
          if (dir_exist(pu->real_file)) {
            add_index_file(pu, &uf, p0env);
            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, p0env);
                if (uf.stream == NULL) {
                  return uf;
                }
            }
            else {
                examineFileExt(q, &uf, p0env);
                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;
            parseURL2(url, pu, current);
            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)) {
      retry_open_ftp_proxy:
          sock = openSocket(FTP_proxy_parsed.host,
                        schemetable[FTP_proxy_parsed.scheme].cmdname,
                        FTP_proxy_parsed.port);
          if (sock < 0) {
            if (sock == -2)
                uf.redirected = TRUE;

            return uf;
          }
          uf.scheme = SCM_HTTP;
          tmp = HTTPrequest(pu, current, hr, extra_header, p0env, TRUE);
          if (my_safe_write(sock, tmp->ptr, tmp->length) &&
            sock == kept_sock) {
            discardKeptSock();
            close(sock);
            goto retry_open_ftp_proxy;
          }
      }
      else {
          openFTP(&uf, pu, p0env);
          return uf;
      }
      break;
    case SCM_HTTP:
#ifdef USE_SSL
    case SCM_HTTPS:
#endif                        /* USE_SSL */
      get_auth_cookie("Authorization:", extra_header, pu, hr, request, p0env);
      if (pu->file == NULL)
          pu->file = allocStr("/", -1);
      if (request) {
          if (request->method == FORM_METHOD_POST && request->body)
            hr->command = HR_COMMAND_POST;
          else if (request->method == FORM_METHOD_HEAD)
            hr->command = HR_COMMAND_HEAD;
      }
    retry_open_http:
      if ((
#ifdef USE_SSL
           (pu->scheme == SCM_HTTPS) ? non_null(HTTPS_proxy) :
#endif                        /* USE_SSL */
           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, pu->host, &uf.ssl_certificate))) {
                *status = HTST_MISSING;
                return uf;
            }
          }
          else if (pu->scheme == SCM_HTTPS) {
            sock = openSocket(HTTPS_proxy_parsed.host,
                          schemetable[HTTPS_proxy_parsed.scheme].cmdname,
                          HTTPS_proxy_parsed.port);
            sslh = NULL;
          }
          else {
#endif                        /* USE_SSL */
                sock = openSocket(HTTP_proxy_parsed.host,
                              schemetable[HTTP_proxy_parsed.scheme].cmdname,
                              HTTP_proxy_parsed.port);
#ifdef USE_SSL
                sslh = NULL;
          }
#endif                        /* USE_SSL */
          if (sock < 0) {
            if (sock == -2)
                uf.redirected = TRUE;
#ifdef SOCK_DEBUG
            else
                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, extra_header, p0env, TRUE);
                *status = HTST_CONNECT;
            }
            else {
                hr->flag |= HR_FLAG_LOCAL;
                tmp = HTTPrequest(pu, current, hr, extra_header, p0env, TRUE);
                *status = HTST_NORMAL;
            }
          }
          else
#endif                        /* USE_SSL */
            {
                tmp = HTTPrequest(pu, current, hr, extra_header, p0env, TRUE);
                *status = HTST_NORMAL;
                pu->label = save_label;
            }
      }
      else {
          sock = openSocket(pu->host,
                        schemetable[pu->scheme].cmdname, pu->port);
          if (sock < 0) {
            if (sock == -2)
                uf.redirected = TRUE;

            *status = HTST_MISSING;
            return uf;
          }
#ifdef USE_SSL
          if (pu->scheme == SCM_HTTPS) {
            if (!(sslh = openSSLHandle(sock, pu->host, &uf.ssl_certificate))) {
                *status = HTST_MISSING;
                return uf;
            }
          }
#endif                        /* USE_SSL */
          hr->flag |= HR_FLAG_LOCAL;
          tmp = HTTPrequest(pu, current, hr, extra_header, p0env, FALSE);
          *status = HTST_NORMAL;
      }
#ifdef USE_SSL
      if (pu->scheme == SCM_HTTPS) {
          uf.stream = newSSLStream(sslh, sock);
          if (sslh) {
            if (SSL_safe_write(sslh, tmp->ptr, tmp->length) != SSL_ERROR_NONE &&
                sock == kept_sock) {
                discardKeptSock();
                close(sock);
                SSL_free(sslh);
                goto retry_open_http;
            }
          }
          else if (my_safe_write(sock, tmp->ptr, tmp->length) &&
                 sock == kept_sock) {
            discardKeptSock();
            close(sock);
            goto retry_open_http;
          }
          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 */
          {
            if (my_safe_write(sock, tmp->ptr, tmp->length) &&
                sock == kept_sock) {
                discardKeptSock();
                close(sock);
                goto retry_open_http;
            }
#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:
    retry_open_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) {
            if (sock == -2)
                uf.redirected = TRUE;

            return uf;
          }
          uf.scheme = SCM_HTTP;
          tmp = HTTPrequest(pu, current, hr, extra_header, p0env, TRUE);
      }
      else {
          sock = openSocket(pu->host,
                        schemetable[pu->scheme].cmdname, pu->port);
          if (sock < 0) {
            if (sock == -2)
                uf.redirected = TRUE;

            return uf;
          }
          if (pu->file == NULL)
            pu->file = "1";
          tmp = Strnew_charp(pu->file);
          tmp = Str_form_unquote(tmp);
          Strcat_char(tmp, '\n');
      }
      if (my_safe_write(sock, tmp->ptr, tmp->length) &&
          sock == kept_sock) {
          discardKeptSock();
          close(sock);
          goto retry_open_gopher;
      }
      break;
#endif                        /* USE_GOPHER */
#ifdef USE_NNTP
    case SCM_NNTP:
      /* nntp://<host>:<port>/<newsgroup-name>/<article-number> */
    case SCM_NEWS:
        /* news:<newsgroup-name> XXX: not yet */
        /* news:<unique>@<full_domain_name> */
      if (pu->scheme == SCM_NNTP) {
          p = pu->host;
      }
      else {
          p = getenv("NNTPSERVER");
      }
      if (p == NULL)
          return uf;
      r = getenv("NNTPMODE");
      sock = openSocket(p, "nntp", pu->port);
      if (sock < 0) {
          if (sock == -2)
            uf.redirected = TRUE;

          return uf;
      }
      stream = newInputStream(sock);
      fw = fdopen(sock, "wb");
      if (stream == NULL || fw == NULL)
          return uf;
      tmp = StrISgets(stream, NULL);
      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, NULL);
          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, NULL);
          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 if (pu->file) {
          fprintf(fw, "ARTICLE <%s>\r\n", url_unquote(pu->file));
      }
      else if (pu->host && (pu->user || pu->pass)) {
          /* broken URI (news://<local_part>@<domain_part>) */
          if (!pu->pass)
            fprintf(fw, "ARTICLE <%s@%s>\r\n", url_unquote(pu->user), url_unquote(pu->host));
          else if (!pu->user)
            fprintf(fw, "ARTICLE <:%s@%s>\r\n", url_unquote(pu->pass), url_unquote(pu->host));
          else
            fprintf(fw, "ARTICLE <%s:%s@%s>\r\n", url_unquote(pu->user), url_unquote(pu->pass), url_unquote(pu->host));
      }
      else
          goto nntp_error;
      fflush(fw);
      tmp = StrISgets(stream, NULL);
      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, Phase0Env *p0env)
{
    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));
    examineFileExt(q, uf, p0env);
    if (uf->stream == NULL)
      return;
    pu->file = p;
    pu->real_file = q;
    return;
}

char *
guessContentTypeFromTable(btri_string_tab_t *table, char *filename, char **p_ext_beg, char **p_ext_end)
{
    char *type = NULL;
    char *ext_beg = NULL, *ext_end = NULL;

    if (table) {
      char *p, *q;

      for (p = filename ; (q = strchr(p, '/')) ;)
          p = &q[1];

      if (*p && (p = strchr(&p[1], '.'))) {
          static btri_string_tab_t *langtab = NULL;

          if (!langtab) {
            char *langx = LangExt;

            langtab = btri_new_node(&btri_string_ci_tab_desc);

            if (langx) {
                char *s;
                Str d;
                int i, j, k;

                for (s = langx ;; s += i + 1) {
                  i = strcspn(s, ",");

                  for (j = 0 ; j < i ; ++j)
                      if (!IS_SPACE(s[j])) break;

                  for (k = j ; k < i ; ++k)
                      if (IS_SPACE(s[k]) || s[k] == ',' || s[k] == ';') break;

                  if (j < k) {
                      d = Strnew_charp_n(&s[j], k - j);
                      btri_search_mem(&btri_string_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
                                  d->ptr, d->length, langtab, (void **)&d->ptr);
                  }

                  if (!s[i]) break;
                }
            }
          }

          for (;;) {
            size_t n = strcspn(++p, ".");

            if (btri_fast_ci_search_mem(p, n, table, (void **)&q) != bt_failure) {
                type = q;
                ext_beg = p - 1;
                ext_end = &p[n];
            }
            else if (!langtab || btri_fast_ci_search_mem(p, n, langtab, NULL) == bt_failure)
                type = NULL;

            if (!*(p += n))
                break;
          }
      }
    }

    if (type) {
      if (p_ext_beg) *p_ext_beg = ext_beg;
      if (p_ext_end) *p_ext_end = ext_end;
    }

    return type;
}

char *
guessContentTypeAndExtension(char *filename, char **p_ext_beg, char **p_ext_end)
{
    char *ret;
    int i;

    if (!filename)
      return NULL;

    if (mimetypes_list)
      for (i = 0; i < mimetypes_list->nitem; i++)
          if ((ret = guessContentTypeFromTable(UserMimeTypes[i], filename, p_ext_beg, p_ext_end)))
            return ret;

    return guessContentTypeFromTable(DefaultGuess, filename, p_ext_beg, p_ext_end);
}

char *
guessContentType(char *filename)
{
    return guessContentTypeAndExtension(filename, NULL, NULL);
}

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;
}

/*
 * RFC2396: Uniform Resource Identifiers (URI): Generic Syntax
 * Appendix A. Collected BNF for URI
 * uric          = reserved | unreserved | escaped
 * reserved      = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
 *                 "$" | ","
 * unreserved    = alphanum | mark
 * mark          = "-" | "_" | "." | "!" | "~" | "*" | "'" |
 *                  "(" | ")"
 * escaped       = "%" hex hex
 */

#define URI_PATTERN     "([-;/?:@&=+$,a-zA-Z0-9_.!~*'()]|(%[0-9A-Fa-f][0-9A-Fa-f]))*"
void
chkExternalURIBuffer(Str rexstr)
{
    struct mailcap *mp, **pmp;
    char *p;
    TextList *l;
    btri_string_tab_t *mhash;
    void *dummy = NULL;
    Str s;

    l = newTextList();
    mhash = btri_new_node(&btri_string_ci_tab_desc);

    for (pmp = UserBrowsecap ; (mp = *pmp) ; ++pmp)
      for (; mp->type; mp++)
          if ((p = strchr(mp->type, '/')) && !(mp->flags & MAILCAP_INTERNAL) &&
            btri_fast_ci_search_mem(mp->type, p - mp->type, mhash, &dummy) == bt_failure) {
            s = Strnew_charp_n(mp->type, p - mp->type);
            btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
                        s->ptr, s->length, mhash, &dummy);
            pushText(l, s->ptr);
          }

    while ((p = popText(l)))
      Strcat(rexstr, Sprintf("|%s:%s", p, URI_PATTERN));
}

ParsedURL *
schemeToProxy(int scheme)
{
    ParsedURL *pu = NULL;     /* for gcc */
    switch (scheme) {
      case SCM_HTTP:
          pu = &HTTP_proxy_parsed;
          break;
#ifdef USE_SSL
      case SCM_HTTPS:
          pu = &HTTPS_proxy_parsed;
          break;
#endif
      case SCM_FTP:
          pu = &FTP_proxy_parsed;
          break;
#ifdef USE_GOPHER
      case SCM_GOPHER:
          pu = &GOPHER_proxy_parsed;
          break;
#endif
#ifdef DEBUG
      default:
          abort();
#endif
    }
    return pu;
}

/* Local Variables:    */
/* c-basic-offset: 4   */
/* tab-width: 8        */
/* End:                */

Generated by  Doxygen 1.6.0   Back to index