/*
 * filesystem.c:
 *
 * Copyright (c) 2000 James McKenzie <james@fishsoup.dhs.org>,
 * All rights reserved.
 *
 */

static char rcsid[] =
    "$Id: http.c,v 1.14 2002/06/23 17:16:39 root Exp root $";

/*
 * $Log: http.c,v $
 * Revision 1.14  2002/06/23 17:16:39  root
 * #
 *
 * Revision 1.13  2001/10/12 13:51:11  root
 * #
 *
 * Revision 1.12  2001/10/04 18:00:36  root
 * #
 *
 * Revision 1.11  2001/01/30 06:16:18  root
 * #
 *
 * Revision 1.10  2000/12/01 04:42:10  root
 * #
 *
 * Revision 1.9  2000/12/01 04:13:42  root
 * #
 *
 * Revision 1.8  2000/12/01 01:53:01  root
 * *** empty log message ***
 *
 * Revision 1.7  2000/11/23 23:45:19  root
 * #
 *
 * Revision 1.6  2000/10/07 18:30:03  root
 * #
 *
 * Revision 1.5  2000/10/06 19:52:18  root
 * #
 *
 * Revision 1.4  2000/09/24 01:58:21  root
 * #
 *
 * Revision 1.3  2000/09/24 01:48:12  root
 * #
 *
 * Revision 1.2  2000/09/24 01:44:47  root
 * #
 *
 * Revision 1.1  2000/09/24 01:12:43  root
 * #
 *
 * Revision 1.2  2000/09/19 05:06:22  root
 * #
 *
 * Revision 1.1  2000/09/19 03:53:27  root
 * #
 *
 */


#include "project.h"
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/time.h>


BEGIN_OBJECT(Tps)
char *left;
char *right;
END_OBJECT(Tps)
static void tps_destructor(Tps);

CREATOR(Tps) = (Creator(Tps)) Obj_DefaultCreator;
DESTRUCTOR(Tps) = tps_destructor;


BEGIN_OBJECT(Uri)
char *uri;
char *method;
char *password;
char *user;
char *auth;
char *host;
char *port;
char *path;
char *proxyhost;
char *proxyport;
END_OBJECT(Uri)
static void uri_destructor(Uri);

CREATOR(Uri) = (Creator(Uri)) Obj_DefaultCreator;
DESTRUCTOR(Uri) = uri_destructor;


BEGIN_OBJECT(Collection_ht) COLLECTION_FILLER;
char *base;                     //URL to base
END_OBJECT(Collection_ht)
static void ht_destructor(Collection_ht);

CREATOR(Collection_ht) = (Creator(Collection_ht)) Obj_DefaultCreator;
DESTRUCTOR(Collection_ht) = ht_destructor;

BEGIN_OBJECT(CFH_ht) CFH_FILLER;
int fd;
off_t offset;
off_t length;
int iseof;

Uri u;
char *path;                     //Absolute URL
char *rel;                      //Relative path to file
Eweid e;
Collection_ht c;                //My mummy

END_OBJECT(CFH_ht)

static void fh_destructor(CFH_ht);

CREATOR(CFH_ht) = (Creator(CFH_ht)) Obj_DefaultCreator;
DESTRUCTOR(CFH_ht) = fh_destructor;

static char collection_id[] = "collection.id";
static char collection_mtd[] = "collection.mtd";

static void fh_destructor(CFH_ht c)
{

  if (c->fd > 0)
    close(c->fd);
  if (c->u) {
    //printf("closing %s\n",c->u->path);
    Obj_Delete(c->u);
  }
  if (c->path)
    free(c->path);
  if (c->rel)
    free(c->rel);
  if (c->e)
    Obj_Delete(c->e);

  free(c);
}


static void uri_destructor(Uri u)
{
  if (!u)
    return;
  if (u->uri)
    free(u->uri);
  if (u->method)
    free(u->method);
  if (u->user)
    free(u->user);
  if (u->password)
    free(u->password);
  if (u->auth)
    free(u->auth);
  if (u->host)
    free(u->host);
  if (u->port)
    free(u->port);
  if (u->path)
    free(u->path);
  if (u->proxyport)
    free(u->proxyport);
  if (u->proxyhost)
    free(u->proxyhost);


  free(u);
}

static char *readtonl(int fd)
{
  char buf[4096], *ptr = buf;


  while (1) {
    if (read(fd, ptr, 1) != 1) {
      perror("http: read");
      return NULL;
    }
    if (*ptr == '\n') {
      ptr++;
      *ptr = 0;
      return strdup(buf);
    }
    ptr++;
  }

}

#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))

static char *base64_encode(const char *in)
{
  /* Conversion table.  */
  static const unsigned char tbl[64] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3',
    '4', '5', '6', '7', '8', '9', '+', '/'
  };
  int i;
  int length = strlen(in);
  char *s = (char *) malloc(length + 5);
  char *q = s;


  unsigned char *ret = (unsigned char *) malloc(1 + BASE64_LENGTH(length));
  unsigned char *p = ret;

  bzero(s, length + 5);
  strcpy(s, in);

  /* Transform the 3x8 bits to 4x6 bits, as required by base64.  */
  for (i = 0; i < length; i += 3) {
    *p++ = tbl[s[0] >> 2];
    *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
    *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
    *p++ = tbl[s[2] & 0x3f];
    s += 3;
  }
  /* Pad the result if necessary...  */
  if (i == length + 1)
    *(p - 1) = '=';
  else if (i == length + 2)
    *(p - 1) = *(p - 2) = '=';
  /* ...and zero-terminate it.  */
  *p = '\0';

  free(q);
  return ret;
}

unsigned char *percent_encode(unsigned char *in)
{
  unsigned char *ret = malloc(1 + (strlen(in) * 3));
  unsigned char *ptr = ret;

  while (*in) {

    if (isdigit(*in) || isalpha(*in)) {
      *(ptr++) = *in;
    } else {
      switch (*in) {
      case '/':
      case '-':
      case ':':
      case '.':
        *(ptr++) = *in;
        break;
      default:


        *(ptr++) = '%';
        ptr += sprintf(ptr, "%02x", *in);

      }
    }
    in++;
  }
  *ptr = 0;

  return ret;
}

static Uri encode64(Uri u)
{


  if (u->auth) {
    char *ptr = u->auth;
    u->auth = base64_encode(ptr);
    free(ptr);
  }

  if (u->path) {
    char *ptr = u->path;
    u->path = percent_encode(ptr);
    free(ptr);
  }
  if (u->uri)
    free(u->uri);

  {
    char *method = u->method;
    char *port = u->port;

    if (!port)
      port = "80";
    if (!method)
      method = "http";

    u->uri =
        malloc(strlen(u->host) + strlen(port) + strlen(method) +
               strlen(u->path) + 10);

    sprintf(u->uri, "%s://%s:%s%s", method, u->host, port, u->path);

  }


  return u;
}

static void uri_uphp(char *in, Uri uri)
{
  char *buf = strdup(in);
  char *ptr, *p2;

  ptr = index(buf, '@');

  if (ptr) {
    *ptr = 0;

    uri->auth = strdup(buf);

    p2 = index(buf, ':');

    if (p2) {
      *p2 = 0;
      uri->password = strdup(p2 + 1);
    }

    uri->user = strdup(buf);
    ptr++;

  } else {
    ptr = buf;
  }


  p2 = index(ptr, ':');
  if (p2) {
    *p2 = 0;
    uri->port = strdup(p2 + 1);
  }

  uri->host = strdup(ptr);


}

static Uri parse_proxy(char *path)
{
  Uri uri = CREATE_OBJ(Uri);
  char *buf;
  char *ptr, *p2;
  int i = strlen(path);

  uri->uri = strdup(path);

  i += 10;

  buf = (char *) malloc(i);
  bzero(buf, i);

  ptr = buf;

  bcopy(path, buf, strlen(path));


  while (*ptr) {
    if ((*ptr == '/') || (*ptr == ':'))
      break;
    ptr++;
  }

  if (!*ptr) {
    Obj_Delete(uri);
    free(buf);
    return NULL;
  }

/*www.fish.soup/foo/bar */
  if (*ptr == '/') {
    uri->method = strdup("http");
    *ptr = 0;
    uri->host = strdup(buf);
    *ptr = '/';
    uri->path = strdup(ptr);
    free(buf);

    return encode64(uri);
  }

/*http://user:pass@host:port/foo/bar */
/*user:pass@host:port/foo/bar */
/*host:port/foo/bar */


/*http://user:pass@host:port/foo/bar */
  if ((*(ptr + 1) == '/') && (*(ptr + 2) == '/')) {
    *ptr = 0;
    uri->method = strdup(buf);
    ptr += 3;
  } else {
    ptr = buf;
  }

  p2 = index(ptr, '/');
  if (p2)
    *p2 = 0;
  uri_uphp(ptr, uri);

  if (p2) {
    *p2 = '/';
    uri->path = strdup(p2);
  } else {
    uri->path = strdup("/");
  }

  free(buf);
  return encode64(uri);
}


static Uri parse_uri(char *path)
{
  Uri uri = CREATE_OBJ(Uri);
  char *buf;
  char *ptr, *p2;
  int i = strlen(path);


  {
    char *proxy = getenv("http_proxy");
    Uri puri;

    if (proxy) {
      puri = parse_proxy(proxy);
      if (puri) {
        uri->proxyhost = strdup(puri->host);
        uri->proxyport = strdup(puri->port);
        Obj_Delete(puri);
      }
    }

  }

  i += 10;

  buf = (char *) malloc(i);
  bzero(buf, i);

  ptr = buf;

  bcopy(path, buf, strlen(path));


  while (*ptr) {
    if ((*ptr == '/') || (*ptr == ':'))
      break;
    ptr++;
  }

  if (!*ptr) {
    Obj_Delete(uri);
    free(buf);
    return NULL;
  }

/*www.fish.soup/foo/bar */
  if (*ptr == '/') {
    uri->method = strdup("http");
    *ptr = 0;
    uri->host = strdup(buf);
    *ptr = '/';
    uri->path = strdup(ptr);
    free(buf);

    return encode64(uri);
  }

/*http://user:pass@host:port/foo/bar */
/*user:pass@host:port/foo/bar */
/*host:port/foo/bar */


/*http://user:pass@host:port/foo/bar */
  if ((*(ptr + 1) == '/') && (*(ptr + 2) == '/')) {
    *ptr = 0;
    uri->method = strdup(buf);
    ptr += 3;
  } else {
    ptr = buf;
  }

  p2 = index(ptr, '/');
  if (p2)
    *p2 = 0;
  uri_uphp(ptr, uri);

  if (p2) {
    *p2 = '/';
    uri->path = strdup(p2);
  } else {
    uri->path = strdup("/");
  }


  free(buf);
  return encode64(uri);
}



static void tps_destructor(Tps tps)
{
  if (!tps)
    return;
  if (tps->left)
    free(tps->left);
  if (tps->right)
    free(tps->right);
  free(tps);
}

static Tps make_tps(char *left, char *right)
{
  Tps tps = CREATE_OBJ(Tps);

  tps->left = strdup(left);
  tps->right = strdup(right);

  return tps;
}

static int send_tps(int fd, Tps t)
{
  int s;


  s = strlen(t->left);
  if (write(fd, t->left, s) != s)
    return 0;
  if (write(fd, ": ", 2) != 2)
    return 0;
  s = strlen(t->right);
  if (write(fd, t->right, s) != s)
    return 0;
  if (write(fd, "\r\n", 2) != 2)
    return 0;

  return 1;
}

static int send_tps_list(int fd, Tps_list l)
{
  int s;
  Tps t = l->head;

  while (t) {
    if (!send_tps(fd, t))
      return 0;
    t = t->next;
  }

  return 1;
}


static Tps parse_tps(char *in)
{
  char *buf = strdup(in);
  char *ptr = index(buf, ':');
  Tps ret = CREATE_OBJ(Tps);

  if (!ptr) {
    Obj_Delete(ret);
    free(buf);
    return NULL;
  }

  *ptr = 0;

  ret->left = strdup(buf);
  ptr++;
  if (*ptr == ' ')
    ptr++;
  ret->right = strdup(ptr);

  free(buf);

  return ret;
}



static int isblankline(char *i)
{
  if (!i)
    return 0;

  if (!*i)
    return 1;
  if (!strcmp(i, "\n"))
    return 1;
  if (!strcmp(i, "\r\n"))
    return 1;

  return 0;
}

static void stripeol(char *i)
{
  char *sptr = i, *dptr = i;

  if (!i)
    return;
  while (*sptr) {
    if (*sptr == '\r')
      sptr++;
    if (*sptr == '\n')
      sptr++;
    *(dptr++) = *(sptr);
    if (*sptr)
      sptr++;
  }


}

static Tps_list read_tps_list(int fd)
{
  char *a;
  Tps t;
  Tps_list l = CREATE_LIST(Tps);

  while (1) {
    a = readtonl(fd);
    if (!a)
      return l;
    if (isblankline(a)) {
      free(a);
      return l;
    }
    stripeol(a);
    t = parse_tps(a);
    if (t) {
      Obj_Insert(l, t, ObjInsertTail);
    }
    free(a);
  }


}


static Tps_list make_tps_from_uri(Uri u)
{
  Tps_list out;

  out = CREATE_LIST(Tps);

  if (u->auth) {
    char *ptr = (char *) malloc(strlen(u->auth) + 10);
    sprintf(ptr, "Basic %s", u->auth);

    Obj_Insert(out, make_tps("Authorization", ptr), ObjInsertTail);
//    printf("AUTH: %s\n", ptr);
    free(ptr);
  }
#if 0
  Obj_Insert(out, make_tps("Connection", "Keep-Alive"), ObjInsertTail);

  if (u->proxyhost) {
    Obj_Insert(out, make_tps("Proxy-Connection", "Keep-Alive"),
               ObjInsertTail);
  }
#endif

  Obj_Insert(out, make_tps("Host", u->host), ObjInsertTail);

  return (out);
}

static Tps find_tps(Tps_list l, char *a)
{
  Tps t = l->head;

  while (t) {
    if (!strcmp(t->left, a)) {
      return t;
    }
    t = t->next;
  }

  return t;

}


static int set_nonblock(int fd)
{
  long arg;

  arg = fcntl(fd, F_GETFL, 0);

  if (arg == -1) {
    //dolog ("fcntl(%d,F_GETFL,%ld) %m", fd, arg);
    return -1;
  }

  arg |= O_NONBLOCK;
  if (fcntl(fd, F_SETFL, arg) == -1) {
    //dolog ("fcntl(%d,F_SETFL,%ld) %m", fd, arg);
    return -1;
  }

  return 0;
}


static int clr_nonblock(int fd)
{
  long arg;

  arg = fcntl(fd, F_GETFL, 0);

  if (arg == -1) {
    //dolog ("fcntl(%d,F_GETFL,%ld) %m", fd, arg);
    return -1;
  }

  arg &= ~O_NONBLOCK;
  if (fcntl(fd, F_SETFL, arg) == -1) {
    //dolog ("fcntl(%d,F_SETFL,%ld) %m", fd, arg);
    return -1;
  }

  return 0;
}

static int open_fd_to_host(Uri u)
{
  int fd;
  int herrno;
  struct hostent *result;
  char buf[1024];
#ifndef SVR4
  struct hostent rbuf[4];
#endif
  char *host;
  int port;
  fd_set writefds;
  struct timeval tv;
  int error, elen;

  struct sockaddr_in sin;


  if (u->proxyhost) {
    host = u->proxyhost;
    if (u->proxyport) {
      port = atoi(u->proxyport);
    } else {
      port = 80;
    }
  } else {
    host = u->host;
    if (u->port) {
      port = atoi(u->port);
    } else {
      port = 80;
    }
  }


  fd = socket(AF_INET, SOCK_STREAM, 0);

  if (fd == -1)
    return fd;

#ifdef SVR4
  gethostbyname_r(host, &result, buf, sizeof(buf), &herrno)
#else
  gethostbyname_r(host, rbuf, buf, sizeof(buf), &result, &herrno);
#endif

  if (!result) {
    close(fd);
    return -1;
  }

  sin.sin_family = AF_INET;

  bcopy((char *) result->h_addr, (char *) &sin.sin_addr, result->h_length);


  sin.sin_port = htons(port);

  set_nonblock(fd);

  if (!connect(fd, (struct sockaddr *) &sin, sizeof(sin))) {
    clr_nonblock(fd);
    return fd;
  }


  if (errno != EINPROGRESS) {
    close(fd);
    return -1;
  }

  tv.tv_sec = 10;
  tv.tv_usec = 0;

  FD_ZERO(&writefds);
  FD_SET(fd, &writefds);

  if (!select(fd + 1, NULL, &writefds, NULL, &tv)) {
    close(fd);
    return -1;
  }


  elen = sizeof(error);

  getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &elen);

  if (elen != sizeof(error)) {
    close(fd);
    return -1;
  }



  if (error) {
    close(fd);
    return -1;
  }


  clr_nonblock(fd);

  return fd;

}



static Tps_list do_request(int fd, char *req, Tps_list in, char **out)
{
  int s;
  Tps_list ret = NULL;
  Tps t;

  *out = NULL;

  s = strlen(req);
  if (write(fd, req, s) != s)
    return ret;
  if (write(fd, "\r\n", 2) != 2)
    return ret;

  if (!send_tps_list(fd, in))
    return ret;

/*Blank line at end of header*/
  if (write(fd, "\r\n", 2) != 2)
    return ret;

  *out = readtonl(fd);

  if (!*out)
    return ret;

  stripeol(*out);

  ret = read_tps_list(fd);

  return ret;
}

static char *uri_where(Uri u)
{
  return u->proxyhost ? u->uri : u->path;
}

static int fh_do_open(CFH_ht fh)
{
  Tps_list out, resp;
  char buf[1024];
  char *in;
  Tps t;
  int length;

  fh->fd = open_fd_to_host(fh->u);

  if (fh->fd < 0)
    return -1;

  out = make_tps_from_uri(fh->u);


  fprintf(stderr, "http: %s: %s ", fh->u->host, fh->u->path);

  if (fh->u->proxyhost) {
    fprintf(stderr, "(proxy: %s) ", fh->u->proxyhost);
  }

  fflush(stdout);
  if (fh->length == -1) {
    fprintf(stderr, "HEAD ");
    fflush(stdout);

    sprintf(buf, "HEAD %s HTTP/1.1", uri_where(fh->u));

    in = NULL;
    resp = do_request(fh->fd, buf, out, &in);


    if ((!in) || (!resp)) {
      Obj_Delete(out);
      if (resp)
        Obj_Delete(resp);
      if (in)
        free(in);
      close(fh->fd);
      fprintf(stderr, "ERR\n");
      return -1;
    }
    {
      char *ptr;
      int code;

      ptr = index(in, ' ');
      if (!ptr) {
        Obj_Delete(resp);
        Obj_Delete(out);
        close(fh->fd);
        free(in);
        fprintf(stderr, "ERR\n");
        return -1;
      }

      code = atoi(ptr + 1);

      switch (code) {
      case 200:
        break;
      default:
        Obj_Delete(out);
        Obj_Delete(resp);
        close(fh->fd);
        free(in);
        fprintf(stderr, "Code %d\n", code);
        return -1;

      }

    }
    free(in);


    t = find_tps(resp, "Content-Length");
    if (!t) {
      Obj_Delete(out);
      Obj_Delete(resp);
      close(fh->fd);
      fprintf(stderr, "No len\n");
      return -1;
    }


    fh->length = atoi(t->right);


    Obj_Delete(resp);

  }
  fprintf(stderr, "[%d] ", (int) fh->length);


  //printf("Opening %s at %d\n",fh->u->path,fh->offset);

  if (fh->offset) {
    sprintf(buf, "bytes=%ld-", (long) fh->offset);
    Obj_Insert(out, make_tps("Range", buf), ObjInsertTail);
  }
//Seems proxys can't keepalive dammit...
  if (fh->u->proxyhost) {
    close(fh->fd);
    fh->fd = open_fd_to_host(fh->u);
  }

  fprintf(stderr, "GET (%d) ", (int) fh->offset);
  fflush(stdout);
  sprintf(buf, "GET %s HTTP/1.1", uri_where(fh->u));

  in = NULL;
  resp = do_request(fh->fd, buf, out, &in);

  Obj_Delete(out);

  if ((!resp) || (!in)) {
    if (resp)
      Obj_Delete(resp);
    if (in)
      free(in);
    close(fh->fd);
    fprintf(stderr, "ERR\n");
    return -1;
  }
  {
    char *ptr;
    int code;

    ptr = index(in, ' ');
    if (!ptr) {
      Obj_Delete(resp);
      close(fh->fd);
      free(in);
      fprintf(stderr, "ERR\n");
      return -1;
    }

    code = atoi(ptr + 1);

    switch (code) {
    case 200:
    case 206:
      break;
    default:
      Obj_Delete(resp);
      close(fh->fd);
      free(in);
      fprintf(stderr, "Code %d\n", code);
      return -1;

    }

  }


  fprintf(stderr, "Done\n");


  return 0;
}


static off_t fs_tell(CFH cfh)
{
  CFH_ht fh = (CFH_ht) cfh;
  return fh->offset;
}

#ifdef DEBUG
static int coredump = 0;
#endif


static off_t fs_seek(CFH cfh, off_t off, int whence)
{
  CFH_ht fh = (CFH_ht) cfh;

  off_t new, delta;

  switch (whence) {
  case SEEK_SET:
    new = off;
    break;
  case SEEK_CUR:
    new = fh->offset + off;
    break;
  case SEEK_END:
    new = fh->length - off;
    break;
  default:
    return -1;
  }

  if (new > fh->length)
    new = fh->length;
  if (new < 0)
    new = 0;

  delta = new - fh->offset;

//  printf("Seek %p from %d to %d delta %d\n",fh ,fh->offset,new,delta);

  if (delta == 0) {
// printf("Null seek\n");
    return new;
  }

  if ((delta > 0) && (delta < 20480)) {
    char buf[1024];
// printf("Ate it\n");

    while (delta) {
      delta -=
          read(fh->fd, buf, delta > sizeof(buf) ? sizeof(buf) : delta);
    }

    fh->offset = new;
    return fh->offset;
  }
//  printf("Opening new channel\n");

  close(fh->fd);
  fh->offset = new;
  fh_do_open(fh);

  return fh->offset;
}

static int fs_read(CFH cfh, void *d, int s)
{
  CFH_ht fh = (CFH_ht) cfh;
  int i, t;
  off_t j;




  j = fh->offset + s;
  if (j > fh->length) {
    j -= fh->length;
    s -= (int) j;

    if (s <= 0)
      return 0;
  }
  //printf("Read %p %d at %d/%d ",fh,s,fh->offset,fh->length);


  t = 0;
  while (s) {
    i = read(fh->fd, d, s);
    if (i > 0) {
      fh->offset += i;
      s -= i;
      t += i;
      d += i;
    } else {
      //printf("Returns early??? %d\n",t);

      return t;
    }
  }

  //printf("Returns %d\n",t);
  return t;

}


static int fs_tread(CFH cfh, void *d, int s)
{
  CFH_ht fh = (CFH_ht) cfh;
  int i;
  off_t j;




  j = fh->offset + s;
  if (j > fh->length) {
    j -= fh->length;
    s -= (int) j;

    if (s <= 0)
      return 0;
  }
  //printf("Read %p %d at %d/%d ",fh,s,fh->offset,fh->length);


  i = read(fh->fd, d, s);
  if (i > 0) {
    fh->offset += i;
  }

  return i;

}


static int fs_write(CFH cfh, void *d, int s)
{
  return 0;
}

static int fs_get(CFH cfh)
{
  CFH_ht fh = (CFH_ht) cfh;
  unsigned char c;

  if (fs_read(cfh, &c, 1) != 1)
    return -1;

  return (int) c;
}

static int fs_put(int c, CFH cfh)
{
  return -1;
}


static int fs_eof(CFH cfh)
{
  CFH_ht fh = (CFH_ht) cfh;

  return fh->offset == fh->length;
}

static off_t fs_size(CFH cfh)
{
  CFH_ht fh = (CFH_ht) cfh;

  return fh->length;
}

static void fs_close(CFH cfh)
{
  Obj_Delete(cfh);
}

static int fs_canread(CFH cfh)
{
  CFH_ht fh = (CFH_ht) cfh;
  return canread(fh->fd);
}
static void fs_dispatch(CFH cfh)
{
}
static int fs_status(CFH cfh)
{
  return 100;
}

static CFH fs_open(Collection rc, Eweid e, int mode)
{
  Collection_ht c = (Collection_ht) rc;
  CFH_ht fh;
  char *path;
  char *buf;
  FILE *f;
  Uri u;

  path = eweid_to_filename(e);

  if (!path)
    return NULL;

  //printf("opening: %s\n",path);

  buf = malloc(strlen(path) + strlen(c->base) + 1);
  strcpy(buf, c->base);
  strcat(buf, path);

  u = parse_uri(buf);

  if (!u) {
    free(buf);
    return (NULL);
  }



  fh = CREATE_OBJ(CFH_ht);
  fh->e = eweid_dup(e);
  fh->path = buf;
  fh->rel = path;
  fh->c = c;
  fh->u = u;



  fh->tell = fs_tell;
  fh->seek = fs_seek;
  fh->read = fs_read;
  fh->tread = fs_tread;
  fh->write = fs_write;
  fh->get = fs_get;
  fh->put = fs_put;
  fh->eof = fs_eof;
  fh->size = fs_size;
  fh->close = fs_close;
  fh->canread = fs_canread;
  fh->dispatch = fs_dispatch;
  fh->status = fs_status;


  fh->length = -1;
  fh->offset = 0;

  if (fh_do_open(fh)) {
    Obj_Delete(fh);
    return NULL;
  }



  return buffer_cfh((CFH) fh);

}


static CFH_ht p_fh;
static int readfn(char *buf, int len)
{

  return fs_read((CFH) p_fh, buf, len);
}

EXTERNAL Collection files_http_collection(char *base)
{
  char *buf, *ptr;
  Eweid e;
  FILE *f;
  int i;
  Uri u;
  CFH_ht fh;

  Collection_ht c;


  buf = malloc(strlen(base) + sizeof(collection_id));
  strcpy(buf, base);
  strcat(buf, collection_id);

  u = parse_uri(buf);
  free(buf);

  if (!u) {
    return NULL;
  }

  fh = CREATE_OBJ(CFH_ht);

  fh->u = u;
  fh->length = -1;
  fh->offset = 0;

  if (fh_do_open(fh)) {
    Obj_Delete(fh);
    return NULL;
  }

  e = CREATE_OBJ(Eweid);
  if (read(fh->fd, e->e, 4) != 4) {
    Obj_Delete(fh);
    return NULL;
  }

  Obj_Delete(fh);

  buf = malloc(strlen(base) + strlen(collection_mtd) + 1);
  strcpy(buf, base);
  strcat(buf, collection_mtd);

  u = parse_uri(buf);
  free(buf);

  if (!u) {
    Obj_Delete(e);
    return NULL;
  }

  fh = CREATE_OBJ(CFH_ht);

  fh->u = u;
  fh->length = -1;
  fh->offset = 0;

  if (fh_do_open(fh)) {
    Obj_Delete(e);
    Obj_Delete(fh);
    return NULL;
  }


  p_fh = fh;

  i = do_parse_mtd(readfn);

  p_fh = NULL;

  Obj_Delete(fh);

  if (i) {
    Obj_Delete(e);
    return NULL;
  }

  c = CREATE_OBJ(Collection_ht);

  c->name = "http";
  c->e = e;
  c->open = fs_open;
  c->base = strdup(base);

  //printf("Added %s\n",c->base);

  return (Collection) c;


}


static void ht_destructor(Collection_ht c)
{

  if (!c)
    return;
  if (c->base)
    free(c->base);
}
