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

#define DMID 14
#include "dmalloc.h"
#include "mailing.h"
#include "lprintf.h"
#include "lib.h"
#include "thread.h"

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE !FALSE
#endif

int  dlog(const char * arg_list, ...);
extern int dmsg(const char *arg_list, ...);
extern int emsg(const char * arg_list, ...);
extern int kmsg(const char * arg_list, ...);

/* RMH: to enable the lang_scan to find strings for translation */
/* later code is required to call the translation function on the string */
/* ONLY works if the string is hard-coded with no %s %d etc inserts */
#define lang_get(x,p) p

/* internal */
#define mailing_reason(reason,str,bfsz) if (reason) ncpy(reason,str,bfsz-1)

/* export */
int mailing_cmp(Mailing **m1, Mailing **m2) {
	return strcmp((*m1)->name,(*m2)->name);
}

/* export */
void mailing_free(Mailing **m) {
	if (!m || !*m) return ;
	pair_free(&(*m)->settings);
	free((*m)->name);
	free(*m);
}

/* internal */
int mailing_open(char *filename, char *mode, FILE **fp, char *reason, int bfsz) {
	FILE *f;
	char name2[BFSZ];
	int done=FALSE;

	*fp = 0;

again:
	f = fopen(filename,mode);
	
	if (!f && errno == ENOENT) {
		dlog("open failed, file doesn't exist\n");
		lib_msleep(1000);
		f = fopen(filename,mode);		
	}
	if (f) if ((lib_file_osize(f)<=0) && (done<3)) {
		done++;
		dlog("lists.dat file has zero size...\n");
		if (f) fclose(f);
		lib_msleep(500);
		goto again;
	}
	// if all else fails try and open the last file instead...
	if (!f || (lib_file_osize(f)<=0)) {
		if (f) fclose(f);
		lprintf(name2,BFSZ-1,"%s_1",filename);
//		f = fopen(filename,mode);		 chrisp fix, it was opening the non existent one again.
		f = fopen(name2,mode);		
		dlog("Using backup file %s\n",name2);
	}

	if (!f) {
		int x = errno;
		dlog("Failed to open file completely %s\n",strerror(x));
		mailing_reason(reason,strerror(x),bfsz);
		return FALSE;
	}

	*fp = f;

	return TRUE;
}
int mailing_lines(FILE *f) 
{
	char bf[BFSZ];
	int n=0;
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		n++;
	}
	fseek(f,0,SEEK_SET);
	return n;
}
	
/* internal */			 
int mailing_read(FILE *f, char *listname, Mailing **mp, char *reason, int bfsz) {
	int ret = TRUE;
	Mailing *m = NULL;
	char *label,*value;
	char bf[BFSZ];
	size_t upto;

	*mp = 0;

	while (TRUE) {
		upto = ftell(f);
		if (!fgets(bf,BFSZ,f)) break;

		for(label = bf; sisspace(*label); label++);
		if (!*label) continue;

		for(value = label; *value && !sisspace(*value); value++);
		for(*value++ = 0; sisspace(*value); value++);
		strip_eol(value);

		if (strcmp(label,"list")==0) {
			if (m) {
				fseek(f,(int)upto,SEEK_SET);
				break;
			}

			dmsg("mailing_read: list(%s) match(%s)",value,listname?listname:"*");

			if (listname && strcmp(value,listname) != 0)
				continue;
			
			m = calloc(1,sizeof(Mailing));
			m->name = strdup(value);
		}
		else {
			if (!m) continue;
			dmsg("mailing_read: setting(%s) value(%s)",label,value);
			pair_add(&m->settings,label,value);
			continue;
		}

	}

	if (ferror(f)) {

		if (m) mailing_free(&m);
		mailing_reason(reason,strerror(errno),bfsz);
		ret = FALSE;
	}
	else if (!m) {
		ncpy(reason,lang_get(1,"List not found"),bfsz-1);
		ret = FALSE;
	}
	else {
		*mp = m;
	}

	return ret;
}

/* internal */
int mailing_write(FILE *f, Mailing *m, char *reason, int bfsz) {
	Pair *p;

	dmsg("mailing_write: list(%s)",m->name);
	if (fprintf(f,"list %s\n",m->name) < 0) goto err;

	for(p = m->settings; p; p = p->next) {		
		if (strcmp(p->val,"false")==0 || !p->val[0]) {
			dmsg("mailing_write: skipping setting(%s) value(%s)",p->var,p->val);
			continue;
		}
		dmsg("mailing_write: setting(%s) value(%s)",p->var,p->val);
		if (fprintf(f,"  %s %s\n",p->var,p->val) < 0) goto err;
	}

	return TRUE;
err:
	mailing_reason(reason,strerror(errno),bfsz);
	return FALSE;
}

/* export */
Array *mailing_lists(char *file, char *reason, int bfsz, int *nfound) {
	Array *list = NULL;
	Mailing *m;
	FILE *f;
	int nlists=0;
	*nfound = 0;

	if (!file) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return NULL; }
	if (reason) *reason = 0;

	list = array_new(100);
	array_setup(list,(int (*)(void **, void **))mailing_cmp,(void (*)(void **))mailing_free);

	if (!mailing_open(file,"rb",&f,reason,bfsz)) return list;

	while(mailing_read(f,NULL,&m,reason,bfsz)) {
//printf("Reading mailing list (%s) %d\n",m->name,nlists);
		array_add(list,m);
		nlists++;
	}


	fclose(f);
	if (nlists==0) emsg("Warning: read zero lists from %s\n",file);
	*nfound = nlists;
	return list;
}

/* export */
int mailing_settings(char *listname, char *file, Pair **listp, char *reason, int bfsz) {
	int ret = FALSE;
	Mailing *m;
	FILE *f;

	if (!listname) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }	
	if (!listp) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }
	if (!file) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }
	if (reason) *reason = 0;
	*listp = 0;

	if (!mailing_open(file,"rb",&f,reason,bfsz)) return FALSE;

	if (mailing_read(f,listname,&m,reason,bfsz)) {
		*listp = m->settings;
		m->settings = NULL;
		mailing_free(&m);		
		ret = TRUE;
	}

	fclose(f);

	return ret;
}

/* export */
int mailing_save(Mailing *newm, char *file, char *reason, int bfsz) {
	int ret = TRUE;
	Array *list = NULL;
	char tmp[BFSZ];
	Mailing *m;
	FILE *fs;
	FILE *fd;

	if (!newm) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }
	if (!file) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }
	if (reason) *reason = 0;

	if (!mailing_open(file,"rb",&fs,reason,bfsz)) {
		if (errno != ENOENT) return FALSE;
	}

	lprintf(tmp,BFSZ,"%s.tmp",file);
	lib_create_dir(tmp,0700);
	fd = fopen(tmp,"w");
	if (!fd) {
		mailing_reason(reason,strerror(errno),bfsz);
		fclose(fs);
		return FALSE;
	}

	if (fs) {
		for(; ret && mailing_read(fs,NULL,&m,reason,bfsz); mailing_free(&m)) {
			if (mailing_cmp(&m,&newm)==0) continue;
			ret = mailing_write(fd,m,reason,bfsz);
		}
		fclose(fs);
	}

	ret = mailing_write(fd,newm,reason,bfsz);
	fclose(fd);

	if (ret) ret = remove_rename(file,tmp,reason,bfsz);
	else remove(tmp);

	dlog("Mailing list write %s:%d\n",__FILE__,__LINE__);
	return ret;
}

/* export */
int mailing_save_list(Array *lists, char *file, int replace, char *reason, int bfsz, char *whence) {
	FILE *fs = NULL;
	int ret = TRUE;
	char tmp[BFSZ];
	Mailing *m;	
	FILE *fd;
	int i;
	int nlist=0;

	if (!lists) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }
	if (!file) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }
	if (reason) *reason = 0;

	emsg("netauth: save mailing lists to (%s)\n",file);
	if (!replace) {
		if (!mailing_open(file,"rb",&fs,reason,bfsz)) {
			if (errno != ENOENT) {
				strcpy(reason,"File open failed");
				return FALSE;
			}
		}
	}

	lprintf(tmp,BFSZ,"%s.tmp_%d",file,th_id());
	lib_create_dir(tmp,0700);
	fd = fopen(tmp,"w");
	if (!fd) {
		mailing_reason(reason,strerror(errno),bfsz);
		if (fs) fclose(fs);
		return FALSE;
	}

	if (fs) { 
		for(; ret && mailing_read(fs,NULL,&m,reason,bfsz); mailing_free(&m)) {
			if (array_find(lists,m) != -1) continue;
			ret = mailing_write(fd,m,reason,bfsz);
			nlist++;
		}
		fclose(fs);
	}

	dlog("Mailing list write %s:%d\n",__FILE__,__LINE__);

	for(i = 0; ret && (m = (Mailing *)array_get(lists,i)); i++) {
		if (m->deleted) {
			dlog("mailing_save_list - removed %s\n",m->name);
			continue;
		}
		nlist++;
		ret = mailing_write(fd,m,reason,bfsz);
	}

	fclose(fd);
	dlog("mailing_save_list finished, wrote %d lists, %s, lists.dat size=%d\n",nlist,whence,lib_file_size(tmp));
	if (ret) {
		// Lets rename to a 'daily' backup of lists.dat .... (chrisp) 
		char file_day[BFSZ];		
		lprintf(file_day,BFSZ-1,"%s_%d",file,lib_mday() % 20);
		remove(file_day);
		rename(file,file_day);
	}

	if (ret) ret = remove_rename(file,tmp,reason,bfsz);
	else remove(tmp);

	return ret;
}

/* export */
int mailing_save_settings(Pair *settings, char *listname, char *file, char *reason, int bfsz) {
	int ret = TRUE;
	char tmp[BFSZ];
	int failed=FALSE;
	int nlists=0;
	Mailing *m;
	FILE *fs;
	FILE *fd;

	if (!settings) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }
	if (!listname) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }
	if (!file) { mailing_reason(reason,lang_get(1,"NULL param"),bfsz); return FALSE; }
	if (reason) *reason = 0;

	if (!mailing_open(file,"rb",&fs,reason,bfsz)) return FALSE;

	lprintf(tmp,BFSZ,"%s.tmp_%d",file,th_id());
	fd = fopen(tmp,"w");
	if (!fd) {
		mailing_reason(reason,strerror(errno),bfsz);
		fclose(fs);
		return FALSE;
	}

	for(; ret && mailing_read(fs,NULL,&m,reason,bfsz); mailing_free(&m)) {
		if (strcmp(m->name,listname)==0) continue;
		ret = mailing_write(fd,m,reason,bfsz);
		nlists++;
	}
	fclose(fs);

	m = calloc(1,sizeof(Mailing));
	m->name = strdup(listname);
	m->settings = settings;
	
	ret = mailing_write(fd,m,reason,bfsz);
	nlists++;
	
	m->settings = NULL;
	mailing_free(&m);
	dlog("save_settings %s:%d lists.dat nlists=%d size=%d %s\n",__FILE__,__LINE__,nlists,lib_file_size(file),file);

	if (fclose(fd)!=0) failed=TRUE;
	if (failed) ret=FALSE;

	if (ret) ret = remove_rename(file,tmp,reason,bfsz);
	else remove(tmp);

	return ret;
}

int mailing_match_moderator(Pair *settings, char *match, int primary)
{
	char tmp[BFSZ];
	char *pp[50];
	int i,np;

	ncpy(tmp,pair_find(settings,"moderator"),BFSZ-1);
	np = token_split(tmp,pp," \t,\r\n",50);
	for(i = 0; i < np; i++) {
		if (match_wild(match,pp[i]))
			return TRUE;
		if (primary) break; //only matches 1st moderator
	}
	return FALSE;
}
