// Actual webio encoding functions - used to live in webio.c

#define DMID 14
#include <stdlib.h>
#include "debug.h"
#include "lib.h"
#include "libwebio.h"
#include "lprintf.h"

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif

/* takes BFSZ NOT BFSZ-1!! */
int url_encode(char *string, char *result, int bfsz) {
	return url_encode_real(string, result, bfsz, false);
}
char *url_encode_new( char *string, char *result,int  bfsz) {
	*result = 0;
	url_encode_real (string, result, bfsz, true);
	return result;
}
int url_encode_real(char *sstring, char *result, int bfsz,int  allow_some_specials) {
	unsigned char *string = (unsigned char *) 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 = (unsigned char *) result; *s && left; ) {
		if (sisalnum(*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;
}
// like percent encode spaces (and other characters maybe??) in urls
int url_encode_real2(char *sstring, char *result, int bfsz) {
	unsigned char *string = (unsigned char *) sstring; // chrisp changed these two lines to unsigned
	unsigned char *p,*s;
	int left, allow_some=TRUE;

	if (!string) return 0;
	if (bfsz <= 0) return 0;
	left = bfsz-1;
	for(s = string, p = (unsigned char *) result; *s && left; ) {
		if (!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;
}
char *db_url_encode(char *out,char *in,int outbfsz)
{
	char *encode="!*'\"();:@&=+$,?%#[]% ";	// DO not change this set	"/" removed??
	char *s,*p;
	if(!in) return "";
	for(s=in,p=out;*s;s++) {
		if((p-out)>=(outbfsz-6)) continue;
		if(strchr(encode,*s)) {
			*p++ = '%';
			*p++ = "0123456789ABCDEF"[*s/16];
			*p++ = "0123456789ABCDEF"[*s%16];
		}else{
			*p++=*s;
		}
	}
	*p++='\0';
	return out;
}


char *web_encode_real_charok(char *out,char *in,int outbfsz, int encode_eol) // allow web characters
{
	char *pin,*pout;

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) return in;
		switch(*pin) {
		case '<': strcpy(pout,"&lt;"); pout+=4; break;
		case '>': strcpy(pout,"&gt;"); pout+=4; break;
		case '"': strcpy(pout,"&#34;"); pout+=5; break;

		default:
			*pout++=*pin;
			if (encode_eol && *pin=='\n') {
				strcpy(pout,"<br>");pout+=4;
			}
			break;

		}
	}
	*pout++='\0';
	return out;
}
char *web_encode_real(char *out,char *in,int outbfsz, int encode_eol)
{
	char *pin,*pout;

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) return in;
		switch(*pin) {
		case '<': strcpy(pout,"&lt;"); pout+=4; break;
		case '>': strcpy(pout,"&gt;"); pout+=4; break;
		case '&': strcpy(pout,"&amp;"); pout+=5; break;
		case '"': strcpy(pout,"&#34;"); pout+=5; break;

		default:
			*pout++=*pin;
			if (encode_eol && *pin=='\n') {
				strcpy(pout,"<br>");pout+=4;
			}
			break;

		}
	}
	*pout++='\0';
	return out;
}
// Like web_encode_real : but does not bail out leaving output buffer in an unusable undefined state
int web_encode_real2(char *out,char *in,char **upto,int outbfsz)
{
	char *pin,*pout;
	
	pin=(in?in:*upto);
	*upto=NULL;
	if(!pin) return 0; 
	for(pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) {
			*upto=pin;
			break;
		}
		switch(*pin) {
			case '<': strcpy(pout,"&lt;"); pout+=4; break;
			case '>': strcpy(pout,"&gt;"); pout+=4; break;
			case '&': strcpy(pout,"&amp;"); pout+=5; break;
			case '"': strcpy(pout,"&#34;"); pout+=5; break;
			default: *pout++=*pin; break;
		}
	}
	*pout='\0';
	return (int)(pout-out);
}
// Like web_encode_real : but never returns input buffer as output
char *web_encode_real3(char *out,char *in,int outbfsz, int encode_eol)
{
	char *pin,*pout=NULL;

	ncpy(out,"",BFSZ-1);
	if(!in) goto done;
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) goto done;
		switch(*pin) {
		case '<': strcpy(pout,"&lt;"); pout+=4; break;
		case '>': strcpy(pout,"&gt;"); pout+=4; break;
		case '&': strcpy(pout,"&amp;"); pout+=5; break;
		case '"': strcpy(pout,"&#34;"); pout+=5; break;

		default:
			*pout++=*pin;
			if (encode_eol && *pin=='\n') {
				strcpy(pout,"<br>");pout+=4;
			}
			break;
		}
	}
done:
	*pout++='\0';
	return out;
}

// Opposite of above...
int web_decode(char *out, char *s,int outbfsz) 
{
	int i;
	for(i = 0; i < outbfsz && *s; i++, s++) {
		if (*s == '&'){
			if (strncmpnc(s,"&lt;",4)==0) {
				out[i] = '<';
				s += 3;
			}
			else if (strncmpnc(s,"&gt;",4)==0) {
				out[i] = '>';
				s += 3;
			}
			else if (strncmpnc(s,"&amp;",5)==0) {
				out[i] = '&';
				s += 4;
			}
			else if (strncmpnc(s,"&nbsp;",6)==0) {
				out[i] = ' ';
				s += 5;
			}else{
				out[i] = *s;
			}
		}
		else out[i] = *s;
	}
	out[i] = '\0';
	return i;
}


char *web_string_encode_real(char *out,char *in,int outbfsz,int adv)
{
	char *pin,*pout;

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) return in;
		switch(*pin) {
		case '"':
			if (adv) {strcpy(pout,"&#34;"); pout+=5;}
			else {strcpy(pout,"\\\""); pout+=2;}
			break;
		case '%': strcpy(pout,"\\%"); pout+=2; break;
		case '\'': strcpy(pout,"\\\'"); pout+=2; break;
		case '\\': strcpy(pout,"\\\\"); pout+=2; break;
		default:
			*pout++=*pin;
			break;

		}
	}
	*pout++='\0';
	return out;
}
char *web_string_encode_basic(char *out,char *in,int outbfsz)
{
	return web_string_encode_real(out,in,outbfsz,FALSE);
}

char *web_string_encode(char *out,char *in,int outbfsz)
{
	return web_string_encode_real(out,in,outbfsz,TRUE);
}

//eg line break 0xE280A8 or paragraph break 0xE280A9
int is_utf8_nasty(unsigned char *s, int *n)
{
	if(*s!=0xE2) return FALSE;
	s++;
	if(*s!=0x80) return FALSE;
	s++;
	if(*s==0xA8 || *s==0xA9) {
		*n = 2;	// Eat two "extra" characters
		return TRUE;
	}
	return FALSE;
}

char *web_encode_js(char *out,char *in,int outbfsz)
{
	char *pin,*pout;
	int n=0;
	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) 
			goto done;

		// Strip out any utf8 character we dont want to feed surgeweb 
		if(is_utf8_nasty(pin, &n)){
			if(n>2) n=2;
			pin+=n;
			continue;
		}

		switch(*pin) {
		case '\\': strcpy(pout,"\\\\"); pout+=2; break;
		case '\'': strcpy(pout,"\\\'"); pout+=2; break;
		case '"':  strcpy(pout,"\\\""); pout+=2; break;
		case '\r' : break;
		case '\n': strcpy(pout,"\\n"); pout+=2; break;
		default:
			*pout++=*pin;
			break;
		}
	}
done:
	*pout++='\0';
	return out;
}
// Just like web_encode_js but leave '"' characters untouched
char *web_encode_js2(char *out,char *in,int outbfsz)
{
	char *pin,*pout;
	int n=0;

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) goto done;

		// Strip out any utf8 character we dont want to feed surgeweb
		if(is_utf8_nasty(pin, &n)){
			if(n>2) n=2;
			pin+=n;
			continue;
		}

		switch(*pin) {
		case '\\': strcpy(pout,"\\\\"); pout+=2; break;
		case '\'': strcpy(pout,"\\\'"); pout+=2; break;
		case '\r' : break;
		case '\n': strcpy(pout,"\\n"); pout+=2; break;
		default:
			*pout++=*pin;
			break;
		}
	}
done:
	*pout++='\0';
	return out;
}

// 24/5/2022 Marijn: WARNING: This function seems horribly broken for the case of two consecutive encoded characters
// Am scared to globally fix so will add second rotine for my usecase
char *web_decode_js(char *out,char *in,int outbfsz)
{
	char *pin,*pout;

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) goto done;
		if(*pin=='\\'){
			pin++;
			switch(*pin) {
				// just ignore \,'," and they gets decoded 
				case 'r' : pin++;break;
				case 'n': pin++;strcpy(pout,"\n"); pout++; break;
			}
		}
		*pout++=*pin;
	}
done:
	*pout++='\0';
	return out;
}
char *web_decode_js_fixed(char *out,char *in,int outbfsz)
{
	char *pin,*pout;

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) goto done;
		if(*pin=='\\'){
			pin++;
			switch(*pin) {
				case 'r' : break;
				case 'n': strcpy(pout,"\n"); pout++; break;
				// just ignore \,'," and they gets decoded 
				default: *pout++=*pin; break;
			}
			continue;
		}
		*pout++=*pin;
	}
done:
	*pout++='\0';
	return out;
}


char *web_encode_noquot(char *out,char *in,int outbfsz)
{
	char *pin,*pout,last='\0';

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) goto done;
		switch(*pin) {
		case '\'': 
			if(last!='\\'){
				strcpy(pout,"\\'"); 
				pout+=2; 
				break;
			}
		default:
			*pout++=*pin;
			break;
		}
		last=*pin;
	}
done:
	*pout++='\0';
	return out;
}
char *web_encode_hyphen(char *out,char *in,int outbfsz)
{
	char *pin,*pout;

	if(!in) return "";
    out[outbfsz-1] = 0;
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) goto done;
		switch(*pin) {
		case '-': strcpy(pout,"&#8209;"); pout+=7; break;
		default:
			*pout++=*pin;
			break;
		}
	}
done:
	*pout++='\0';
	return out;
}
char *web_encode_dollar(char *out,char *in,int outbfsz)
{
	char *pin,*pout;

	if(!in) return "";
    out[outbfsz-1] = 0;
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) goto done;
		switch(*pin) {
		case '$': strcpy(pout,"$$"); pout+=2; break;
		default:
			*pout++=*pin;
			break;
		}
	}
done:
	*pout++='\0';
	return out;
}
char *web_base_format_url(char *out, char *base, char *url, int bfsz)
{
	char bf[BFSZ],*p,*s;
	ncpy(bf,base,BFSZ-1);
	p=bf;
	if(strncmpnc(p,"http://",7)==0)
		p+=7;

	if(*url=='/'){
		s=strchr(p,'/');
		if (s)
			*s='\0';
	}else{
		s=strchr_last(p,'/');
		if (s && *(s+1))
			*s='\0';
	}
	lprintf(out,bfsz,"%s%s",bf,url);
	return out;
}
char *web_encode_bar(char *out,char *in,int outbfsz)
{
	char *pin,*pout;

	if(!in) return "";
    out[outbfsz-1] = 0;
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) goto done;
		switch(*pin) {
		case '|': strcpy(pout,"&#124;"); pout+=6; break;
		default:
			*pout++=*pin;
			break;
		}
	}
done:
	*pout++='\0';
	return out;
}


char *api_encode(char *in, char *out)
{
	char tmp[BFSZ];
	web_encode(tmp, in);
	web_encode_js(out,tmp,BFSZ);
	return out;
}