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

#include "vsnprintf.h"

#define IS_PROP 1

#include "thread.h"
#include "lib.h"
#include "dstr.h"
#include "prop.h"
#include "lprintf.h"
#define DMID 8
#include "dmalloc.h"
/*
Property 'load/save' functions

the 'property' file (filename.prop) 
contains info in this format:

Property:NAME\n 
(length in bytes)\n
<DATA>\n
Property:name\n
(length)
<DATA>\n


*/

Prop *prop_load(char *fname)
{
	FILE *f;
	Prop *plist=NULL;
	char *data;
	int len;
	int n;
	char name[BFSZ];
	char ns[BFSZ];
	char bf[BFSZ];
	f = fopen(fname,"rb");
	if (f==NULL) return NULL;
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		if (strncmp(bf,"Property:",9)!=0) continue;
		ncpy(name,bf+9,BFSZ-1);
		lib_strip_crlf(name);

		if (fgets(bf,BFSZ-1,f)==NULL) break;
		if (strncmp(bf,"NameSpace:",10)!=0) continue;
		ncpy(ns,bf+10,BFSZ-1);
		lib_strip_crlf(ns);

		if (fgets(bf,BFSZ-1,f)==NULL) break;
		len = atoi(bf);
		if (len<=0) continue;
		if (len>1000000) break; // Data is too big. 
		data = malloc(len+1);
		n = (int)fread(data,1,len,f);
		if (n!=len) {free(data); break;} // Unable to read all the data.
		data[n] = 0;
		prop_add(&plist,name,data,ns);
		free(data);
		// Blank line after data.
		if (fgets(bf,BFSZ-1,f)==NULL) break;
	}
	fclose(f);
	return plist;
}
int prop_save(char *fname, Prop *plist)
{
	Prop *p;
	FILE *f;
	
	f = fopen(fname,"wb");
	if (f==NULL) return FALSE;
	for (p=plist;p!=NULL;p=p->next) {
		fprintf(f,"Property:%s\n",p->var);
		fprintf(f,"NameSpace:%s\n",p->ns);
		fprintf(f,"%d\n",(int) lib_strlen(p->val));
		fwrite(p->val,1,(int) lib_strlen(p->val),f);
		fprintf(f,"\n\n");
	}
	fclose(f);
	return TRUE;
}



int prop_add(Prop **pp, char *var, char *val,char *ns)
{
	Prop *p;
	for (p=*pp;p!=NULL;p=p->next) {
		if (strcmp(ns,p->ns)==0) {
			if (strcmp(var,p->var)==0) {
					free(p->val);
					p->val = strdup(val);
			}
		}
	}
	p = malloc(sizeof(Pair));
	p->var = strdup(var);
	p->val = strdup(val);
	p->ns = strdup(ns);
	p->next = *pp;
	*pp = p;
	return TRUE;
}
int prop_free_one(Prop *x);
int prop_free(Prop **x)
{
	Prop *next;
	Prop *p;
	for (p=*x;p!=NULL;p=next) {
		next = p->next;
		prop_free_one(p);
	}
	*x = NULL;
	return TRUE;
}
int prop_free_one(Prop *p)
{
	free(p->ns);
	free(p->var);
	free(p->val);
	free(p);
	return TRUE;
}

int prop_remove(Prop **x, char *name, char *ns)
{
	Prop *next;
	Prop *p;
	Prop **last = x;
	for (p=*x;p!=NULL;p=next) {
		next = p->next;
		if (strcmp(p->ns,ns)==0) if (strcmp(p->var,name)==0) {
			prop_free_one(p);
			*last = next; 
			continue; 
		}
		last = &p->next; 
	}
	return TRUE;
}

/*   (principal | invert), (grant|deny), protected?,
                  inherited?)>
				  */

/*===============================================================================*/
// ** Coding ACL stuff in here too, since it's a small file and seems related....

/*
typedef struct {char *name; int value; } Apriv;
Aprive privs[]={
	"write",P_WRITE
	,"write-properties",P_WRITE_PROPERTIES
	,"write-content",P_WRITE_CONTENT
	,"write-acl",P_WRITE_ACL
	,"unlock",P_UNLOCK
	,"read",P_READ
	,"read-acl",P_READ_ACL
	,"read-current-user-privilege-set",P_READ_CURRENT_USER_PRIVILEGE_SET
	,"bind",P_BIND
	,"all",P_ALLPRIV
	,NULL,0
};
*/

char *acl_fname(char *fname, char *object)
{
	lprintf(fname,BFSZ-1,"%s.__acl__",object);
	return fname;
}
Ace *acl_load(char *fname)
{
	char bf[BFSZ];
	Ace *a;
	Ace *list=NULL;
	Ace **last = &list;
	char *p[100];
	int np;
	FILE *f; 
	f = fopen(fname,"rb");
	if (f==NULL) return NULL;
	for (;!feof(f);) {

		if (fgets(bf,BFSZ-1,f)==NULL) break;
		// fred@xyz.com|grant or deny|read,write
		np = token_split(bf,p,"|\r\n",10);
		if (np<3) continue;
		a = calloc(1,sizeof(Ace));
		a->user = strdup(p[0]);
		if (strcmpnc(p[1],"grant")==0) a->isgrant = TRUE;
		if (strcmpnc(p[1],"deny")==0) a->isdeny = TRUE;
		if (strcmpnc(p[1],"inherit")==0) a->isinherit = TRUE;
		a->priv = atoi(p[2]);
		*last = a; // tack it on the end of the list 
	}
	fclose(f);
	return list; 
}
int acl_free(Ace **list)
{
	Ace *next =NULL;
	Ace *a;
	for (a=*list;a!=NULL;a=next) {
		next = a->next;
		free(a->user);
		free(a);
	}
	*list=NULL;
	return TRUE;
}
int acl_write(char *fname, Ace *list);
int acl_write_object(char *object, Ace *list)
{
	char fname[BFSZ];
	acl_fname(fname,object);
	return acl_write(fname,list);
}
int acl_write(char *fname, Ace *list)
{
	Ace *a;
	FILE *f; 
	f = fopen(fname,"wb");
	if (f==NULL) return FALSE;
	
	for (a=list;a!=NULL;a=a->next) {
		fprintf(f,"%s|%s %s %s|%d\n",a->user,a->isgrant?"grant":"",a->isdeny?"deny":"",a->isinherit?"inherit":"",a->priv);
	}
	fclose(f);
	return TRUE; 
}
Dstr *acl_load_text(char *obj) // freed by calling function...
{
	char fname[BFSZ];
	Ace *list,*a;
	int i;
	Dstr *dst;
	dstr_new(&dst); dstr_nodisk(dst,TRUE);

	// Find 'related' file name for acl
	acl_fname(fname,obj);

	// Load list into acl list.
	list = acl_load(fname);
	
	// Format into 'text' for response.
	dstr_printf(dst,"<D:acl>\n");
	for (a = list;a!=NULL;a=a->next) {
		if (a->isinherit) { // or maybe load in acl_load... ? 
			// Read inherited url and display ace entries from it with inherited flag set... ? 
			continue;
		} 
		dstr_printf(dst,"<D:ace>\n");
		dstr_printf(dst,"<D:principal>\n");
		dstr_printf(dst,"<D:href>%s</D:href>\n",a->user);
		dstr_printf(dst,"</D:principal>\n");
		if (a->isgrant) {
			dstr_printf(dst,"<D:grant>\n");
			for (i=0; privs[i].name!=NULL; i++) {
				if ((a->priv & privs[i].value)>0) dstr_printf(dst,"<D:%s/>\n",privs[i].name);
			}
			dstr_printf(dst,"</D:grant>\n");
		}
		if (a->isdeny) {
			dstr_printf(dst,"<D:deny>\n");
			for (i=0; privs[i].name!=NULL; i++) {
				if ((a->priv & privs[i].value)>0) dstr_printf(dst,"<D:%s/>\n",privs[i].name);
			}
			dstr_printf(dst,"</D:deny>\n");
		}
		dstr_printf(dst,"</D:ace>\n");
	} 
	dstr_printf(dst,"<D:acl>\n");

	acl_free(&list);

	return FALSE;
}


char *acl_findname(int p)
{
	int i;
	for (i=0; privs[i].name!=NULL; i++) {
		if (p==privs[i].value) return privs[i].name;
	}
	return "PRIV_NOT_FOUND";
}
