
#ifdef __XCODE__
#include "xcode_quiet.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>




#ifdef WIN32
#include <process.h>
#include <io.h>
#endif 
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>



#include "lib.h"
#include "xml.h"
#include "tcplib.h"
#include "lprintf.h"
#include "qpair.h"
#include "wio.h"
#include "debug.h"
#include "emsg.h"
#include "thread.h"
#include "file64.h"
#include "base64.h"

#ifdef WIN32
#define vsnprintf _vsnprintf
#endif

int wio_read_content(Webio *l,char *bf);
void wio_decode_content(Webio *l, char *bf, char *tempfile);
int wio_growin(Webio *l, int max);
int wio_readmore(Webio *l, int msec);
int wio_reset(Webio *l);
void wio_unescape_url_bin(char *url, int max, int *rlen);

int wio_process(Webio *l);
USER_AGENT wio_identify_agent(char *agent_string);

int wio_free(Webio *w);
int wio_request(Webio *w, char *bf);

static char *months[]={
        "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
};
static char *weekday[]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", };
char *wio_cookie_date(char *bf, int tplus)
{
		time_t now;
        struct tm *t;
		struct tm atm;

        time(&now);
		now += tplus;
        t = safe_localtime(&now,&atm);
        lprintf(bf,70-1,"%s, %d-%s-%d %02d:%02d:%02d GMT",
                weekday[t->tm_wday],
                t->tm_mday,months[t->tm_mon],t->tm_year+1900,
                t->tm_hour,t->tm_min,t->tm_sec);
        return bf;
}

void wio_add_header(Webio *l, char *header, char *value) 
{
	pair_add_allowdup(&l->headers,header,value);
	/* changed from pair_add to pair_add_allowdup so that multiple cookies can be set. */

}

int wio_cookie_set(Webio *w, char *var, char *val)
{
	char bf[BFSZ];

	char tmp[BFSZ];
	lprintf(bf,BFSZ-1,"%s=%s; expires=%s",var,val,wio_cookie_date(tmp,60*60*24*190));
	wio_add_header(w,"Set-Cookie",bf);
	return TRUE;
}
int wio_printf(Webio *l, char * arg_list, ...)
{
    va_list arg_ptr;
    char *format;
    int len;
    char output[5000];
    va_start(arg_ptr, arg_list);
    format = arg_list;
    output[0] = 0;
    len = vsnprintf(output,4999, format, arg_ptr);
	if (len == -1 || len >= 4999) output[len=4999] = '\0';
	tcp_buffer_add(l->c,output,len);
    return TRUE;
}
int wio_printf_flush(Webio *l)
{
	tcp_buffer_flush(l->c);
	return TRUE;
}
int wio_printf_done(Webio *l)
{
	return wio_printf_flush(l);
}


int wio_handle_request(Chan *c, int (*fn)(Webio *w, void *data),void *data, int isproxy)
{
	int tout=30*1000;
	Webio *w = calloc(1,sizeof(Webio));
	w->c = c;
	w->fn = fn;
	w->fn_data = data;
	w->isproxy = isproxy;
	
	for (;TRUE;) {
		if (w->c==NULL) break;
		if (!wio_readmore(w,tout)) break;
		if (w->c==NULL) break;
		if (!wio_process(w)) continue; // waiting for more data / end of packet...
		if (w->c==NULL) break;
		if (w->httpversion==0) if (!w->keepalive) w->closing = TRUE;
		//break; // required or it waits, must have broken something from surgemail code...
		// If we have a 1.0 connection then lets break every time... 
		if (w->httpversion==0) break;
		if (w->closing) break;
	}
//	imsg("wio_handle_request Closing version %d closing %d keepalive %d \n",w->httpversion,w->closing,w->keepalive);
	

	wio_free(w);
	return TRUE;
}
#define iffree(a) if (a) free(a); a = NULL
char *wio_input_attachment(Webio *l, char *var)
{
	return pair_find(l->attachments,var);
}
char *wio_var_find(Webio *l, char *var)
{
	return qpair_find(l->vlist,var);
}


int wio_free(Webio *w)
{
	imsg("wio_free called for %p\n",w);
	qpair_free(&w->vlist);
	pair_free(&w->head);
	pair_free(&w->form);
	pair_free(&w->headers);
	pair_free(&w->cookie_list);
	pair_free(&w->attachments);
	tcp_free(&w->c); w->c = NULL;
	iffree(w->inbf); 
	iffree(w->ref); 
	iffree(w->cgi); 
	iffree(w->original_request); 
	iffree(w->mod); 
	iffree(w->cookie); 
	iffree(w->user); 
	iffree(w->user_groups); 
	iffree(w->content_store); 
	xml_free(w->xml); w->xml = NULL; 
	free(w);
	return TRUE;
}
int wio_growin(Webio *l, int max)
{
	int want;
	if (l->inmax>=max) return FALSE;
	if (l->inmax==0) want = 1000; else want = 32000;
	l->inbf = realloc(l->inbf,want);
	l->inmax = want;
	return TRUE;
}

int wio_readmore(Webio *l, int msec)
{
	int want = l->inmax - l->inlen - 1;
	int r;
	time_t idle;

	if (want<10) {
		wio_growin(l,132000); // just to temporarily fix a bug
		want = l->inmax - l->inlen - 1;
		if (want<10) {
			l->inlen = 0;
		}
	}
	idle = time(NULL);
	for (r=-2; r== -2; ) {
		r = tcp_read_timed(l->c,l->inbf+l->inlen,want,msec);
		if (r==-2) {
			if (msec<2) return TRUE; /* No more now */
		}
		if ((r<=0) && (r!=-2)) break; /* socket closed */
		if ((time(NULL)-idle) > (msec/1000)) {
			l->closing = TRUE;
			return FALSE;
		}
	}
	if (r<=0) return FALSE; /* Socket closed I guess */
	l->inlen += r;
	return TRUE;
}
int wio_reset(Webio *l)
{
	l->contentlen = 0;
	ncpy(l->contenttype,"",BFSZ-1);
	xml_free(l->xml); l->xml = NULL;
	pair_free(&l->query);
	pair_free(&l->form);
	pair_free(&l->headers);
	if (l->ref) free(l->ref); l->ref = NULL;
	if (l->cgi) free(l->cgi);l->cgi = NULL;
	l->chunked = FALSE;
	l->done_content = FALSE;

	return TRUE;
}

int wio_process(Webio *l)
{
	char *s;
	char *start;
	char *headers;
	int nrest;

	start = l->inbf;
	l->inbf[l->inlen] = 0;
	/* Search for double line feed and then process */
	s = strstr(start,"\r\n\r\n");
	if (s==NULL) {
		return FALSE; /* Waiting for more */
	}
	s += 4;
	s[-2] = 0;
	nrest = l->inlen - (s-start);

	headers = strdup(start);


	// now we can trash 'start' since we have coppied it... 
	tcp_insert_buffer(l->c,s,nrest);

//	

	wio_request(l,headers);
//	
	if (l->c==NULL) goto tidyup;
	wio_finish(l);
tidyup:
	free(headers);
	wio_reset(l); // reset everything we need to reset
	l->inlen = 0;		
	return TRUE;
}
int wio_finish(Webio *l)
{
	if (l->chunked) { // flush the chunked mode data... 
		char bf2[BFSZ]; 
		tcp_nodelay(l->c,TRUE);
		lprintf(bf2,BFSZ-1,"0\r\n\r\n");
		tcp_write_timed(l->c,bf2,strlen(bf2),1000*60*10);
	}
	tcp_buffer_flush(l->c);
	return TRUE;
}
static int webdav_enabled = FALSE;
int wio_set_webdav(int x)
{	
	webdav_enabled = x;
	return TRUE;
}
int wmime_attach_close(Webio *l, FILE **attach, char *name, char *fname)
{
	if (*attach!=NULL) {
		fclose(*attach);
		*attach = NULL;
//		pair_add(&l->form,name,fname); // Remove this line at some point. Anything that currently relise on it probably has a security hole.
		imsg("Adding attachments name (%s) (%s) \n",name,fname);
		pair_add(&l->attachments,name,fname);
	}
	strcpy(fname,"");
	return TRUE;
}
#define xisspace(x) ((x==' ') || (x=='\t'))
int wio_input_add(Webio *l, char *var, char *val)
{
	if (!l->form) pair_add(&l->query,var,val);
	else pair_add(&l->form,var,val);
	return TRUE;
}
void wio_getword(char *word, char *line, char stop)
{
    int x = 0,y;

    for(x=0;((line[x]) && (line[x] != stop));x++)
        word[x] = line[x];

    word[x] = '\0';
    if(line[x]) ++x;
    y=0;

    while(line[y++] = line[x++]);
}
void wio_plustospace(char *str)
{
    register int x;

    for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' ';
}
char x2c(char *what)
{
    register char digit;

    digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
    digit *= 16;
    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
    return(digit);
}

void wio_unescape_url(char *url, int max)
{
	wio_unescape_url_bin(url,max,NULL);
}

void wio_unescape_url_bin(char *url, int max, int *rlen)
{
    register int x,y;
	int len = strlen(url);
    for(x=0,y=0;url[y];++x,++y) {
        if((url[x] = url[y]) == '%') {
            url[x] = x2c(&url[y+1]);
            y+=2;
        }
		if (y>len+1) break;
		if (x>=max) break;
    }
    url[x] = '\0';
	if (rlen!=NULL) *rlen = x;
}

int wio_decode_ref(Webio *l, char *ref)
{
	char *cl;
	char *var=NULL;
	char *s;
	int vlen;
	int max;
	int x;
	/* /cgi/surgeftpmgr.cgi?cmd=domain&domain=2 */
	l->original_request = strdup(ref);
	s = strchr(ref,'?');
	if (s==NULL) {
		if (l->cgi!=NULL) free(l->cgi);
		l->cgi = strdup(ref);
		max = strlen(l->cgi);
		wio_plustospace(l->cgi);
		wio_unescape_url(l->cgi,max);
		return TRUE;
	}
	*s = 0;
	if (l->cgi!=NULL) free(l->cgi);
	l->cgi = strdup(ref);
	max = strlen(l->cgi);
	wio_plustospace(l->cgi);
	wio_unescape_url(l->cgi,max);
	s++;
	cl = s;
	max = strlen(cl)*2+1000;
	var = malloc(max);
	pair_free(&l->query);
	for(x=0;cl[0] != '\0';x++) {
		wio_getword(var,cl,'&');
		wio_plustospace(var);
		wio_unescape_url_bin(var,max-1,&vlen);
		s = strchr(var,'=');
		if (s==NULL) continue;
		*s = 0;
		if (strlen(var)>200) continue;
		if (strlen(s+1)>1000) continue;
		pair_add_bin(&l->query,var,s+1,vlen-(var-s+1));
	}
	free(var);
	return TRUE;
}

int wio_var_add(Webio *l, char *var, char *val)
{
	return qpair_add(&l->vlist,var,val);
}
char *wio_attach_temp_name(Webio *l, char *bf, int bfsz)
{
	lprintf(bf,bfsz-1,"work/attach_%d.tmp",th_id());
	return bf;
}
int wio_decode_xml(Webio *w,char *contents)
{
	
	// Read content into xml structure... 
	//printf("XML: %s\n",contents);
	w->xml = xml_read_buffer(contents);
	return TRUE;
}


#define BIGSZ 100000
void wio_decode_form(Webio *l, char *contents)
{
	char content[BFSZ];
	char tmp[BFSZ];
	Strlines v;
	char bound_part[BFSZ];
	char bound_end[BFSZ];
	char *s;
	int len,i;
	FILE *attach=NULL;
	char *line=NULL;
	int lasteol=FALSE;
	char fname[BFSZ];
	char name[BFSZ];
	char rname[BFSZ];
	char bf2[BFSZ];
	char boundary[BFSZ];
	char *body=NULL;
	int inhead = FALSE;
	char a_type[BFSZ];
	char a_name[BFSZ];
//	FILE *f;

	
	body = malloc(BIGSZ+1);
	strcpy(a_name,"");
	strcpy(body,""); strcpy(a_type,"");
	wio_attach_temp_name(l,fname,BFSZ-1);
	ncpy(content,l->contenttype,BFSZ-1);
	s = strstr(content,"boundary=");
    if (s==NULL) {
        imsg("Can't find boundary in mime contents %s",content);
		free(body); body = NULL;
		return;
	}
	s += 9;
    ncpy(boundary,lib_get_quote(s,tmp),BFSZ-1);
    imsg("Got boundary (%s) \n",boundary);
    lprintf(bound_part,BFSZ-1,"--%s",boundary);
    lprintf(bound_end,BFSZ-1,"--%s--",boundary);
	strcpy(name,"");

	str_lines_start(&v,contents,l->contentlen);
	line = str_lines_next_bin(&v,&len);
	for (;line!=NULL;line = str_lines_next_bin(&v,&len)) {
		char xline[BFSZ];
		int n;
		n = len;
		if (n>=BFSZ) n = BFSZ-1;
		if (n<0) n = 0;
		ncpy(xline,line,n);

		if (strncmp(xline,bound_part,strlen(bound_part))==0) {
			inhead = TRUE;
			if (!name[0]) continue;
			if (attach!=NULL) wmime_attach_close(l,&attach,name,fname);
			else {
				//imsg("mime: ADding variable (%s) (%s)\n",name,body);
				pair_add(&l->form,name,body);
			}
			continue;
		}
		else if (strncmp(xline,bound_end,strlen(bound_end))==0) {
			if (!name[0]) continue;
			if (attach!=NULL) wmime_attach_close(l,&attach,name,fname);
			else {
				//imsg("mime: ADding variable2 (%s) (%s)\n",name,body);
				pair_add(&l->form,name,body);
			}
			continue;
		}
		if (inhead) {
			strcpy(body,"");
			lasteol = FALSE;
			if (strcmp(xline,"\r\n")==0) 
				inhead = FALSE;
			if (xisspace(xline[0])) goto skip;
			ncpy(bf2,xline,BFSZ-1); strlwr(bf2);
			if (strstr(bf2,"content-type: ")!=NULL) {
				s = xline + 14;
				ncpy(a_type,xline,BFSZ-1);
				s = strchr(a_type,';'); if (s!=NULL) *s = 0;
				s = strchr(a_type,'\r'); if (s!=NULL) *s = 0;
				s = strchr(a_type,'\n'); if (s!=NULL) *s = 0;
			}
			if (strstr(bf2,"content-disposition:")!=NULL) {
				char tmp5[BFSZ];
skip:			s = strstr(xline,"name="); if (s==NULL) continue;
				ncpy(name,lib_get_quote(s+5,tmp),BFSZ-1);
				s = strstr(xline,"filename="); if (s==NULL) continue;
				ncpy(rname,lib_get_quote(s+9,tmp),BFSZ-1);
				ncpy(a_name,rname,BFSZ-1);
				wio_var_add(l,"attach_name",a_name);
				lprintf(tmp5,BFSZ-1,"%s_name",name);
				wio_input_add(l,tmp5,a_name);
				if ((attach==NULL)&&(strlen(a_name)>0)) {
					attach = fopen(fname,"wb");
					l->has_attach=true;
					if (attach==NULL) emsg("Cannot create (%s) (%s)\n",fname,strerror(errno));
					//imsg("Writing to %s %p\n",fname,attach);
				}
			}
		} else {
			if (attach!=NULL) {
				if (lasteol) fwrite("\r\n",1,2,attach);
				lasteol = FALSE;
				if (len>=2) if (strncmp(line+len-2,"\r\n",2)==0) {
					len-=2;
					lasteol = TRUE;
				}
//printf("len %d, lasteol %d\n",len,lasteol);
				fwrite(line,1,len,attach);
			} else if (strlen(name)>0) {
				if (lasteol) ncat(body,"\r\n",BIGSZ-1);
				lasteol = FALSE;
				if (len >= 2 && strncmp(&line[len-2],"\r\n",2)==0) {
					lasteol = TRUE;
					len -= 2;
				}
				for(s = body; *s; s++) {}
				for(i = 0; i < len && (s-body) < BIGSZ-1; i++) *s++ = line[i];
				*s = 0;
			}
		}
	}
	str_lines_end(&v);
	if (attach!=NULL) fclose(attach);
	free(body); body = NULL;
}
char *fgets_len(char *bf, int max, FILE *f, int *len)
{
	int c;
	char *out=bf;
	*len = 0;
	if (feof(f)) return NULL;
	for (;!feof(f);) {
		c = fgetc(f);	
		if (c==EOF) break;
		*out++ = c;
		if (c=='\n') break;
		if (out-bf>max-2) break;
	}
	if (out==bf) return NULL;
	*len = out-bf;
	*out++ = 0;
	return bf;
}
void wio_decode_form_file(Webio *l, char *tmpfile)
{
	char content[BFSZ];
	char tmp[BFSZ];
	char bound_part[BFSZ];
	char bound_end[BFSZ];
	char *s;
	int len,i;
	FILE *attach=NULL;
	char line[BFSZ];
	int lasteol=FALSE;
	char fname[BFSZ];
	char name[BFSZ];
	char rname[BFSZ];
	char bf2[BFSZ];
	char boundary[BFSZ];
	char *body=NULL;
	int inhead = FALSE;
	char a_type[BFSZ];
	char a_name[BFSZ];
	FILE *f;

	f = fopen(tmpfile,"rb");
	if (f==NULL) return ;

	body = malloc(BIGSZ+1);
	strcpy(a_name,"");
	strcpy(body,""); strcpy(a_type,"");
	wio_attach_temp_name(l,fname,BFSZ-1);
	ncpy(content,l->contenttype,BFSZ-1);
	s = strstr(content,"boundary=");
    if (s==NULL) {
        imsg("Can't find boundary in mime contents %s",content);
		free(body); body = NULL;
		fclose(f);
		return;
	}
	s += 9;
    ncpy(boundary,lib_get_quote(s,tmp),BFSZ-1);
    imsg("Got boundary (%s) \n",boundary);
    lprintf(bound_part,BFSZ-1,"--%s",boundary);
    lprintf(bound_end,BFSZ-1,"--%s--",boundary);
	strcpy(name,"");

	for (;!feof(f); ) {
		char xline[BFSZ];
		int n;

		if (fgets_len(line,BFSZ-2,f,&len)==NULL) break; 
		n = len;
	//	imsg("Read line %d (%s)\n",len,line);
		if (n>=BFSZ) n = BFSZ-1;
		if (n<0) n = 0;
		ncpy(xline,line,n);

		if (strncmp(xline,bound_part,strlen(bound_part))==0) {
			inhead = TRUE;
			if (!name[0]) continue;
			if (attach!=NULL) wmime_attach_close(l,&attach,name,fname);
			else {
				imsg("mime: ADding variable (%s) (%s)\n",name,body);
				pair_add(&l->form,name,body);
			}
			continue;
		}
		else if (strncmp(xline,bound_end,strlen(bound_end))==0) {
			if (!name[0]) continue;
			if (attach!=NULL) wmime_attach_close(l,&attach,name,fname);
			else {
				imsg("mime: ADding variable2 (%s) (%s)\n",name,body);
				pair_add(&l->form,name,body);
			}
			continue;
		}
		if (inhead) {
//imsg("mime header (%s)\n",xline);
			strcpy(body,"");
			lasteol = FALSE;
			if (strcmp(xline,"\r\n")==0) 
				inhead = FALSE;
			if (xisspace(xline[0])) goto skip;
			ncpy(bf2,xline,BFSZ-1); strlwr(bf2);
			if (strstr(bf2,"content-type: ")!=NULL) {
				s = xline + 14;
				ncpy(a_type,xline,BFSZ-1);
				s = strchr(a_type,';'); if (s!=NULL) *s = 0;
				s = strchr(a_type,'\r'); if (s!=NULL) *s = 0;
				s = strchr(a_type,'\n'); if (s!=NULL) *s = 0;
			}
			if (strstr(bf2,"content-disposition:")!=NULL) {
				char tmp5[BFSZ];
skip:			s = strstr(xline,"name="); if (s==NULL) continue;
				ncpy(name,lib_get_quote(s+5,tmp),BFSZ-1);
				s = strstr(xline,"filename="); if (s==NULL) continue;
				ncpy(rname,lib_get_quote(s+9,tmp),BFSZ-1);
				ncpy(a_name,rname,BFSZ-1);
				wio_var_add(l,"attach_name",a_name);
				lprintf(tmp5,BFSZ-1,"%s_name",name);
				wio_input_add(l,tmp5,a_name);
//imsg("rname=%s name=%s fname=%s\n",rname,name,fname);
				if ((attach==NULL)&&(strlen(a_name)>0)) {
					attach = fopen(fname,"wb");
					l->has_attach=true;
					if (attach==NULL) emsg("Cannot create (%s) (%s)\n",fname,strerror(errno));
					//imsg("Writing to %s %p\n",fname,attach);
				}
			}
		} else {
			if (attach!=NULL) {
				if (lasteol) fwrite("\r\n",1,2,attach);
				lasteol = FALSE;
				if (len>=2) if (strncmp(line+len-2,"\r\n",2)==0) {
					len-=2;
					lasteol = TRUE;
				}
//printf("len %d, lasteol %d\n",len,lasteol);
				fwrite(line,1,len,attach);
			} else if (strlen(name)>0) {
				if (lasteol) ncat(body,"\r\n",BIGSZ-1);
				lasteol = FALSE;
				if (len >= 2 && strncmp(&line[len-2],"\r\n",2)==0) {
					lasteol = TRUE;
					len -= 2;
				}
				for(s = body; *s; s++) {}
				for(i = 0; i < len && (s-body) < BIGSZ-1; i++) *s++ = line[i];
				*s = 0;
			}
		}
	}
	if (attach!=NULL) fclose(attach);
	free(body); body = NULL;
	fclose(f);
}
int wio_read_content_fhandle(Webio *w, FILE *f)
{
	int got;
	int want;
	int r;
	char tmp[9000];
	got = 0;
	strlwr(w->agent);

	if (strstr(  pair_find(w->headers,"Expect"),"100-continue"  ) !=NULL) {
		tcp_printf(w->c,"HTTP/1.1 100 Continue\r\n\r\n");
	}
	for (;got<w->contentlen;) {
		want = w->contentlen-got;
		if (want>8500) want = 8192;
		r = tcp_read_timed(w->c,tmp,want,6000);
		if (r<=0) break;
		
		fwrite(tmp,r,1,f);
		got += r;
	}
	fclose(f); f = NULL;
	return TRUE;
}
int wio_read_chunked(Webio *l, FILE *f);

int wio_read_content_file(Webio *w)
{
	FILE *f;
	// create a temporary file, store the name in the webio structure, and store the data...
	lprintf(w->contentfname,BFSZ-1,"./work/webcon_%d.tmp",th_id());
	f = fopen(w->contentfname,"wb");
	if (f==NULL) return FALSE;
	if (strstrnc(pair_find(w->head,"Transfer-Encoding"),"Chunked")!=NULL) return wio_read_chunked(w,f);

	wio_read_content_fhandle(w,f);	
		return TRUE;
}
int wio_auth_decodex(Webio *l, char *bf, char *user, char *pass, int bfsz);
int wio_auth_decode(Webio *l, char *user, char *pass)
{
	return wio_auth_decodex(l,pair_find(l->head,"Authorization"),user,pass,BFSZ-1);
}
int wio_auth_decodex(Webio *l, char *bf, char *user, char *pass, int bfsz)
{
	char uinfo[BFSZ];
	char *s;
	int n;

	s = strstr(bf,"Basic");
	if (s == NULL) {
		emsg("web_auth no basic method (%s)\n", bf);
		return FALSE;
	}

	if (strlen(s)>200) {
		emsg(" web_auth line too long (%s)\n", bf);
		return FALSE;
	}

	n = decode_base64(s+6,uinfo,BFSZ);
	if (n<0 || n>BFSZ-1) return FALSE;
	uinfo[n] = 0;

	ncpy(user,uinfo,bfsz-1);
	*pass = 0;

	s = strchr(user,':');
	if (s) {
		*s = 0;
		ncpy(pass,s+1,bfsz-1);
	}

	return TRUE;
}

int wio_read_content(Webio *l,char *bf)
{
	int r;
	char tmp[9000];
	int got=0;
	char *content=NULL;
	int want;
	char *rest=NULL;
	int ispipe=FALSE;
	int maxlen=0;

	maxlen = l->contentlen + 1;

//	imsg("Read content length %d type %s\n",l->contentlen,l->contenttype);

	if (l->www_method==WEB_PUT) {
		// if it's a put, store it directly into a file...  -- YAY
		wio_read_content_file(l);
		return TRUE;
	}
	
	
	if ((l->contentlen>2000000) && (strstr(l->contenttype,"multipart/form-data")!=NULL) ) { // If more than 2mb then use a file instead if a form-data input
		char tempfile[BFSZ];
		FILE *f;
		lprintf(tempfile,BFSZ-1,"./work/webin_%d.tmp",th_id());
		f = fopen(tempfile,"wb");
		if (f==NULL) return FALSE;
		wio_read_content_fhandle(l,f);
		wio_decode_content(l,NULL,tempfile);
		remove(tempfile);
		return TRUE;
	}
	if (l->contentlen>0) {

		content = malloc(l->contentlen+100);
		got = 0;
		/* Next two lines to deal with buggy IE */
		strlwr(l->agent);
		//if (strstr(l->agent,"msie")!=NULL) l->contentlen += 2;
		for (;got<l->contentlen;) {
			//imsg("Got %d of %d, read %d more\n",got,l->contentlen,l->contentlen-got);
			want = l->contentlen-got;
			if (want>8500) want = 8192;
			r = tcp_read_timed(l->c,tmp,want,6000);
			if (r<=0) break;
			
			if (got+r<maxlen) {
				memcpy(content+got,tmp,r);
				content[got+r] = 0;
			}
			got += r;
		}
//		imsg("Got2 %d of %d, read %d more\n",got,l->contentlen,l->contentlen-got); // delay is before here...
		if (content!=NULL) content[got] = 0;
		wio_decode_content(l,content,NULL);
		if (content!=NULL) free(content);
	}

	return TRUE;
}

int wio_read_to_file(Webio *l, FILE *f, int len)
{
	char tmp[9000];
	int got=0;
	int want;
	int r;
	int tot=0;

	for (;got<len;) {
		want = len-got;
		if (want>8500) want = 8192;
		r = tcp_read_timed(l->c,tmp,want,6000);
		if (r<=0) break;
		if (f) fwrite(tmp,1,r,f);
		got += r;
		tot += r;
	}
	return got;
}

int wio_read_chunked(Webio *l, FILE *f)
{
	unsigned int len;
	int tot = 0;
	int nb=0;
	char bf[BFSZ];
	for (;TRUE;) {
		if (!tcp_readline(l->c,bf,BFSZ-1)) {
			imsg("web: read_chunk read line failed - socket closed maybe\n");
			break;
		}
		if (strlen(bf)==0) {
			imsg("web: read_chunk read a blank line\n");
			nb++;
			if (nb>10) break;
			continue;
		}
		nb = 0;
		len = lib_xtoi(bf);
		if (len==0) {
			if (*bf=='0') {
				imsg("web: read_chunk, got a zero (%s)\n",bf);
				break;
			}
			imsg("web: read_chunk got a non hex thing (%s)\n",bf);
			continue;
		}
		imsg("web: read_chunk %d bytes (%s) %c\n",len,bf,(int) *bf);
		tot += wio_read_to_file(l,f,len);
		imsg("web: read_chunk %d bytes - total=%d\n",len,tot);
	}
	imsg("web: read_chunk finished %d\n",tot);
	return tot;
}


void wio_decode_content(Webio *l, char *bf, char *tempfile)
{
	char *var;
	char *cl=bf;
	char *s;
	int max;
	int x;

	if (l->isproxy) {
		l->content_store = (char *) malloc(l->contentlen+1);
		memcpy(l->content_store,bf,l->contentlen);
	}
	if (strstr(l->contenttype,"multipart/form-data")!=NULL) {
		imsg("Decode form(%s)",l->contenttype);
		if (tempfile) wio_decode_form_file(l,tempfile);
		else wio_decode_form(l,bf);
		imsg("Decode form(%s)-completed",l->contenttype);
		return;
	}
	if (strstr(l->contenttype,"text/xml")!=NULL) {
		imsg("Decode xml(%s)",l->contenttype);
		wio_decode_xml(l,bf);
		return;
	}
	max = strlen(cl)*2+1000;
	var = malloc(max);
	pair_free(&l->form);
	for(x=0;cl[0] != '\0';x++) {
		wio_getword(var,cl,'&');
		wio_plustospace(var);
		wio_unescape_url(var,max-1);
		s = strchr(var,'=');
		if (s==NULL) continue;
		*s = 0;
		if (strlen(var)>200) continue;
		//imsg("Form data (%s)(%.20s%s)",var,s+1,(strlen(s+1)>20)?"...":"");
		pair_add(&l->form,var,s+1);
	}
	free(var);
}

#ifndef WIO_NO_URL_ENCODE
int url_encode_real(char *sstring, char *result, int bfsz,int  allow_some_specials);

int url_encode(char *string, char *result, int bfsz) {
	return url_encode_real(string, result, bfsz, false);
}
int url_encode_real(char *sstring, char *result, int bfsz,int  allow_some_specials)
{
	unsigned char *string = sstring; // chrisp changed these two lines to unsigned
	unsigned char *p,*s;
	int left;

	if (!string) return 0;
	if (bfsz <= 0) return 0;
	left = bfsz-1;
	for(s = string, p = result; *s && left; ) {
		if (isalnum(*s) || (allow_some_specials && strchr("._",*s))) {
			*p++ = *s++;
			left--;
		}
		else {
			if (left < 3) {
				left = 0;
				break;
			}
			*p++ = '%';
			*p++ = "0123456789ABCDEF"[*s/16];
			*p++ = "0123456789ABCDEF"[*s%16];
			left -= 3;
			s++;
		}
	}
	*p = '\0';
	if (left == 0 && *s) return -1;
	return bfsz-left;
}
#endif

char *wio_input_query(Webio *l, char *string, int bfsz) {
	if (string) {
		int upto = 0;
		Pair *p;

		for(p = l->query; p; p = p->next) {
			if (upto) upto += lpcat(string,bfsz,"&%s=",p->var);
			else upto = lprintf(string,bfsz,"%s=",p->var);
			upto += url_encode(p->val,&string[upto],bfsz-upto);
			upto--;
		}
	}
	return string;
}
char *wio_input_null(Webio *l, char *var)
{
	char *s;
	s = pair_find_null(l->query,var);
	if (!s) s = pair_find_null(l->form,var);
	return s;
}
char *wio_input(Webio *l, char *var)
{
	char *s;
	s = pair_find(l->query,var);
	if (strlen(s)>0) return s;
	return pair_find(l->form,var);
}
char *wio_input_bin(Webio *l, char *var, int *len)
{
	char *s;
	s = pair_find_bin(l->query,var,len);
	if (strlen(s)>0) return s;
	return pair_find_bin(l->form,var,len);
}



int wio_find_method(char *s)
{
	int m;
	for (m=0;wio_names[m]!=NULL;m++) {
		if (strcmpnc(s,wio_names[m])==0) return m;
	}
	return -1;
}
int wio_modified(Webio *l, char *dte)
{
	if (l->mod!=NULL) free(l->mod);
	l->mod = strdup(dte);
	return TRUE;
}
USER_AGENT wio_identify_agent(char *agent_string)
{
	char *p;

	//Opera/7.23 (Windows NT 5.0; U)  [en]
	if (strstrnc(agent_string,"opera"))
		return UA_OPERA;

	if ((p = strstr(agent_string,"MSIE "))) {
		//Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; .NET CLR 1.0.3705)
		//Mozilla/4.0 (compatible; MSIE 5.22; Mac_PowerPC)
		//IE 5.0

		//Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; MyIE2; .NET CLR 1.1.4322)
		//IE 6.0
		//MYIE2

		if (p[6] == '6') return UA_IE_6;
		if (p[6] == '5') return UA_IE_5;
		//bug_hunt("agent.log","%s\n",agent_string);
		return UA_IE_5; //lowest supported browser
	}

	if ((p = strstrnc(agent_string,"Mozilla"))) {
		//Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; m18) Gecko/20010131 Netscape6/6.01
		//Netscape 6.01
		if (strstrnc(agent_string,"Netscape6"))
			return UA_NETSCAPE_6;

		//Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.5
		//Safari(MAC)
		if (strstrnc(agent_string,"Safari"))
			return UA_SAFARI;

		//Mozilla/4.75 [en] (Windows NT 5.0; U)
		//Netscape 4.75
		if (p[7] == '4')
			return UA_NETSCAPE_4;

		//Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.5a) Gecko/20030728 Mozilla Firebird/0.6.1
		//Firebird 0.6.1

		//Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.6a) Gecko/20031030
		//Mozilla 1.6a

		//bug_hunt("agent.log","%s\n",agent_string);
		return UA_MOZILLA_5;
	}

	//bug_hunt("agent.log","%s\n",agent_string);
	return UA_UNKNOWN;
}

int wio_decode_head(Webio *l, char *bf)
{
	Strlines v;
	char *line;
	char *s,*s2;
	int m;
	char *rest = NULL;
	char *content = NULL;
	/* GET /cgi/surgeftpmgr.cgi?cmd=domain&domain=2 HTTP/1.0) */

	 l->depth = 2; // default to infinity

	str_lines_start(&v,bf,strlen(bf));
	l->wwwget = TRUE;
	line = str_lines_next(&v);
	if (line==NULL) return FALSE;
	s = strchr(line,' ');
	if (s==NULL) return FALSE;

	if (strstrnc(line,"http/1.1")!=NULL) l->httpversion = 1;
	*s = 0;
	m = wio_find_method(line);
	l->www_method = m;

	l->wwwget = FALSE;
	if (m>WEB_POST) if (!webdav_enabled) {
		imsg("webio: Invalid method, was not GET or POST (%.20s) \n",line);
		return FALSE;
	}
	if (m==WEB_POST) l->wwwget = FALSE;
	else if (m==WEB_GET) l->wwwget = TRUE;

	s++;
	s2 = strchr(s,' ');
	if (s2!=NULL) {
		*s2 = 0;
		if (l->ref!=NULL) free(l->ref);
		l->ref = strdup(s);
		wio_decode_ref(l,s);
	} else {
		if (l->ref!=NULL) free(l->ref);
		l->ref = strdup(s);
		wio_decode_ref(l,s);
	}

	l->gotwebauth = FALSE;
	ncpy(l->hostname,"localhost",BFSZ-1);
	l->hostport = -1;

	if (line!=NULL) line = str_lines_next(&v);
	for (;line!=NULL;) {
		/*dmsg("Line (%.90s) %d\n",line,l->contentlen);*/

//imsg("HTTP: header (%s)\n",line);
		if (strncmpnc(line,"Content-Length:",15)==0) l->contentlen = atoi(line+16);
		if (strncmpnc(line,"Content-Type:",13)==0) ncpy(l->contenttype,line+14,100);
		if (strncmpnc(line,"Range: ",7)==0) {
			ncpy(l->range,line+7,BFSZ-1);
		} 
		if (strncmpnc(line,"Depth:",6)==0) {
			l->depth = atoi(line+7);
			if (strstrnc(line,"infinity")!=NULL) l->depth = 2;
		}
		/*if (strncmpnc(line,"Authorization:",14)==0) wio_auth(l,line);*/
		if (strncmpnc(line,"If-Modified-Since: ",19)==0) wio_modified(l,line+19);
		if (strncmpnc(line,"User-Agent: ",12)==0) {
			for(s = &line[12]; isspace(*s); s++);
			l->agentid = wio_identify_agent(s);
			ncpy(l->agent,s,100);
		}
		if (strncmpnc(line,"Cookie: ",8)==0) {
			if (l->cookie!=NULL) free(l->cookie);
			l->cookie = strdup(line+8);
		}
		if (strncmpnc(line,"Connection: Close",17)==0) l->closing = TRUE;
		if (strncmpnc(line,"Connection: Keep-Alive",22)==0) l->keepalive = TRUE;
		if (strncmpnc(line,"Host:",5)==0){
			s = strchr(line+6,':');
			if (s!=NULL) {
				*s = 0;
				l->hostport = atoi(s+1);
			}
			ncpy(l->hostname,line+6,BFSZ-1);
			if (strlen(l->hostname)>700){ // MW: Limiting the hostname (700 should be plenty!) in case it is unsafely used later on
				l->hostname[700]='\0';
				emsg("Truncating excessively long host header (probable hacking attempt)");
			}
		}

		s = strchr(line,':');
		if (s) {
			for(*s++ = 0; isspace(*s); s++);
			for(s2 = line; isspace(*s2); s2++);
			pair_add(&l->head,s2,s);
		}

		if (strlen(line)==0) break;
		line = str_lines_next(&v);
	}
	str_lines_end(&v);
	if (l->contentlen<0) {
		emsg("Content length is negative\n");
		return FALSE;
	}
	if (strstr(l->contenttype,"multipart/form-data")==NULL) {
		if (l->contentlen>100000000) {
			emsg("Content length is greater than 100mb %d\n",l->contentlen);
			return FALSE;
		}
	}

	return TRUE;
}
char *wio_clean(char *fname, char *tmp)
{
	char *s;
	char *xtmp=tmp;
	for (s=fname;*s!=0;s++) {
		if (isalnum(*s)) *tmp++ = *s;
		else if (*s=='/') *tmp++ = *s;
		else if (*s=='.') *tmp++ = *s;
		else if (*s==':') *tmp++ = *s;
		else if (*s==' ') *tmp++ = *s;
		else if (*s=='-') *tmp++ = *s;
		else if (*s=='+') *tmp++ = *s;
		else if (*s=='_') *tmp++ = *s;
		else if (*s=='\\') *tmp++ = *s;
		else *tmp++ = '?';
	}
	*tmp++ = 0;
	return xtmp;
}
char *wio_clean_right(char *fname, char *tmp)
{
	char bf[BFSZ];
	char *s;
	char *last=NULL;
	ncpy(bf,wio_clean(fname,tmp),BFSZ-1);
	for (s=bf;*s!=0;s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	if (last!=NULL) ncpy(tmp,last+1,BFSZ-1);
	else ncpy(tmp,bf,BFSZ-1);
	return tmp;
}
int wio_redirect(Webio *l, char *location)
{
	char bf[BFSZ];
	if (l->done_content) return TRUE; 
	l->done_content = TRUE;
	if (!wio_printf_now(l,"HTTP/1.%d 307 OK\r\n",l->httpversion)) return FALSE;
	if (!wio_printf_now(l,"Server: DManager\r\n")) return FALSE;
	if (!wio_printf_now(l,"Location: %s\r\n",location)) return FALSE;
	if (!wio_printf_now(l,"Date: %.200s\r\n",lib_web_date(bf))) return FALSE;
	if (!wio_printf_now(l,"Content-Type: text/plain\r\n")) return FALSE;
	if (!wio_printf_now(l,"Content-Length: 0\r\n")) return FALSE;
	if (!wio_printf_now(l,"\r\n")) return FALSE;
	return TRUE;

}
#define REALM "online"

int wio_any_header(Webio *l, char *content, bigint length, int chunked)
{
	char bf[BFSZ];
	Pair *p;
	char tmp[BFSZ];


	if (l->done_content) return TRUE; 
	l->done_content = TRUE;
	if (!wio_printf_now(l,"HTTP/1.%d 200 OK\r\n",l->httpversion)) return FALSE;
	if (!wio_printf_now(l,"Server: DManager\r\n")) return FALSE;
	if (!wio_printf_now(l,"MIME-version: 1.0\r\n")) return FALSE;
	if (!wio_printf_now(l,"WWW-Authenticate: Basic realm=\"%s.%s\"\r\n", REALM, tcp_hostname(bf))) return FALSE;
	if (!wio_printf_now(l,"Date: %.200s\r\n",lib_web_date(bf))) return FALSE;
//	if (!pair_find(l->headers,"Cache-Control") && !wio_printf_now(l,"Cache-Control: max-age=%d\r\n",1)) return FALSE;
	if (!wio_printf_now(l,"Content-Type: %s\r\n",content)) return FALSE;
	if (chunked && l->httpversion==1) {
		if (!wio_printf_now(l,"Transfer-Encoding: chunked\r\n")) return FALSE;
//		if (!wio_printf_now(l,"Connection: close\r\n")) return FALSE;
	}
	for(p = l->headers; p; p = p->next) {
		if (!wio_printf_now(l,"%s: %s\r\n",p->var,p->val)) return FALSE;

	}
	if (!chunked) if (!wio_printf_now(l,"Content-Length: %s\r\n",bigint_to_str(length,tmp))) return FALSE;

	if (!wio_printf_now(l,"\r\n")) return FALSE;
	if (chunked  && l->httpversion==1) l->chunked = TRUE; // after we sent the rest of the headers :-) 
	// if httpversion == 0 then we close connection every time anyway so we don't need to use chunked... 

	return TRUE;
}

int wio_with_code(Webio *l, char *content, int code, char *data)
{
	char bf[BFSZ];
	Pair *p;
	int length = strlen(data);



	if (l->done_content) return TRUE; 
	l->done_content = TRUE;
	if (!wio_printf_now(l,"HTTP/1.d %d %s OK\r\n",l->httpversion,code,data)) return FALSE;
	if (!wio_printf_now(l,"Server: DManager\r\n")) return FALSE;
	if (!wio_printf_now(l,"MIME-version: 1.0\r\n")) return FALSE;
	if (!wio_printf_now(l,"Date: %.200s\r\n",lib_web_date(bf))) return FALSE;
	if (!wio_printf_now(l,"Content-Type: %s\r\n",content)) return FALSE;
	for(p = l->headers; p; p = p->next) {
		if (!wio_printf_now(l,"%s: %s\r\n",p->var,p->val)) return FALSE;

	}
	if (!wio_printf_now(l,"Content-Length: %d\r\n",length+2)) return FALSE;

	if (!wio_printf_now(l,"\r\n")) return FALSE;
	if (!wio_printf_now(l,"%s\r\n",data)) return FALSE;
	return TRUE;
}



int wio_printf_now(Webio *l, char *format, ...) 
{
	char bf[5000];
	va_list args;
	int len,w;
	char bf2[BFSZ];

	if (l==NULL) return TRUE; // so it doesn't crash on a null wink...

	va_start(args,format);
	len = vsnprintf(bf,4999,format,args);
	if (len == -1) bf[len=4999] = '\0';
	va_end(args);

//	if (l->outfn) w = l->outfn(l->outp,bf,len); else 

//	printf("--> %s",bf);
	if (l->chunked) {
		lprintf(bf2,BFSZ-1,"%x\r\n",len);
		tcp_write_timed(l->c,bf2,strlen(bf2),1000*60*10);
	}
	w = tcp_write_timed(l->c,bf,len,1000*60*10);
	if (l->chunked) {
		lprintf(bf2,BFSZ-1,"\r\n");
		tcp_write_timed(l->c,bf2,strlen(bf2),1000*60*10);
	}

	if (w != len) return FALSE;
	return TRUE;
}

#ifndef WIN32
#define _O_WRONLY  O_WRONLY
#define _O_BINARY 0
#define _O_RDONLY  O_RDONLY
#define _O_CREAT O_CREAT 
#define _open(a,b,c) open(a,b,0777)
#endif


int wio_range_split(char *range, char *p[])
{
	char *s;
	s = strstr(range,"bytes=");
	if (s==NULL) return 0;
	s += 6;

	return token_split(s,p,",",99);
}

int wio_range_get(char *range, bigint *r1, bigint *r2, bigint sz)
{
	char bf[BFSZ];
	char *s;
	*r1 = *r2 = 0;
	*r2 = -1;
	ncpy(bf,range,BFSZ-1);
	s = strchr(range,'-'); 
	if (s==NULL) return FALSE;
	*s = 0;
	*r1 = str_to_bigint(bf);
	*r2 = str_to_bigint(s+1);
	if (strlen(s+1)==0) *r2 = sz-1;
	return TRUE;
}
#ifdef WIN32
#define read _read
#define close _close
#endif
int wio_showimage(Webio *l, char *fname)
{
	char *p[110];
	int np;
	int n;
	time_t mtime;
	int f = -1;
	char lenbf[BFSZ];
	bigint send;
	int i;
	int chunk;
	bigint r1,r2;
	char tmp3[BFSZ];
	char dte[BFSZ];
	char bf2[BFSZ];
	char *bigbf=NULL;
	int ret=TRUE;
	char tmp1[BFSZ];
	char bf[BFSZ];
	char tmp2[BFSZ];
	char range[BFSZ];
	bigint fz;
	bigint rangetot=0;

	f = _open( fname, _O_RDONLY | _O_BINARY, _S_IREAD );
	if (f==-1) {
		wio_show_code(l,404,"Requested file not found");
		imsg("Sending no such file (%.100s)\n",fname);
		
		return TRUE;
	}
	fz = bigint_file_size(fname);
	imsg("File (%s) length (%d) \n",fname,(int) fz);

	ncpy(range,l->range,BFSZ-1);
	np = wio_range_split(range,p);
	// Now process the 'range' string, and figure out total length.
	rangetot = 0;
	for (i=0; i<np; i++) {
		wio_range_get(p[i],&r1,&r2,fz);
		rangetot += r2-r1+1;
	}

	mtime = lib_mtime(fname);
	lib_web_date2(mtime,dte);
	if (l->mod!=NULL) {
		if (mtime<=web_date_get_correct(l->mod)) {
			if (!wio_printf_now(l,"HTTP/1.%d 304 Not modified\r\n",l->httpversion)) goto failed;
			if (!wio_printf_now(l,"Server: DManager\r\n")) goto failed;
			if (!wio_printf_now(l,"MIME-version: 1.0\r\n")) goto failed;
			if (l->keepalive && l->trykeep) {
				if (!wio_printf_now(l,"Connection: Keep-Alive\r\n")) goto failed;
				l->iskeep = TRUE;
			}
			if (!wio_printf_now(l,"Date: %.200s\r\n",lib_web_date(bf2))) goto failed;
			if (!wio_printf(l,"Content-Length: 0\r\n")) goto failed;
			if (!wio_printf(l,"\r\n",0)) goto failed;
			if (!wio_printf_flush(l)) goto failed;
			goto failed;
		}
	}
	if (strlen(l->range)>0) {
		if (!wio_printf(l,"HTTP/1.%d 206 OK\r\n",l->httpversion)) goto failed;
	} else {
		if (!wio_printf(l,"HTTP/1.%d 200 OK\r\n",l->httpversion)) goto failed;
	}
	if (!wio_printf(l,"Server: DManager\r\n")) goto failed;
	if (!wio_printf(l,"MIME-version: 1.0\r\n")) goto failed;
	if (!wio_printf(l,"WWW-Authenticate: Basic realm=\"%s.%s\"\r\n", REALM, tcp_hostname(bf))) return FALSE;
	if (!wio_printf(l,"Date: %.200s\r\n",lib_web_date(bf2))) goto failed;
	if (!wio_printf(l,"Last-modified: %.200s\r\n",lib_web_date2(mtime,bf2))) goto failed;
	if (!wio_printf(l,"Accept-Ranges: bytes\r\n")) goto failed;
	if (!wio_printf(l,"ETag: \"%d\"\r\n",mtime)) goto failed;



	if (l->keepalive && l->trykeep) {
		if (!wio_printf(l,"Connection: Keep-Alive\r\n")) goto failed;
		l->iskeep = TRUE;
	}
	if (!wio_printf(l,"Content-Type: %.200s\r\n",lib_guess_content(fname))) goto failed;

	ncpy(range,l->range,BFSZ-1);
	if (strlen(range)>0) {
		np = wio_range_split(range,p);
		if (np>=1) {
			wio_range_get(p[0],&r1,&r2,fz);			
			if (!wio_printf(l,"Content-Range: bytes %s-%s/%s\r\n",bigint_to_str(r1,tmp1),bigint_to_str(r2,tmp2),bigint_to_str(fz,tmp3))) goto failed;
		}
	}


	
   

//	printf("Content-type: %.200s\r\n",lib_guess_content(fname));
	if (rangetot==0) rangetot = fz;
	bigint_to_str(fz,lenbf);
	if (!wio_printf(l,"Content-Length: %s\r\n",lenbf)) goto failed; 
	if (!wio_printf(l,"\r\n")) goto failed;
	if (!wio_printf_flush(l)) goto failed;

	// Now process the 'range' of the file, and don't use fopen as it won't work on little files... ? 
	ncpy(range,l->range,BFSZ-1);
	if (strlen(range)==0) ncpy(range,"bytes=-",BFSZ-1);

	np = wio_range_split(range,p);
	// Now process the 'range' string, and figure out total length.
	bigbf = malloc(33000);
	for (i=0; i<np; i++) {
		wio_range_get(p[i],&r1,&r2,fz);
		lseek64(f, r1,SEEK_SET);
		send = r2-r1+1;
		for (;send>0;) {
			chunk = (int) send;
			if (send>32000) chunk = 32000;
			ret = read(f,bigbf,chunk);
			if (ret!=chunk) {imsg("read failed for some reason %s\n",fname); break; }
			n = tcp_write(l->c,bigbf,chunk);
			if (n!=chunk) {imsg("write failed\n"); break; }
			send -= chunk;
		}
	}
	
failed:
	if (f!= -1) close(f);
	free(bigbf);
	return ret;
}

int wio_show_code(Webio *l, int code, char *reason)
{
	char bf[BFSZ];

	if (!wio_printf_now(l,"HTTP/1.%d %d %s\r\n",l->httpversion,code,reason)) goto failed;
	if (!wio_printf_now(l,"Server: DManager\r\n")) goto failed;
	if (!wio_printf_now(l,"MIME-version: 1.0\r\n")) goto failed;
	if (!wio_printf_now(l,"WWW-Authenticate: Basic realm=\"%s.%s\"\r\n", REALM, tcp_hostname(bf))) return FALSE;
	if (!wio_printf_now(l,"Date: %.200s\r\n",lib_web_date(bf))) goto failed;
	if (!wio_printf_now(l,"Content-Length: %d\r\n\r\n",strlen(reason)+2)) goto failed;
	if (!wio_printf_now(l,"%s\r\n",reason)) goto failed;
//	if (!wio_printf_now(l,"\r\n",reason)) goto failed;
	return TRUE;
failed:
	return FALSE;
}
int wio_auth_required(Webio *w, char *msg)
{
	wio_show_code(w,401,msg);
	return TRUE;
}

int wio_request(Webio *w, char *bf)
{
	
	if (!wio_decode_head(w,bf)) {
		imsg("wio_request - bad headers\n");
		wio_show_code(w,550,"headers incorrect");
		return FALSE;
	}
	// 4 second delay in wio_read_content...
	wio_read_content(w,bf);
	// Now call the actual function callaback
//	imsg("Now run the function\n");
	
	w->fn(w,w->fn_data);
	
//	imsg("Now run the function-DONE\n");
	return TRUE;
}


void wio_list_cookies(Webio *w)
{
	char *s = w->cookie;
	char tmp[BFSZ];
	char *p;

	if (!s) return ;

	for(; *s; s++) {
		for (; isspace(*s); s++) {}
		lib_copy_upto(&s,';',tmp,BFSZ);
		p = strchr(tmp,'=');
		if (!p) continue;
		*p++ = 0;
		pair_add(&w->cookie_list,tmp,p);

		if (*s != ';') break;
	}
}

char *wio_cookie_get(Webio *w, char *var)
{
	if (!w->cookie_list) wio_list_cookies(w);
	return pair_find(w->cookie_list,var);
}

int wio_showafile(Webio *l, char *fname)
{
	wio_showimage(l,fname);
	return TRUE;
}

