// ----------------------------------------------------
// vcard processing sam - used much like the mime sam
// author: M Weehuizen, November 2009
// ----------------------------------------------------

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "lib.h"
#define DMID 25
#include "debug.h"
#include "vcard.h"
#include "base64.h"


// An example of the fields we need to process:

// BEGIN:VCARD
// VERSION:3.0
// N:Taylor;Gary;A;Mr;CNA
// FN:Mr Gary A Taylor CNA
// NICKNAME:Gary
// EMAIL:gtaylor@nexicomgroup.net
// TEL;TYPE=WORK,PREF:705-932-4191
// ADR:;;5 King St. E.;Millbrook;ON;L0A1G0;Canada
// LABEL;ENCODING=B:NSBLaW5nIFN0LiBFLgpNaWxsYnJvb2ssIE9OCiAgICAgICAgIEwwQTFHMC
//  BDYW5hZGE=
// TITLE:Technician
// ORG:Nexicom;
// BDAY:1958-02-21
// TZ:-05:00
// URL:www.nexicom.net
// CLASS:PUBLIC
// REV:2009-11-05T19:50:19Z
// X-OWM-CHARSET:iso-8859-1
// X-OWM-UID:20091105-195019-JJYJMCBCWCWC-79ER
// END:VCARD

int vcf_new(VcfImport **vc)
{
	*vc = calloc(1,sizeof(VcfImport));
	return TRUE;
}
int vcf_free(VcfImport **vc)
{
	free(*vc);
	*vc = NULL;
	return TRUE;
}
int vcf_line(VcfImport *vc, void *f, char *raw, int *len, int bfsz)
{
	char *s,bf[BFSZ],tmp[BFSZ];
	int length,n;

read_more:
	raw[0] = 0;
	*len = 0;
	vc->base64=FALSE;

	// Get the "start of line"
	if (vc->gotline){
		ncpy(bf,vc->nextline,BFSZ-1);
		vc->gotline = FALSE;
	}else{
		if(!fgets(bf,BFSZ-1,f)) {
			return VCF_END;
		}
		lib_strip_crlf(bf);
	}
	s=bf;
	lib_copy_upto(&s,':',tmp,BFSZ-1);
	if(strstrnc(tmp,"BASE64")) vc->base64=TRUE;

	// look for more
	length=lib_strlen(bf);
	for (;TRUE;) {
		if(!fgets(vc->nextline,BFSZ-1,f)) break;
		lib_strip_crlf(vc->nextline);
		vc->gotline = TRUE;
		if(vc->base64) break;

		if ((vc->nextline[0]==' ') || (vc->nextline[0]=='\t')) {
			if(length+lib_strlen(vc->nextline)>BFSZ-20) break;
			ncat(bf,vc->nextline+1,BFSZ-1);
			vc->gotline=FALSE;
			continue;
		}
		break;
	}

	ncpy(raw,bf,bfsz-1);

	// split into component parts:  (group . ) name ( ; param) : value
	s=strchr(raw,':');
	if(!s) goto read_more;		// line is not a valid format
	*s='\0';
	vc->value=s+1;				// value peeled fo the end
	s=strchr(raw,'.');
	if(s){					// Deal with group prefixes and actual entry name
		*s='\0'; 
		vc->group=raw; vc->name=s+1;
	}else{
		vc->group=NULL; vc->name=raw;
	}
	s=strchr(vc->name,';');		// Raw access to the parameters
	if(s){
		*s='\0';
		vc->param=s+1;
	}else{
		vc->param=NULL;
	}

	// make sure we actually should be importing something
	if(strcmpnc(vc->name,"BEGIN")==0 && strcmpnc(vc->value,"VCARD")==0){
		vc->incard=TRUE;
		return VCF_BEGINCARD;
	}
	if(strcmpnc(vc->name,"END")==0 && strcmpnc(vc->value,"VCARD")==0){
		vc->incard=FALSE;
		return VCF_ENDCARD;
	}
	if(strcmpnc(vc->name,"VERSION")==0 && atoi(vc->value)>=3){
		vc->version_ok=TRUE;
		goto read_more;
	}
	if(!(vc->incard&&vc->version_ok))
		goto read_more;

	// deal with binary encoding
	if(vc->param && vc->value && strstrnc(vc->param,"ENCODING=B")){
		n=decode_base64(vc->value,bf,BFSZ-1);
		if(n>BFSZ-1) n=BFSZ-1;
		if (n>0) ncpy(vc->value,bf,n);
	}

	return vc->base64 ? VCF_DATA : VCF_RECORD;
}
int vcf_rawline(VcfImport *vc, FILE *f, char *raw)
{
	if(!vc->gotline) return FALSE;
	if(vc->nextline[0]!=' ' && vc->nextline[0]!='\t') return FALSE;
	ncpy(raw,vc->nextline,BFSZ-1);
	if(!fgets(vc->nextline,BFSZ-1,f)) vc->gotline=FALSE;
	lib_strip_crlf(vc->nextline);
	return TRUE;
}



// locate what "type" of entry this is based on entries like this:
// EMAIL;TYPE=intl,post,parcel,home;type=pref:marijn@northernsolutions.co.nz
// EMAIL;type=INTERNET;type=HOME:a@b.c
// EMAIL;TYPE=internet,pref:jane_doe@abc.com
// TEL;type=WORK;type=FAX:123456
// TEL;type=HOME;type=FAX:234567
char *vcard_locate_type(char *param, char *name, char *out)
{
	ncpy(out,"",BFSZ-1);
	if(!param) goto done;

	if(strstrnc(param,"WORK")) ncpy(out,"work",BFSZ-1);
	if(strstrnc(param,"HOME")) ncpy(out,"home",BFSZ-1);

	if(strcmpnc(name,"TEL")==0){
		if(strstrnc(param,"CELL")) ncpy(out,"mobile",BFSZ-1);
		if(strstrnc(param,"FAX")) {
			if(out[0]) ncat(out," ",BFSZ-1);
			ncat(out,"fax",BFSZ-1);
		}
	} // possibly do additional tests for other fields too??

done:
	if(out[0]=='\0') ncpy(out,"work",BFSZ-1);
	return out;
}
char *vcard_decode(char *in,char *out)
{
	// this will do to decode: '\;' '\:' '\\"' '\r' '\n'
	//	return web_decode_js(out,in,BFSZ-1);	// nope not good enough, lets roll our own... 
	char *pin,*pout;
	int outbfsz=BFSZ-1;

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) return in;
		if(*pin=='\\'){
			switch(*(pin+1)) {
				case 'r' : pin++;break;
				case 'n': pin++;strcpy(pout,"\n"); pout++; break;

				case '\\': if(*(pin+2)==';' || *(pin+2)==':' || *(pin+2)==',')pin++; *pout++=*pin;break;
//				case '\\': 
				case ',': 
				case ';': 
				case ':':  pin++;*pout++=*pin;break;
				default:	
					*pout++=*pin;break;
			}
		}else{
			*pout++=*pin;
		}
	}
	*pout++='\0';
	return out;

}
char *vcard_decode_text(char *in,char *out)
{
	char *pin,*pout;
	int outbfsz=BFSZ-1;

	if(!in) return "";
	for(pin=in,pout=out;*pin;pin++) {
		if((pout-out)>=(outbfsz-6)) return in;
		if(*pin=='\\'){
			switch(*(pin+1)) {
				case 'r' : pin++;break;
				case 'n': pin++;strcpy(pout,"\n"); pout++; break;

				case '\\': if(*(pin+2)==';' || *(pin+2)==',')pin++; *pout++=*pin;break;
				case ',': 
				case ';': pin++;*pout++=*pin;break;
				default:	
					*pout++=*pin;break;
			}
		}else{
			*pout++=*pin;
		}
	}
	*pout++='\0';
	return out;

}
// opposite of vcard_decode
char *vcard_encode(char *in,char *out)
{
	char *pin,*pout;
	int outbfsz=BFSZ-1;

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

}
char *vcard_encode_text(char *in,char *out)
{
	char *pin,*pout;
	int outbfsz=BFSZ-1;

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

}

static char *vcard_encode_chars="#`\">< |{}[]%^";	// I _think_ this is the correct set of chars to be encoded
													// defintely not all chars used in lib_url_encode3. ( "#'`,$\\\"><+ &|/;:=?@{}~[]!%)(-_*^." )
char *vcard_url_encode(char *url, char *tmp)
{
	int len = BFSZ;
	char *out = tmp;
	int i;
	unsigned char *s = (unsigned char *) url;
	if (s==NULL) return "";
	for (i=0; *s ;i++,s++) {
		if (strchr(vcard_encode_chars,*s)!=NULL || *s>126 || *s<32) {
			*out++ = '%';
			sprintf(out,"%02X",*s);
			out += lib_strlen(out);
		} else *out++ = *s;
		if (out-tmp>BFSZ-10) break;
	}
	*out++ = 0;
    return tmp;
}

char *vcard_url_decode(char *in,char *out)
{
	return lib_decode_percent_clean(out, in, BFSZ);
}

char *vcard_eol_decode(char *bf, char *s) 
{
	int i,x;

	for(i = 0; i < BFSZ && *s; i++, s++) {
		if (*s == '&') {
			if (s[1] == '#') {
				x = 0;
				for(s += 2; *s && *s != ';'; s++) {
					if (sisdigit(*s)) x += *s - '0';
					x *= 10;
				}
				x /= 10;
				bf[i] = x;
			}
			else if (strncmp(s,"&amp;",5)==0) {
				bf[i] = '&';
				s += 4;
			}
			else bf[i] = *s;
		}
		else bf[i] = *s;
	}
	bf[i] = '\0';
	return bf;
}

