//
// Copyright (C) 1999-2002 Toshikaz Hirabayashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// TOSHIKAZ HIRABAYASHI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Except as contained in this notice, the name of Toshikaz Hirabayashi shall
// not be used in advertising or otherwise to promote the sale, use or other
// dealings in this Software without prior written authorization from
// Toshikaz Hirabayashi.

#define _ANSI_SOURCE
#include <basic.h>
#include <btron/bsocket.h>
#ifdef TE
#include <btron/errcode.h>
#else
#include <errcode.h>
#endif
//#include <WScom.h>
#include <WStcpcom.h>
#include <WSCstring.h>
#include <stdio.h>
#include <stdlib.h>

static char WS_CONTENT_TYPE[]="Content-type:";
static char WS_CONTENT_LEN[]="Content-length:";
static char WS_POST_CONTENT_TYPE[]="application/x-www-form-urlencoded";

void WSGFtcpInit(){ }

static void SetSocketBlockingState(int* sock,int blocking){
  int ret;
//#ifdef O_NONBLOCK
  int flags = so_fcntl(*sock, F_GETFL,0);
  ret = so_fcntl(*sock, F_SETFL, blocking ? (flags & (~O_NDELAY)) : (flags | O_NDELAY));
//#else
//  int val=(!n_blocking);
//  ret = ioctl(*sock, FIONBIO, &val);
//#endif
  if (ret == -1) {
    fprintf(stderr, "Connection error. <%s>\n", (blocking ? "blocking" : "non-blocking"));
  }
}

#define MIN_READ_ERR_SIZE 0x100
long WSGFtcpRead(int sock,char** outbuf,int* size){
  long bsize = 0x400;
  long eof_fl = False;
  long len = 0;

  char *buf1 = new char[bsize];
  memset(buf1,0, bsize * sizeof(char));

  if(size != NULL){
    *size = 0;
  }
  *outbuf = NULL;
  if (buf1 == NULL) {
    fprintf(stderr, "Memory allocation error.\n");
    return WS_MEM_ERR;
  }
  do{
    int bytes_read;
    if (bsize - len < MIN_READ_ERR_SIZE) {
      bsize += 0x400;
      delete buf1;
      buf1 = new char[bsize];
      if ( buf1 == NULL ){
        fprintf(stderr, "Memory allocation error.\n");
        return WS_MEM_ERR;
      }
    }
    bytes_read = so_read(sock, &buf1[len], bsize-len-1);
    if (bytes_read <= 0) {
      if (bytes_read < 0 && (bytes_read == EX_NOTCONN || bytes_read == EX_CONNRESET || bytes_read == EX_BADF)) {
        fprintf(stderr, "Network read error.\n");
        delete buf1;
        return WS_READ_ERR;
      }else if (bytes_read < 0) {
        fprintf(stderr, "Network error.\n");
        delete buf1;
        return WS_NET_ERR;
      }
      eof_fl = True;
    }else{
      len += bytes_read;
    }
  }while(!eof_fl);

  buf1[len] = '\0';
  *outbuf = buf1;
  if(size != NULL){
    *size = (len+1);
  }
  return WS_NO_ERR;
}


long WSGFtcpWrite(int sock,char* data,int size){
  if (data == NULL){
    return WS_NO_ERR;
  }
  int ret = so_write(sock, data, (int)size);
  if (ret <= 0){
    if (ret == 0){
      fprintf(stderr, "0 bytes written.\n");
    }else if ((ret == EX_NOTCONN || ret == EX_CONNRESET || ret == EX_BADF)) {
      fprintf(stderr, "Network write error.\n");
      return WS_WRITE_ERR;
    }
  }
  return WS_NO_ERR;
}

long WSGFtcpConnect(char* host,int  port,int* sock){
  int status = WS_NO_ERR;
  struct sockaddr_in addr;
  struct sockaddr_in *sin=(&addr);
  struct hostent hostent1;
  struct hostent *hostent=&hostent1;

  if (*host >= '0' && *host <= '9') {
    sin->sin_addr.s_addr = inet_addr(host);
  }else{
    B tmpbuf[HBUFLEN];
    ERR ret = so_gethostbyname(host,hostent,tmpbuf);
    if (ret < 0) {
      fprintf(stderr, "Cannot find Internet node name.\n");
      return WS_HOSTN_ERR;
    }
    memcpy(&sin->sin_addr, hostent->h_addr, hostent->h_length);
  }
  sin->sin_family = AF_INET;
  sin->sin_port = htons((WSCushort)port);
  *sock = so_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  SetSocketBlockingState(sock, False);

  status = so_connect(*sock, (struct sockaddr*)&addr, sizeof(addr));
  if ((status < 0) && (status==EX_INPROGRESS || status==EX_WOULDBLOCK)) {
    struct timeval timeout;
    int ret=0;

    timeout.tv_sec = 0;
    timeout.tv_usec = 100000;
    while (ret <= 0) {
      fd_set writefds;

      FD_ZERO(&writefds);
      FD_SET(*sock, &writefds);
#ifdef HPUX
      ret = so_select(FD_SETSIZE, NULL, (int*)&writefds, NULL, &timeout);
#else //HPUX
      ret = so_select(FD_SETSIZE, NULL, &writefds, NULL, &timeout);
#endif //HPUX
      if ((ret < 0)&&(ret != EX_ALREADY)) {
        status = ret;
        break;
      }else if (ret > 0) {
        status = so_connect(*sock, (struct sockaddr*)&addr,sizeof(addr));
        if ((status < 0)&&(status == EX_ISCONN)){
          status = WS_NO_ERR;
        }
        if (status == EX_ALREADY){
          ret = 0;
        }else{
          break;
        }
      }else{
        status = so_connect(*sock, (struct sockaddr*)&addr, sizeof(addr));
        if ((status < 0) && (status != EX_ALREADY) && (status != EX_ISCONN) && (status != EX_WOULDBLOCK)){
               break;
         }
      }
    }
  }
  if (status >= 0) {
    SetSocketBlockingState(sock, True);
  }else{
    so_close(*sock);
  }
  return status;
}


#define WS_MAX_PATH_LEN 2048

#ifdef DS
extern "C"{
  extern int gethostname(char*,WSCuint);
}
#endif

#if 0 /*SUN*/
extern "C"{
  extern int gethostname(char*,WSCuint);
}
#endif


static char _ws_http_ver[]="HTTP/1.0";
static char _client_name[1024];
static char _user_name_buf[1024];

static long _cgi_query        = False;
static char *_fname_cgi_query = NULL;

#ifdef DS90
extern "C"{
  extern int gethostname(char*,WSCuint);
}
#endif
#if 0 /*SUN*/
extern "C"{
  extern int gethostname(char*,WSCuint);
}
#endif


struct WSCurlCache {
   long   fl;
   long   rbuf_size;
   char*  url_base_name;
   char*  rbuf;
   char*  content_type;
   struct WSCurlCache* child;
   struct WSCurlCache* parent;
   WSCurlCache(){
     fl = 0;
     rbuf_size = 0;
     rbuf = NULL;
     content_type = NULL;
     url_base_name = NULL;
     child = NULL;
     parent = NULL;
   };
   ~WSCurlCache(){
     if (rbuf != NULL){
       delete rbuf;
     }
     if (content_type != NULL){
       delete content_type;
     }
     if (url_base_name != NULL){
       delete url_base_name;
     }
   };
};

static char          _default_file[] = "index.html";
static char          _http_proxy[WS_MAX_PATH_LEN+1];
static WSCurlCache* _first_url_cache = NULL;
static WSCurlCache* _last_url_cache  = NULL;
static long          _max_url_cache   = -1;
static long          _cur_url_cache   = 0;

static long          _http_initialized = 0;
long WSGFhttpInit(){
  if (_http_initialized != 0){
    return 0;
  }
  _http_initialized = 1;
extern void WSGFtcpInit();
  WSGFtcpInit();
  char* httpp = getenv("WSHTPROXY");
  if (httpp != NULL){
    strcpy(_http_proxy,httpp);
  }
  return 0;
}


void _get_user_name(char* buffer,long buf_size){
   char user_name[1024];
   long total=0;

   sprintf(user_name, "ws@");
   total = strlen(user_name);
#if 0
   if (gethostname(&user_name[total], sizeof(user_name)-1-total) < 0) {
      sprintf(&user_name[total], "UNKNOWN");
   }else{
#endif
   {
      B tmpbuf[HBUFLEN];
      struct hostent p_hostent;
      ERR ret = so_gethostbyname(&user_name[total],&p_hostent,tmpbuf);

      if (ret == 0 && p_hostent.h_name != NULL &&
            p_hostent.h_name[0] != '\0') {
         strcpy(&user_name[total], p_hostent.h_name);
      }
   }
   strncpy(buffer, user_name, buf_size);
}

static char *_get_url_name(char *url){
   char *ptr = strchr(url, '#');

   if (ptr != NULL) {
      char *return_buf;

      *ptr = '\0';
      return_buf = WSGFstrdup(url);
      *ptr = '#';
      return return_buf;
   }
   return WSGFstrdup(url);
}

static void _init_cache(){
  if (_max_url_cache == (-1)) {
    _max_url_cache = 40;
    _cur_url_cache = 0;
    _first_url_cache = _last_url_cache = NULL;
  }
}

static void _remove_cache( struct WSCurlCache *url){
  if (url == NULL) return;
  if (url->parent == NULL) {
    _first_url_cache = url->child;
  }else{
    url->parent->child = url->child;
  }
  if (url->child == NULL) {
    _last_url_cache = url->parent;
  }else{
    url->child->parent = url->parent;
  }
  url->parent = url->child = NULL;
  _cur_url_cache--;
}


static void _add_cache(struct WSCurlCache* parent_url,
                           struct WSCurlCache* child_url,
                           struct WSCurlCache* url) {
  url->parent = parent_url;
  url->child = child_url;
  if (parent_url == NULL) {
    _first_url_cache = url;
  }else{
    parent_url->child = url;
  }
  if (child_url == NULL) {
    _last_url_cache = url;
  }else{
    child_url->parent = url;
  }
  _cur_url_cache++;
}

void _update_url( struct WSCurlCache *url){
   _remove_cache(url);
   _add_cache(_last_url_cache, NULL, url);
}

static WSCurlCache *_find_url_cache(char* url_name,long update_lru){
  char *url_base_name = _get_url_name(url_name);
  struct WSCurlCache *url;

  _init_cache();
  if (url_base_name == NULL){
    return NULL;
  }
  for (url=_last_url_cache; url != NULL; url=url->parent) {
    if (strcmp(url_base_name, url->url_base_name) == 0) {
      delete url_base_name;
      if (update_lru){
        _update_url(url);
      }
      return url;
    }
  }
  delete url_base_name;
  return NULL;
}

static char *_get_proxy_info(char* proxy_spec,long def_port,long* pn_port){
   char *colon_ptr=strchr(proxy_spec, ':'), *return_buf=NULL;

   if (colon_ptr == NULL) {
      *pn_port = def_port;
      return_buf = WSGFstrdup(proxy_spec);
   }else{
      *colon_ptr = '\0';
      *pn_port = atoi(&colon_ptr[1]);
      return_buf = WSGFstrdup(proxy_spec);
      *colon_ptr = ':';
   }
   return return_buf;
}

static long _usr_agent_name_inited = False;

static void _init_client_name() {
   if (_usr_agent_name_inited){
     return;
   }
   _usr_agent_name_inited = True;
   sprintf(_client_name,"ws/web");
   _get_user_name(_user_name_buf, sizeof(_user_name_buf));
}

long WSGFhttpConnect(char* host,long port,int* sock) {
   long rc, len=strlen(host)+80;
   char *msg=new char[len+1];

   if (msg == NULL) {
      WSMFtrace("Memory allocation failed.\n");
      return WS_MEM_ERR;
   }

#ifdef DTRACE
   WSMFtrace("Making an HTTP connection to \"%s:%1d\"...\n", host, port);
#endif

   rc = WSGFtcpConnect(host, port, sock);

   if (rc == WS_NO_ERR) {
#ifdef DTRACE
      WSMFtrace("HTTP: connection to \"%s:%1d\" established.\n", host, port);
#endif
   }else{
      WSMFtrace("Fail to connect to HTTP server on \"%s:%1d\".\n", host, port);
   }
   delete msg;

   return rc;
}

static char *accept_strs[] = {
  "text/plain",
  "text/html",
  "application/x-ws",
  "*/*",
  NULL
};

static char *_append_acstr(char* buf){
  char **ptr;
  WSCstring tmp;
  tmp.setString(buf);
  delete buf;
  for (ptr=accept_strs; *ptr != NULL; ptr++) {
    WSCstring tmp2;
    tmp2.setString(tmp.getString());
    tmp2.addString("Accept: ");
    tmp2.addString(*ptr);
    tmp2.addString("\r\n");
    tmp.setString(tmp2.getString());
  }
  buf = WSGFstrdup(tmp.getString());
  return buf;
}

static char *_append_string(char* buf,char* name,char* value){
  WSCstring tmp;
  if (buf != NULL){
    tmp.setString(buf);
    delete buf;
    tmp.addString(name);
    tmp.addString(": ");
    tmp.addString(value);
    tmp.addString("\r\n");
    return WSGFstrdup(tmp.getString());
  }else{
    tmp.setString(name);
    tmp.addString(": ");
    tmp.addString(value);
    tmp.addString("\r\n");
    return WSGFstrdup(tmp.getString());
  }
}

static char *_append_uastr(char* buf){
  _init_client_name();
  return _append_string(buf, "User-Agent", _client_name);
}

static char *_append_from(char* buf){
   _init_client_name();
   return _append_string(buf, "From", _user_name_buf);
}

static char *_append_typestr(char* buf){
   return _append_string(buf, "Content-type", WS_POST_CONTENT_TYPE);
}

static char* _append_lenstr(char* buf,long content_length){
   char len_str[20];
   sprintf(len_str, "%1d", content_length);
   return _append_string(buf, "Content-length", len_str);
}

static char* _append_connect_str(char* buf,FILE* fp,long content_length){
  long cur_len=strlen(buf), bytes_read, total_read=0;
  long new_len=cur_len+content_length+1;
  char tmp_buf[512];
  char* new_buf = new char[new_len];
  if (new_buf == NULL){
    return NULL;
  }
  strcpy(new_buf,buf);
  delete buf;
  while ((bytes_read=fread(tmp_buf, sizeof(char), sizeof(tmp_buf),fp)) > 0) {
    if (bytes_read+total_read > content_length) {
      bytes_read = content_length-total_read;
      fprintf(stderr, "Lines too long in _append_connect_str().\n");
    }
    strncpy(&new_buf[cur_len+total_read], tmp_buf, bytes_read);
    total_read += bytes_read;
  }
  new_buf[cur_len + content_length] = '\0';
  return new_buf;
}

static char* append_return(char* buf){
  if (buf != NULL){
    WSCstring tmp;
    tmp.setString(buf);
    tmp.addString("\r\n");
    delete buf;
    return WSGFstrdup(tmp.getString());
  }else{
    return WSGFstrdup("\r\n");
  }
}

long WSGFhttpWrite(int sock,char* path){
  long status=0, total_sz=0;
  char *buf=(char*)calloc(strlen(path)+5+2+31, sizeof(char));
  FILE *fp=NULL;

  if (buf == NULL) {
    fprintf(stderr, "Memory allocation failed.\n");
    return WS_MEM_ERR;
  }
  if (_cgi_query) {
     sprintf(buf, "POST %s %s\r\n", path, _ws_http_ver);
  }else{
     sprintf(buf, "GET %s %s\r\n", path, _ws_http_ver);
  }
  buf=_append_acstr(buf);
  buf=_append_uastr(buf);
  buf=_append_from(buf);

  if (_cgi_query && _fname_cgi_query != NULL) {
     long bytes_read;
     char tmp_buf[512];

     if ((fp=fopen(_fname_cgi_query, "r")) == NULL) {
        fprintf(stderr, "Fail to open '%s' for read.\n",
              _fname_cgi_query);
        return WS_READ_ERR;
     }
     while ((bytes_read=fread(tmp_buf, sizeof(char), sizeof(tmp_buf),fp)) > 0){
        total_sz += bytes_read;
     }
     rewind(fp);
     buf=_append_typestr(buf);
     buf=_append_lenstr(buf, total_sz);
  }
  buf=append_return(buf);
  if (fp != NULL) {
      buf = _append_connect_str(buf, fp, total_sz);
      fclose(fp);
      if (buf == NULL) {
         fprintf(stderr, "Memory allocation failed.\n");
         return WS_MEM_ERR;
      }
  }

#ifdef DTRACE
   WSMFtrace("HTTP: sending requests...\n");
#endif

  status = WSGFtcpWrite(sock, buf, (long)strlen(buf));
  delete buf;

  if (status != WS_NO_ERR) {
     WSMFtrace("HTTP: fail to send requests.\n");
  }
  return status;
}

char *_http_extxt(char* buf,long* bsize,long* fl,char** ctype){
  char* ptr = strchr(buf, '\n');
  char* content_type = NULL;
  char* line_ptr = buf;
  long  content_length = -1;
  long  len2 = strlen(WS_CONTENT_LEN);
  long  len1 = strlen(WS_CONTENT_TYPE);
  long  text_type = False;

  if (bsize != NULL){
    *bsize = 0;
  }
  if (fl != NULL){
    *fl = False;
  }
  if (ctype != NULL){
    *ctype = NULL;
  }
  while (ptr != NULL) {
    char *parent_ptr = ptr;
    if (parent_ptr != line_ptr && *(--parent_ptr) == '\r') {
      *parent_ptr = '\0';
    }else{
      parent_ptr = NULL;
      *ptr = '\0';
    }
    if (*line_ptr == '\0') {
      if (parent_ptr == NULL) {
        *ptr = '\n';
      }else{
        *parent_ptr = '\r';
      }
      line_ptr = &ptr[1];
      break;
    }
    if (content_type == NULL && WSGFstrnccmp(line_ptr, WS_CONTENT_TYPE, len1) == 0) {
      content_type = WSGFstrdup(&line_ptr[len1]);
      if (content_type != NULL) {
        WSGFtrimSpace(content_type);
        if (ctype != NULL) {
          *ctype = WSGFstrdup(content_type);
        }
        if (WSGFstrnccmp(content_type, "text/", 5) == 0) {
          text_type = True;
          if (strcmp(&content_type[5], "html") == 0) {
            if (fl != NULL){
              *fl = True;
            }
          }
        }
      }
    }else if (content_length == -1 &&
           WSGFstrnccmp(line_ptr, WS_CONTENT_LEN, len2) == 0) {
      char *tmp_ptr = WSGFstrdup(&line_ptr[len2]);
      if (tmp_ptr != NULL) {
        WSGFtrimSpace(tmp_ptr);
        content_length = atoi(tmp_ptr);
        delete tmp_ptr;
      }
    }
    if (parent_ptr == NULL) {
      *ptr = '\n';
    }else{
      *parent_ptr = '\r';
    }
    line_ptr = &ptr[1];
    ptr = strchr(line_ptr, '\n');
  }
  if (content_type != NULL){
    delete content_type;
  }
  if (text_type){
    long buf_len = strlen(line_ptr);
    char *return_buf;

    if (content_length == (-1)) {
      content_length = buf_len;
      return_buf = (char*)calloc(buf_len+1, sizeof(char));
    }else{
      return_buf = (char*)calloc(content_length+1, sizeof(char));
    }

    if (return_buf == NULL) {
      WSMFtrace("Memory allocation failed.\n");
      return NULL;
    }
    if (buf_len <= content_length) {
      memcpy(return_buf, line_ptr, content_length);
    }else{
      while (buf_len > content_length) {
      if (*line_ptr == '\r' || *line_ptr == '\n') {
        line_ptr++;
        buf_len--;
      }else{
        break;
      }
    }
    memcpy(return_buf, line_ptr, content_length);
  }
  return_buf[content_length] = '\0';
  if (bsize != NULL) *bsize = (content_length+1);
    return return_buf;
  }else if (content_length != (-1)) {
    char *return_buf=(char*)calloc(content_length+1, sizeof(char));

    if (return_buf == NULL) {
       WSMFtrace("Memory allocation failed.\n");
       return NULL;
    }
    memcpy(return_buf, line_ptr, content_length);
    return_buf[content_length] = '\0';
    if (bsize != NULL) *bsize = (content_length+1);
    return return_buf;
  }
  return NULL;
}

#define MIN_READ_SIZE 0x100

long WSGFhttpRead(int sock,char** pbuffer,long* bsize){
  long  size = 0x400;
  long  len  = 0;
  char* buf = new char[size];

  memset(buf,0,sizeof(char)*size);
  if (bsize != NULL){
    *bsize = 0;
  }
  *pbuffer = NULL;
  if (buf == NULL) {
    WSMFtrace("Memory allocation failed.\n");
    return WS_MEM_ERR;
  }

  long  status = WS_NO_ERR;
  long  end_of_file = False;
  while (status == WS_NO_ERR && !end_of_file){
    long bytes_read;
    if (size - len < MIN_READ_SIZE) {
      size += 0x400;
      char* new_buf = new char[size];
      memcpy(new_buf,buf,size-0x400);
      delete buf;
      buf = new_buf;
    }
    bytes_read = so_read(sock, &buf[len], size-len-1);
    if (bytes_read <= 0) {
      if (bytes_read < 0 && (bytes_read == EX_NOTCONN || bytes_read == EX_CONNRESET ||
          bytes_read == EX_BADF)) {
        WSMFtrace("Network read error.\n");
        status = WS_READ;
      }else if (bytes_read < 0) {
        WSMFtrace("Network error.\n");
        status = WS_NET_ERR;
      }
      end_of_file = True;
    }else{
      len += bytes_read;
    }
  }

  if (status == WS_NO_ERR) {
    buf[len] = '\0';
    *pbuffer = buf;
    if (bsize != NULL){
      *bsize = (len+1);
    }
  }else{
    if (buf != NULL){
      delete buf;
    }
    WSMFtrace("HTTP: error encountered in receiving responses.\n");
  }
  return status;
}

static long _url_parse(char* url,char** protocol,char** host,long* port,char** path){
  char *ptr = strchr(url, ':');

  *protocol = *host = *path = 0;
  if (ptr == NULL){
    return WS_FORMAT_ERR;
  }

  *ptr = '\0';
  *protocol = WSGFstrdup(url);
  *ptr++ = ':';
  if (strncmp(ptr, "//", 2) == 0) {
    char *tmp_host=(&ptr[2]), *port_ptr;

    if ((ptr=strchr(tmp_host, '/')) == NULL) {
      *path = WSGFstrdup("");
    }else{
      *path = WSGFstrdup(ptr);
      *ptr = '\0';
    }
    if ((port_ptr=strchr(tmp_host, ':')) != NULL) {
      *port_ptr = '\0';
      *port = (long)atoi(&port_ptr[1]);
      *host = WSGFstrdup(tmp_host);
      *port_ptr = ':';
      if (*port <= 0) {
        return WS_FORMAT_ERR;
      }
    }else{
       *host = WSGFstrdup(tmp_host);
    }
    if (ptr != NULL) *ptr = '/';
  }else{
    *host = WSGFstrdup("localhost");
    *path = WSGFstrdup(ptr);
  }
  return WS_NO_ERR;
}

void _update_url_cache(char* url_name,char* rbuf,char* ctype, long rbuf_size, long fl){
  if (rbuf == NULL){
    rbuf = WSGFstrdup("");
  }
  if (ctype == NULL){
    ctype = WSGFstrdup("");
  }
  char *url_base_name = _get_url_name(url_name);
  WSCurlCache *url;

  _init_cache();
  if (url_base_name == NULL) return;
  for (url=_last_url_cache; url != NULL; url=url->parent) {
    if (strcmp(url_base_name, url->url_base_name) == 0) {
      break;
    }
  }
  if (url != NULL){
    _remove_cache(url);
    delete url;
  }else{
    if (_cur_url_cache >= _max_url_cache) {
      url = _first_url_cache;
      _remove_cache(url);
      if (url != NULL){
        delete url;
      }
    }
  }
  url = new WSCurlCache;
  if (url == NULL) {
    WSMFtrace("Memory Allocate error\n");
    delete url_base_name;
    return;
  }
  url->fl = fl;
  url->rbuf_size = rbuf_size;
  url->rbuf = WSGFstrdup(rbuf);
  url->content_type = WSGFstrdup(ctype);
  url->url_base_name = url_base_name;
  _add_cache(_last_url_cache, NULL, url);
}

long WSGFloadRemoteFile(char* url,char** pbuffer,char** ctype,
                         long* bsize,long* fl,long reload){
  char* buf = NULL;
  char* proxy_host = NULL;
  struct WSCurlCache *curl = NULL;

  WSGFhttpInit();
  if (bsize != NULL){
    *bsize = 0;
  }
  if (fl != NULL){
    *fl = False;
  }
  *pbuffer = NULL;

  if (!reload){
    curl = _find_url_cache(url, True);
  }
  if (curl != NULL && curl->rbuf_size > 0 && curl->rbuf != NULL) {
    *pbuffer = new char[curl->rbuf_size];
    memset(*pbuffer,0,curl->rbuf_size);
    if (*pbuffer == NULL){
      WSMFtrace("Memory allocate error!\n");
    }
    memcpy(*pbuffer, curl->rbuf, curl->rbuf_size);
    *bsize = curl->rbuf_size;
    if (ctype != NULL) {
      *ctype = WSGFstrdup(curl->content_type);
    }
    *fl = curl->fl;
    return True;
  }

  char* protocol = NULL;
  char* host = NULL;
  char* path = NULL;
  char  port_str[20];
  port_str[0] = '\0';
  long  port = 0;
  long  proxy_port = 0;
  long status = _url_parse(url, &protocol, &host, &port, &path);
  if (status != WS_NO_ERR) {
    if (host != NULL){
      delete host;
    }
    if (protocol != NULL){
      delete protocol;
    }
    if (path != NULL){
      delete path;
    }
    WSMFtrace("Invalid URL format. <%s>\n", url);
    return False;
  }

  long  size = 0;
  int   sock = 0;
  if (WSGFstricmp(protocol, "http") == 0) {
    long len=strlen(path);
    if (path[len-1] == '/') {
      char *work = new char[len+strlen(_default_file)+1];
      memset(work,0, len+strlen(_default_file)+1);
      if (work == NULL) {
        WSMFtrace("Memory allocate error.\n");
        return False;
      }
      sprintf(work, "%s%s", path, _default_file);
      delete path;
      path = work;
    }
    if (port == 0){
      port = 80;
    }

    if (*_http_proxy == '\0') {
      sprintf(port_str, "%1d", port);
      status = WSGFhttpConnect(host, port, &sock);
    }else{
      proxy_host = _get_proxy_info(_http_proxy, 80, &proxy_port);
      sprintf(port_str, "%1d", proxy_port);
      if (proxy_host == NULL) {
        status = -1;
      }else{
        status = WSGFhttpConnect(proxy_host, proxy_port, &sock);
      }
    }
    if (status == WS_INTR) {
      WSMFtrace("HTTP: connection interrupted.\n");
    }else if (status < 0) {
      WSMFtrace("HTTP: unable to connect to remote host.\n");
      if (*_http_proxy == '\0') {
        WSMFtrace("Can not connect to %s%s%s.\n", host,
              port==80 ? "" : ":", port==80 ? "" : port_str);
      }else{
        WSMFtrace("Can not connect to %s%s%s.\n", proxy_host,
                  proxy_port==80 ? "" : ":", proxy_port==80 ? "" : port_str);
      }
    }else if (status == WS_NO_ERR){
      if (WSGFhttpWrite(sock, (*_http_proxy=='\0' ? path : url)) == WS_NO_ERR) {
        if ((status=WSGFhttpRead(sock, &buf, &size)) ==
                WS_NO_ERR && buf != NULL && *buf != '\0') {
               *pbuffer = _http_extxt(buf, bsize, fl,ctype);
          delete buf;
        }else if (status == WS_INTR) {
          WSMFtrace("HTTP: connection longerrupted.\n");
        }else if (buf == NULL || *buf == '\0') {
          WSMFtrace( "HTTP: fail to retrieve file from the server.\n");
          WSMFtrace( "Can not get %s.\n", url);
        }else{
          WSMFtrace( "HTTP: network error occurred when retrieving file.\n");
          if (*_http_proxy == '\0') {
            WSMFtrace("Network error while talking to %s%s%s.\n",
                   host, port==80 ? "" : ":", port==80 ? "" : port_str);
          }else{
            WSMFtrace("Network error while talking to %s%s%s.\n",
                   proxy_host, proxy_port==80 ? "" : ":",
                   proxy_port==80 ? "" : port_str);
          }
        }
      }
      so_close(sock);
    }else{
      WSMFtrace("HTTP: unable to connect to remote host.\n");
      if (*_http_proxy == '\0') {
        WSMFtrace("Can not connect to %s%s%s.\n", host,
               port==80 ? "" : ":", port==80 ? "" : port_str);
      }else{
        WSMFtrace("Can not connect to %s%s%s.\n", proxy_host,
                proxy_port==80 ? "" : ":", proxy_port==80 ? "" : port_str);
      }
    }
    if (status == WS_NO_ERR) {
      _update_url_cache(url, *pbuffer,
      ctype==(char**)NULL ? (char*)NULL : *ctype,*bsize, *fl);
    }
  }else if (WSGFstricmp(protocol, "ftp") == 0){
    WSMFtrace("Do not support the ftp protocol.\n");
  }else{
    WSMFtrace("Do not support the '%s' protocol.\n", url);
  }
  if (host != NULL){
    delete host;
  }
  if (path != NULL){
    delete path;
  }
  if (protocol != NULL){
    delete protocol;
  }
  if (proxy_host != NULL){
    delete proxy_host;
  }
  return True;
}


