/*
Check for memory leaks bugs
	re-lint it

--------------------
dnewsweb - Add features to deal with it (Add to group,etc)

	qread
  
    Start with 'pull.c source'
	Read a queue file
	Send an item out.
---------------------------------------------
Scan for these when first deciding to send to mail gateway.
		if (matches(s,"X-Gateway-From:")) goto abortit;
		if (matches(s,"Message-ID: <fake@netwin")) goto abortit;
	
	  - Implement tomail.c
		news-mail - mail-news, simpler gateway.
		mail - dnewsweb - subscribe to group via email
		mail - dnewsweb - subscribe to thread via email
		tomail.conf
			send groups=x.y to=me@here thread=<asdf> subject="asdf"

	- Implement pulling as wel		Fetches mail from a pop host.
			pullmail.conf
				pull groups=rec.humor*  pophost=x.y.z user=xxx pass=yyy
				....

	- Auto reload on 'change' of both files.

--------------------------  
    
      nntp command:
	xtomail add group=group.name to=email.address [thread=<xxx@xxx.com>] [subject=this+subject]

group=test.1 to=chrisp@netwin.co.nz sender=sender@here.com
groups="pdx.*" to=@list.server dot_to_dash strip=pdx.

    xtomail del id=n to=email.address sender=x@y
	xtomail show to=email.address
	id	group.name	to	thread=xxx	subject=yyy...
In config directory
	tomail.conf
		Fixed 'xmailme' rules.
	
Limit per user-to address, bytes and items per day 
	Just use fixed limits. 
Security, how do we know they are allowed to do this? 
	- require that the user be logged in and record the real username as well as the email
	address, optionally require the to address contain the real username. 

	Write changes to disk, re-write existing rules occasionally.
	Store in a linked list, one per group.
		When a message arrives in a group, scan list and apply each rule, if matches and not limited.
		If limit reached send message saying so.
		then append to 'queue' file list of emails to send.
	In background, read queue file,
		Grab message body - cache it
		For each user who wants it generate a unique email message with
		url to remove them from this list etc. (use template)
		Send it.		
	Clear limits at midnight.


*/




#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "emsg.h"
#include "str.h"
#include "cmd.h"
#include "sary.h"
#include "group.h"
#include "dnews.h"
#include "lib.h"
#include "item.h"
#include "chan.h"
#include "hist.h"
#include "expire.h"
#include "feed.h"
#include "post.h"
#include "suck.h"
#include "chan_dmy.h"
#include "db.h"

#include "tomail.h"
#undef mymalloc
#undef myfree
#define mymalloc(x) mymalloc2(x,M_TOMAIL)
#define myfree(x) myfree2(x,M_TOMAIL)

int tomail_waiting(void);

#ifdef OS2
#define vsnprintf _vbprintf
#endif




typedef struct ULIST {char *to; int ref; int id; struct ULIST *next; } Ulist;
typedef struct QENTRY {
	time_t tqued,tsent;
	int ntries,num;
	char gname[BFSZ];
	char toemail[BFSZ];
	char cheat[BFSZ];
	char status[BFSZ];
	char addhead[BFSZ];
	char from[BFSZ]; char sender[BFSZ];
} Qentry;
typedef struct LINK {
	int chan,state,donehelo;
	time_t lastdata;
	Qentry q;
	String *xhead,*xbody;
	char lastref[BFSZ];
} Link;
typedef struct TORULE {
	int grp; char *toemail; char *subj; char *mid; char *uname; char *sender; char *from; char *addhead;
	int items,bytes;
	int xtime;
	int isnntp;
	Ulist *u;	
	struct TORULE *next;
} Torule;

static int status_nrules,status_nfeed,status_senttry,status_sentok,time_started;


void tomail_rewrite(void);
int tomail_qread_data(Link *l);
int tomail_nntp_add(char *uname, char *gname, char *toemail, char *subject, char *thread, String *result);
int tomail_nntp_del(char *gname, char *toemail, String *result);
int tomail_nntp_show(char *gname, char *toemail, String *result);
void tomail_openseek(void);
int tomail_readnew(void);
char *tomail_host(void);
int tomail_start(void);
void tomail_clear_limits(void);
void tomail_free(Torule *t);
int tomail_inlist(Torule *rlist, char *uname,int grp,char *tomail,char *subj, char *thread) ;
int strcmp_n(char *a, char *b);

int tomail_qread(Link *l);
int tomail_link_close(Link *l);
int tomail_data_chan(int chan, char *data, int len);
void tomail_send_message(Link *l);
void tomail_data(Link *l, char *line);
int tomail_write(Link *l,char *arg_list, ...);
int tomail_close_link(Link *l, char *reason);
void tomail_closeadd(void);
void tomail_config_line(char *xin);
int tomail_rule_add(char *uname,char *gname, char *to, char *subj, char *mid, int isnntp, char *from, char *sender, char *addhead);
Ulist *tomail_user_findadd(char *to);
int tomail_freelist(Torule **rlist);
int tomail_dellist(Torule **rlist, int grp, char *tomail) ;
int tomail_addlist(Torule **rlist, char *uname, int xtime, int grp, char *tomail, char *subj, char *thread);
void tomail_rule_reset(void);
int tomail_rule_exists(char *uname, char *gname, char *to, char *subj, char *mid);


static Link lnk;

enum {P_CLOSED,P_OPENING, P_HELO, P_FROM, P_TO, P_DATA, P_DATA_SEND, P_DATA_SENT, P_RSET};

#define TOMAIL_SEND file_work("tomail.send")
#define TOMAIL_ADD file_work("tomail.add")
#define TOMAIL_FAIL file_work("tomail.fail")
#define TOMAIL_UPTO file_work("tomail.upto")

/*
File format:

timeadded timeretry ntries toemail group.name:item:cheatinfo # Reason for last failure

*/
void tomail_read_config(void);
int tomail_reset_readnew(void);

void tomail_init(void)
{
	static int done;
	if (!done) {
		tomail_rewrite();
		time_started = lib_gmtime();
	}
	done = TRUE;

	lnk.chan = -1;
	lnk.state = 0;
	imsg("tomail_init called, rules reset %d\n",status_nrules);
	tomail_rule_reset();
	tomail_reset_readnew();
	tomail_read_config();
	imsg("tomail_init called, rules reset, config read %d\n",status_nrules);
}
static int cur_date;
void tomail_read_config(void)
{
	FILE *f;
	char bf[BFSZ];
	char *s;
	cur_date = lib_cdate(file_config("tomail.conf"));
	f = fopen(file_config("tomail.conf"),"r");
	if (f==NULL) {
		imsg("No tomail.conf file, this is ok (%s)\n",file_config("tomail.conf"));
		return;
	}
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		s = strchr(bf,'\n'); if (s!=NULL) *s = 0;
		tomail_config_line(bf);
	}
	fclosep(f);
}
void tomail_config_line(char *xin)
{
	char *p[100];
	int n;
	int i;
	int touser = FALSE;
	int todash = FALSE;
	char gwild[BFSZ];
	char bf[BFSZ];
	char xtoemail[BFSZ];
	char strip[BFSZ];
	char gname[BFSZ],toemail[BFSZ],subj[BFSZ],mid[BFSZ],xsender[BFSZ],xfrom[BFSZ];
	char addhead[BFSZ];

	strcpy(strip,"");
	strcpy(addhead,"");
	strcpy(gwild,"");
	strcpy(gname,"");
	strcpy(subj,"");
	strcpy(xsender,"");
	strcpy(xfrom,"");
	strcpy(toemail,"");
	strcpy(mid,"");
	n = token_quote(xin,p,99);

	if (n==0) return;
	if (strncmp(p[0],"#",1)==0) return; 
	for (i=0; i<n; i++) {
		if (strcmp(p[i],"group")==0) {
			if (i+2>=n) {emsg("Parameters missing in tomail.conf %s\n",p[i]); return;}
			i++;
			ncpy(gname,p[++i],BFSZ-1); 
		} else if (strcmp(p[i],"groups")==0) {
			if (i+2>=n) {emsg("Parameters missing in tomail.conf %s\n",p[i]); return;}
			i++;
			ncpy(gname,p[++i],BFSZ-1); 
		} else if (strcmp(p[i],"strip")==0) {
			if (i+2>=n) {emsg("Parameters missing in tomail.conf %s\n",p[i]); return;}
			i++;
			ncpy(strip,p[++i],BFSZ-1); 
		} else if (strcmp(p[i],"todash")==0) {
			todash = TRUE;
		} else if (strcmp(p[i],"touser")==0) {
			touser = TRUE;
		} else if (strcmp(p[i],"to")==0) {
			if (i+2>=n) {emsg("Parameters missing in tomail.conf %s\n",p[i]); return;}
			i++;
			ncpy(toemail,p[++i],BFSZ-1); 
		} else if (strcmp(p[i],"sender")==0) {
			if (i+2>=n) {emsg("Parameters missing in tomail.conf %s\n",p[i]); return;}
			i++;
			ncpy(xsender,p[++i],BFSZ-1); 
		} else if (strcmp(p[i],"addhead")==0) {
			if (i+2>=n) {emsg("Parameters missing in tomail.conf %s\n",p[i]); return;}
			i++;
			ncpy(addhead,p[++i],BFSZ-1); 
		} else if (strcmp(p[i],"from")==0) {
			if (i+2>=n) {emsg("Parameters missing in tomail.conf %s\n",p[i]); return;}
			i++;
			ncpy(xfrom,p[++i],BFSZ-1); 
		} else if (strcmp(p[i],"thread")==0) {
			if (i+2>=n) {emsg("Parameters missing in tomail.conf %s\n",p[i]); return;}
			i++;
			ncpy(mid,p[++i],BFSZ-1); 
		} else if (strcmp(p[i],"subject")==0) {
			if (i+2>=n) {emsg("Parameters missing in tomail.conf %s\n",p[i]); return;}
			i++;
			ncpy(subj,p[++i],BFSZ-1); 
		} else {
			emsg("Unknown setting in tomail.conf (%s)\n",p[i]);
		}
	}
	if (strchr(gname,'*')!=NULL) {
		strcpy(gwild,gname);
		strcpy(gname,"");
	}
	if (toemail[0]!='@') touser = TRUE;
	if (strlen(gwild)>0) {
		for (i=0; i<group_n(); i++) {
	            if (group_match(gwild,group_namelc(i))) {
			ncpy(bf,group_namelc(i),200);
			if (strlen(strip)>0) {
				if (strncmp(bf,strip,strlen(strip))==0) {
					memmove(bf,bf+strlen(strip),strlen(bf)-strlen(strip)+1);
				}
			}
			if (todash) str_todash(bf);
			lprintf(xtoemail,BFSZ-1,"%.200s%.200s",bf,toemail);
			if (touser) ncpy(xtoemail,toemail,BFSZ-1);
			tomail_rule_add("nobody",group_namelc(i),xtoemail,subj,mid,FALSE,xsender,xfrom,addhead);
		    }
		}
	} else {
		if (strlen(toemail)>0) if (strlen(gname)>0) tomail_rule_add("nobody",gname,toemail,subj,mid,FALSE,xsender,xfrom,addhead);
	}
}
char *str_todash(char *s)
{
	char *orig=s;
	for (;*s!=0;s++) {
		if (*s=='.') *s = '-';

	}
	return orig;
}
char *str_todot(char *s)
{
	char *orig=s;
	for (;*s!=0;s++) {
		if (*s=='-') *s = '.';
	}
	return orig;
}
/*	

groups="pdx.*" to=@list.server dot_to_dash strip=pdx.

tomail group=group.name to=email.address [thread=<xxx@xxx.com>] [subject=this+subject]*/
void tomail_idle(void)
{
	Link *l=&lnk;
	static int last;
	static int lastday;
	int t = lib_gmtime();
	int xnow;

	if (config_true(C_DISKIO_MIN)) return;
	if (is_slave()) return;
	if (!config_true(C_TOMAIL_RUN)) if (is_readonly()) return;
	tomail_readnew();

	if (l->state==P_CLOSED) {
		xnow = lib_cdate(file_config("tomail.conf"));
		if (xnow!=cur_date) {
			imsg("tomail: Reload as tomail.conf date changed\n");
			tomail_init();
		}
		if (tomail_waiting()) {
			imsg("tomail: tomail_start called\n");
			tomail_start();
		}
		return;
	}
	if (t - l->lastdata>300) {
		tomail_close_link(l,"No data received on connection for 5 minutes");
	}
	if ((t-lastday)>(60*60*24)) {
		imsg("tomail: Aging tomail user limits\n");		lastday = t;
		tomail_clear_limits();
	}
	dmsg("tomail: Idle state %d\n",l->state);
}
int tomail_close_link(Link *l, char *reason)
{
	if (l->chan>=0) chan_close(l->chan);
	l->chan = -1;
	l->state = 0;
	return TRUE;
}
int tomail_waiting(void)
{
	static time_t last;
	time_t t;
	char bf[BFSZ];
	FILE *f;
	t=time(NULL);
	if (t-last<10) return FALSE;
	last = t;

	tomail_closeadd();
	f = fopen(TOMAIL_SEND,"rb");
	if (f==NULL) {
		dmsg("tomail: waiting1 %p %s\n",f,strerror(errno));
		strcpy(bf,TOMAIL_SEND);
		if (errno==ENOENT) remove(TOMAIL_UPTO);
		if (rename(TOMAIL_ADD,bf)!=0) if (errno!=ENOENT) imsg("tomail: rename failed %s %s\n",bf,strerror(errno));
		f = fopen(TOMAIL_SEND,"rb");
	}
	dmsg("tomail: waiting2 %p %s\n",f,strerror(errno));
	if (f==NULL) return FALSE;
	fclosep(f);
	return TRUE;
}
static FILE *fsend,*fadd;
void tomail_closeadd(void)
{
	if (fadd!=NULL) fclosep(fadd);
	fadd = NULL;
}
void tomail_que_add(Torule *r, char *cheat)
{
	char ff[BFSZ];
	char ss[BFSZ];
	char addhead[BFSZ];
	time_t t = time(NULL);
	if (fadd==NULL) {
		fadd = fopen(TOMAIL_ADD,"ab");
		if (fadd==NULL) {filemsg("Cannot write %s\n",TOMAIL_ADD); return;}
	}
	strcpy(ff,"NULL"); 
	strcpy(ss,"NULL"); 
	strcpy(addhead,"NULL"); 
	if (r->from!=NULL) ncpy(ff,r->from,BFSZ-1);
	if (r->sender!=NULL) ncpy(ss,r->sender,BFSZ-1);
	if (r->addhead!=NULL) ncpy(addhead,r->addhead,BFSZ-1);

	imsg("tomail: Queued entry to user %s %s\n",cheat,r->toemail);
	fprintf(fadd,"%d\t%d\t%d\t%s\t%s\t%s\t%s\tNewly Qued\t%s\n",t,t,0,r->toemail,ff ,ss,cheat,addhead);
}
void tomail_openseek(void)
{
	FILE *fupto;
	char bf[BFSZ];
	if (fsend!=NULL) {emsg("fsend is not null\n"); abort();}
	fsend = fopen(TOMAIL_SEND,"rb");
	if (fsend==NULL) return;
	fupto = fopen(TOMAIL_UPTO,"r");
	if (fupto==NULL) {
		return;
	}
	fgets(bf,100,fupto);
	fclosep(fupto);
	fseek(fsend,atoi(bf),SEEK_SET);
	return ;
}
char *tomail_host(void)
{
	static char bf[BFSZ];
	lprintf(bf,BFSZ-1,"%s:25",feed_smtpname());
	return bf;
}
int tomail_start(void)
{
	Link *l=&lnk;
	static int lastopen;

	if (fsend!=NULL) fclosep(fsend); fsend = NULL;
	if (lib_gmtime()-lastopen<20) return TRUE;
	lastopen = lib_gmtime();
	tomail_openseek();
	if (fsend==NULL) return TRUE;
	if (tomail_qread(l)) {
		if (l->state==P_CLOSED) {
			l->chan = chan_open(tomail_host());
			imsg("tomail: Opening connection to (%s)\n",tomail_host());
			if (l->chan>=0) {
				chan_setfn(l->chan,tomail_data_chan);
				l->state = P_OPENING;
			} else imsg("tomail: Cannot open SMTP link to %s\n",tomail_host());
		} else {
			emsg("tomail: unknown state for start call %d\n",l->state);
		}
	}
	return TRUE;
}
void tomail_link_next(Link *l)
{
	int loop=0;
	static char lastcheat[BFSZ];
	static char last[BFSZ];
again:
	loop++;
	if (tomail_qread(l)) {
		imsg("tomail_link_next, read item %s %s\n",l->q.toemail,l->q.cheat);
		if (strcmp(last,l->q.toemail)==0) if (strcmp(lastcheat,l->q.cheat)==0) {
			imsg("tomail: DUPLICATE ENTRY, TRYING NEXT ONE\n");
			if (loop>1000000) dnews_ex("tomail fatal loop");
			goto again;
		}
		ncpy(last,l->q.toemail,BFSZ-1);
		ncpy(lastcheat,l->q.cheat,BFSZ-1);
		l->state = P_FROM;
		tomail_data(l,"none");
	} else {
		imsg("tomail: Nothing waiting, so closing link\n");
		tomail_link_close(l);
	}
}


/*timeadded timeretry ntries toemail group.name:item:cheatinfo # Reason for last failure*/
int tomail_write_upto(int x)
{
	FILE *f;
	f = fopen(TOMAIL_UPTO,"w");
	if (f!=NULL) {
		fprintf(f,"%d\n",x);
		fclose(f);
	}
	return TRUE;
}
int tomail_qread(Link *l)
{
	char bf[BFSZ];
	FILE *f=fsend;
	char *s;
	if (fsend==NULL) return FALSE;
	for (;!feof(f);) {
		tomail_write_upto(ftell(f));
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		s = strtok(bf,"\r\n\t"); if (s==NULL) continue;
		l->q.tqued = atoi(s);
		s = strtok(NULL,"\r\n\t"); if (s==NULL) continue;
		l->q.tsent = atoi(s);
		s = strtok(NULL,"\r\n\t"); if (s==NULL) continue;
		l->q.ntries = atoi(s);
		s = strtok(NULL,"\r\n\t"); if (s==NULL) continue;
		ncpy(l->q.toemail,s,BFSZ-1);
		s = strtok(NULL,"\r\n\t"); if (s==NULL) continue;
		ncpy(l->q.from,s,BFSZ-1);
		s = strtok(NULL,"\r\n\t"); if (s==NULL) continue;
		ncpy(l->q.sender,s,BFSZ-1);
		s = strtok(NULL,"\r\n\t"); if (s==NULL) continue;
		ncpy(l->q.gname,s,BFSZ-1);
		ncpy(l->q.cheat,s,BFSZ-1);
		s = strchr(l->q.gname,':'); if (s==NULL) continue; 
		*s = 0;
		l->q.num = atoi(s+1);
		s = strtok(NULL,"\r\n\t"); if (s==NULL) continue;
		ncpy(l->q.status,s,BFSZ-1);
		s = strtok(NULL,"\r\n\t"); 
		strcpy(l->q.addhead,"");
		if (s!=NULL) ncpy(l->q.addhead,s,BFSZ-1);
		if (tomail_qread_data(l)) {
			return TRUE;
		}
	}
	fclosep(fsend); fsend = NULL;
	if (remove(TOMAIL_SEND)!=0) imsg("Unable to remove %s %s\n",TOMAIL_SEND,strerror(errno));
	if (remove(TOMAIL_UPTO)!=0) imsg("Unable to remove %s %s\n",TOMAIL_SEND,strerror(errno));
	return FALSE; 
}
int tomail_qread_data(Link *l)
{
	/* Read the head and body into memory */
	String *x;
	static String *lasthead,*lastbody;
	static int lastg= -1,lastn;
	int g;
	if (lasthead==NULL) lasthead = str_new();
	if (lastbody==NULL) lastbody = str_new();
	g = group_find(l->q.gname);
	if (g<0) return FALSE;
	if ((lastg!=g) || (lastn!=l->q.num)) {
		x = item_head_cheat(g,l->q.num,l->q.cheat);
		if (x==NULL) return FALSE;
		str_copy(lasthead,x);
	        x = item_body_cheat(g,l->q.num,l->q.cheat);
		if (x==NULL) return FALSE;
		str_copy(lastbody,x);
	}
	lastg = g;
	lastn = l->q.num;
	l->xhead = lasthead;
	l->xbody = lastbody;
	return TRUE;
}
int tomail_link_close(Link *l)
{
	if (l->chan<0) return FALSE;
	if (l->chan>=MAX_CHAN) return FALSE;
	imsg("tomail: Closing link\n");
	chan_close(l->chan);
	l->chan = -1;
	l->state = P_CLOSED;
	return TRUE;
}

int tomail_data_chan(int chan, char *data, int len)
{
	Link *l=&lnk;
	static int in;
	if ((chan<0)||(chan>=MAX_CHAN)) return TRUE;
	l->lastdata = lib_gmtime();
	if (len<0) {
		if (in>0) return TRUE;
		in=1;
		emsg("tomail: Channel closed s=%d\n",l->state);
		if (l->chan>=0)  {
			tomail_link_close(l);
		}
		in = 0;
		return TRUE;
	}
	l->lastdata = lib_gmtime();
	data[len] = 0;
	for (;len>0;len--) {
		if (data[len-1]!='\n') if (data[len-1]!='\r') break;
		len--;
		data[len] = 0;
	}
	if (len>200) data[len] = 0;
	tomail_data(l,data);
	return TRUE;
}
char *tomail_from(Qentry *q)
{
	static char bf[BFSZ];
	if (strcmp(q->from,"NULL")!=0) return q->from;
	strcpy(bf,config_get(C_TOMAIL_FROM));
	if (strlen(bf)==0) lprintf(bf,BFSZ-1,"dnews-gateway@%s",config_get(C_MYNAME));
	return bf;
}
char *str_line_next(String *xstr, int *xat)
{
	static char bf[BFSZ];
	char *start,*s;
	int len;
	if (*xat>=str_len(xstr)) return NULL;
	start = s = str_s(xstr) + *xat;	
	if (*s==0) return NULL;
	for (;*s!=0;s++) {
		if (*s=='\n') {
			len = s - start - 1;
			if (len>0) if (s[-1]!='\r') len++; /* Don't strip non cr char. */
			if (len<0) len = 0;
			if (len>=BFSZ) len=BFSZ-1;
			ncpy(bf,start,len);
			*xat = s - str_s(xstr) + 1;
			return bf;
		}
	}
	return NULL;
}
#define matches(a,b) (strncmp(a,b,strlen(b))==0) 
int tomail_link_next_rset(Link *l);
void tomail_send_message(Link *l)
{
	/*
		Munge headers and send them,
		Then send body
	*/
	char bf[BFSZ];
	char subject[BFSZ];
	char tmp2[BFSZ];
	char newsgroups[BFSZ];
	char from[BFSZ];
	int xat;
	int j=0;
	char *s,*tmp;
	int gobble = FALSE;
	int strip_mime;
	int article=0;
	char ref[BFSZ];
	char newfrom[BFSZ];
	char newreply[BFSZ];

	strcpy(newreply,"");
	strip_mime = 	config_true(C_TOMAIL_STRIP_BODY);
	ncpy(newfrom,l->q.from,BFSZ-1);	
	if (strlen(config_get(C_TOMAIL_FROM_FORMAT))>0) {
		lprintf(newfrom,200,config_get(C_TOMAIL_FROM_FORMAT),"newsgroupname");
	}
	strcpy(ref,"");
	strcpy(from,"");
	strcpy(subject,"");
	strcpy(newsgroups,"");
	if (l->xhead==NULL) {
		emsg("cannot happen, link head null\n");
		tomail_link_next(l);
		return;
	}
	xat = 0;
	s = str_line_next(l->xhead,&xat);
	for (;s!=NULL;s=str_line_next(l->xhead,&xat)) {
		if (gobble) if ((*s==' ') || (*s=='\t')) {
			goto skip_line;
		}
		gobble = FALSE;
		if (matches(s,"From:")) ncpy(from,s+5,100);
		if (matches(s,"Newsgroups:")) ncpy(newsgroups,s+11,100);
		if (matches(s,"Reply-to:")) goto skip_line;
		if (matches(s,"Reply-To:")) goto skip_line;
		if (strip_mime) {
			if (matches(s,"MIME-Version:")) goto skip_line;
			if (matches(s,"Content-Type:")) goto skip_line;
			if (matches(s,"Content-type:")) goto skip_line;
		}
		
		if (matches(s,"Path:")) goto skip_line;
		if (matches(s,"Xref:")) {
			ncpy(ref,s,BFSZ-1);
			goto skip_line;
		}
		if (matches(s,"Reply-To: ")) {
			tomail_write(l,"X-Originally-%.300s\r\n",s);
			ncpy(newreply,s+10,BFSZ-1);
			goto skip_line;
		}
		if (matches(s,"To:")) {
			tomail_write(l,"X-Originally-%.300s\r\n",s);
			goto skip_line;
		}
		if (matches(s,"From:")) {
			if ((strcmp(newfrom,"NULL")!=0)) {
				tomail_write(l,"X-Originally-%.300s\r\n",s);
				goto skip_line;
			}
		}
		if (matches(s,"Subject:")) {
			ncpy(subject,s+8,200);
			if (config_true(C_TOMAIL_SUBJECT_ADD)) goto skip_line;
		}
		if (matches(s,"Sender:")) {
			if (strcmp(l->q.sender,"NULL")!=0) {
				tomail_write(l,"X-Originally-%.300s\r\n",s);
				goto skip_line;
			}
		}
		for (;strlen(s) > 380; ) {
			char *a;
			for (a=s+300;*a!=0;a++) {
				if (*a==' ') {
					*a = 0;
					tomail_write(l,"%.400s\r\n",s);
					*a = ' ';
					s = a;
					goto next_line;
				}				
			}
			break;
next_line:;
		}
		tomail_write(l,"%.400s\r\n",s);
		continue;
skip_line:;
		gobble = TRUE;
	}
	if (strlen(config_get(C_TOMAIL_FROM_FORMAT))>0) {
		ncpy(tmp2,newsgroups,BFSZ-1);
		s = strchr(tmp2,','); if (s!=NULL) *s = 0;
		strlwr(tmp2);
		lprintf(newfrom,200,config_get(C_TOMAIL_FROM_FORMAT),tmp2);
	}
	if (strlen(config_get(C_TOMAIL_REPLY_FORMAT))>0) {
		ncpy(tmp2,newsgroups,BFSZ-1);
		s = strchr(tmp2,','); if (s!=NULL) *s = 0;
		strlwr(tmp2);
		lprintf(newreply,200,config_get(C_TOMAIL_REPLY_FORMAT),tmp2);
	}
	if (strcmp(l->q.sender,"NULL")!=0) tomail_write(l,"Sender: %.200s\r\n",l->q.sender);
	if (config_true(C_TOMAIL_FROM_USER)) {
    	    tomail_write(l,"From: %.300s\r\n",from);
	} else {
    	    if (strcmp(newfrom,"NULL")!=0) tomail_write(l,"From: %.300s\r\n",newfrom);
	}
	if (strcmp(l->q.addhead,"NULL")!=0) tomail_write(l,"%.300s\r\n",l->q.addhead);
	if (strlen(newreply)>0) tomail_write(l,"Reply-to: %.300s\r\n",newreply);
	tomail_write(l,"To: %.300s\r\n",l->q.toemail);
	if (config_true(C_TOMAIL_SUBJECT_ADD)) {
		ncpy(bf,newsgroups,100);
		s = strchr(bf,','); if (s!=NULL) *s = 0;
		tomail_write(l,"Subject: [Posted to %.100s]%.300s\r\n",bf,subject);
	}
	tomail_write(l,"X-Gateway-From: %.300s\r\n",config_get(C_MYNAME));
	tomail_write(l,"\r\n");
	if (config_true(C_TOMAIL_STRIP_BODY)) {
		tomail_write(l,"A new message has been posted to:\r\n");
		if (config_true(C_TOMAIL_PROJECT)) {
			tomail_write(l,"    Project: %.100s\r\n",newsgroups);
		} else {
			tomail_write(l,"    Newsgroups: %.100s\r\n",newsgroups);
		}
		tomail_write(l,"    Subject:    %.100s\r\n",subject);
		tomail_write(l,"    From:       %.100s\r\n",from);
		if (strlen(config_get(C_TOMAIL_URL))>0) {
			*bf='\0';
			if(config_true(C_TOMAIL_WEB_LINK) || config_true(C_TOMAIL_NNTP_LINK)) {
				if(tmp=strrchr(ref,':'))  {
					article=atoi(tmp+1);
					*tmp='\0';
					if(tmp=strrchr(ref,' ')) {
						if(config_true(C_TOMAIL_NNTP_LINK)) {
							lprintf(bf,BFSZ-1,"/%s",tmp+1);
						} else printf(bf,BFSZ-1,"?cmd=article&group=%s&item=%d",tmp+1,article);
					}
				}
			}
			tomail_write(l,"    %s%s\r\n",config_get(C_TOMAIL_URL),bf);
		}
	} else {
		chan_write(l->chan,l->xbody);
	}
	chan_write_s(l->chan,".\r\n");
}
int tomail_link_nest_rset(Link *l);
void tomail_data(Link *l, char *line)
{
	int code = atoi(line);
	l->lastdata = lib_gmtime();
	dmsg("tomail: <--[%d] %.300s\n",l->state,line);
	if (strlen(line)>2) if (line[3]=='-') return; /* should be another line on the way */
again:
	switch (l->state) {
	case P_OPENING:
		if ((code<200) || (code>299)) {
			 emsg("Open responded (%.300s)\n",line);
			 tomail_close_link(l,line);
			 return;
		}
		l->state = P_HELO;
		tomail_write(l,"HELO %.300s\r\n",config_get(C_MYNAME));
		break;
	case P_HELO:
		if ((code<200) || (code>299)) {
			 emsg("Helo responded (%.300s)\n",line);
			 tomail_close_link(l,line);
			 return;
		}
		l->state = P_FROM;
		goto again;
	case P_FROM:
		status_senttry++;
		tomail_write(l,"MAIL FROM:<%.300s>\r\n",tomail_from(&(l->q)));
		l->state = P_TO;
		break;
	case P_TO:
		if (code!=250) {
			emsg("tomail: try setting tomail_from in dnews.conf (%.300s)\n",line); tomail_link_close(l); 
			tomail_write(l,"RSET\r\n");
			l->state = P_RSET;
			break;
		}
		tomail_write(l,"RCPT TO:<%.300s>\r\n",l->q.toemail);		
		l->state = P_DATA;
		break;
	case P_DATA: 
		if (code!=250) {emsg("tomail: to address failed (%.300s)\n",line); tomail_link_next_rset(l); return;}
		tomail_write(l,"DATA\r\n");
		l->state = P_DATA_SEND;
		break;
	case P_DATA_SEND: /* 211 3892 170808 174699 rec.humor selected*/
		if (code!=354) {emsg("tomail: data refused?  (%.300s)\n",line); tomail_link_close(l); return;}
		tomail_send_message(l);
		l->state = P_DATA_SENT;
		break;
	case P_DATA_SENT:
		if (code!=250) {emsg("tomail: message refused (%.300s)\n",line); tomail_link_next_rset(l); break;}
		status_sentok++;
		imsg("tomail: Message sent ok to %.300s %.300s %d\n",l->q.toemail,l->q.gname,l->q.num);
		tomail_write(l,"RSET\r\n");
		l->state = P_RSET;
		break;
	case P_RSET:
		if (code!=250) {emsg("tomail: rset failed (%.300s)\n",line); tomail_link_next(l); break;}
		tomail_link_next(l);
		break;
	}
}
int tomail_link_next_rset(Link *l)
{
	tomail_write(l,"RSET\r\n");
	l->state = P_RSET;
	return TRUE;
}
#ifdef WIN32
#define vsnprintf _vsnprintf
#endif
int tomail_write(Link *l,char *arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	char output[4000];
	char *s;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsnprintf(output,900, format, arg_ptr);
	if (len>400) {
		emsg("Wrote too much data to tomail_write %.80s\n",output);
	}
	if (l->chan<0) return -1;
	chan_write_s(l->chan,output);
	s = strchr(output,'\r'); if (s!=NULL) *s = 0;
	dmsg("tomail: -> %.300s\n",output);
	return TRUE;
}

static Torule **rtable;
static int maxg;
void tomail_malloc_groups(void)
{
	if (rtable==NULL) {
		rtable = mymalloc(max_groups * sizeof(Torule *));
		memset(rtable,0,max_groups * sizeof(Torule *));
		maxg = max_groups;
	}
}
int tomail_rule_add(char *uname, char *gname, char *to, char *subj, char *mid, int isnntp, char *sender, char *from, char *addhead)
{
	Torule *t;
	int h = group_find(gname);

	if (strcmp(uname,"nobody")==0) {
	  imsg("tomail: tomail_rule_add(%.300s,%.300s,%.300s,%.300s,%.300s,%.300s)\n",gname,to,subj,mid,sender,from);
	}
	dmsg("tomail: tomail_rule_add(%.300s,%.300s,%.300s,%.300s,%.300s,%.300s)\n",gname,to,subj,mid,sender,from);
	if (h<0) return FALSE;
	if (rtable==NULL) tomail_malloc_groups();
	if (h>=maxg) return FALSE;
	if (h<0) return FALSE;

	if (isnntp) if (tomail_rule_exists(uname,gname,to,subj,mid)) return TRUE;
	t = mymalloc(sizeof(Torule));
	status_nrules++;
	memset(t,0,sizeof(Torule));
	t->grp = h;
	t->isnntp = isnntp;
	t->uname = mystrdup(uname,SD_TOMAIL);
	t->toemail = mystrdup(to,SD_TOMAIL);
	if (strlen(sender)>0) {t->sender = mystrdup(sender,SD_TOMAIL); strlwr(t->sender);}
	if (strlen(from)>0) {t->from = mystrdup(from,SD_TOMAIL); strlwr(t->from);}
	if (strlen(subj)>0) {t->subj = mystrdup(subj,SD_TOMAIL); strlwr(t->subj);}
	if (strlen(mid)>0) {t->mid = mystrdup(mid,SD_TOMAIL); strlwr(t->mid);}
	if (strlen(addhead)>0) {t->addhead = mystrdup(addhead,SD_TOMAIL);}
	t->u = tomail_user_findadd(to);
	t->next = rtable[h];
	rtable[h] = t;
	return TRUE;
}
void tomail_rule_reset(void)
{
	Torule *t,*tnext;
	int h;
	if (rtable==NULL) return;
	
	for (h=0; h<maxg; h++) {
 	 for (t=rtable[h];t!=NULL;t=tnext) {
		tnext = t->next;
		tomail_free(t);
		status_nrules--;
	 }
	 rtable[h] = NULL;
	}

}
void tomail_clear_limits(void)
{
	Torule *t;
	int h;
	if (rtable==NULL) return;
	for (h=0; h<maxg; h++) {
 	 for (t=rtable[h];t!=NULL;t=t->next) {
		 t->bytes = t->items = 0;
	 }
	}
}
void tomail_free(Torule *t)
{
	if (t->sender!=NULL) mystrfree(t->sender,SD_TOMAIL);
	if (t->addhead!=NULL) mystrfree(t->addhead,SD_TOMAIL);
	if (t->from!=NULL) mystrfree(t->from,SD_TOMAIL);
	if (t->subj!=NULL) mystrfree(t->subj,SD_TOMAIL);
	if (t->mid!=NULL) mystrfree(t->mid,SD_TOMAIL);
	if (t->uname!=NULL) mystrfree(t->uname,SD_TOMAIL);
	mystrfree(t->toemail,SD_TOMAIL);
	myfree(t);
}
void tomail_rule_del(char *gname, char *to)
{
	Torule *t,**last;
	Torule *next;
	int h;
	h = group_find(gname);
	if (h<0 || h>=maxg) return ;
	last = &rtable[h];
	for (t=rtable[h];t!=NULL;t=next) {
		next = t->next;
		if (!t->isnntp) continue; /* Cannot delete fixed rules*/
		if (strcmpnc(to,t->toemail)==0) {
			*last = t->next;
			tomail_free(t);
			status_nrules--;
		} else {
			last = &t->next;
		}
	}
}

int tomail_rule_exists(char *uname, char *gname, char *to, char *subj, char *mid)
{
	Torule *t;
	int h;
	h = group_find(gname);
	if (h<0 || h>=maxg) return TRUE;
	for (t=rtable[h];t!=NULL;t=t->next) {
		if (strcmpnc(to,t->toemail)==0) if (strcmp_n(t->subj,subj)==0) if (strcmp_n(t->mid,mid)==0){
			return TRUE;
		}
	}
	return FALSE;
}

static Ulist *ulist;
Ulist *tomail_user_findadd(char *to)
{
	Ulist *u;
	for (u=ulist;u!=NULL;u=u->next) {
		if (strcmpnc(u->to,to)==0) {
			u->ref++;
			return u;
		}
	}
	u = mymalloc(sizeof(Ulist));
	memset(u,0,sizeof(Ulist));
	u->id = 0;
	u->next = ulist;
	u->to = mystrdup(to,SD_TOMAIL);
	u->ref = 1;
	ulist = u;
	return u;
}
int tomail_feed(char *xsubj, char *xref,int grps[], int ngrps, char *cheat, int len)
{
	/* Remove duplicates somehow, create linked list of everyone we are sending to? */

	int h;
	static int id;
	int i;
	int user_bytes,user_items;
	char subj[BFSZ],ref[BFSZ];
	Torule *r;
	int maxlen;

	id++;
	user_bytes = atoi(config_get(C_TOMAIL_USER_BYTES));
	user_items = atoi(config_get(C_TOMAIL_USER_ITEMS));
	if (user_bytes==0) user_bytes = 10000000;
	if (user_items==0) user_items = 300;
	maxlen = atoi(config_get(C_TOMAIL_MAX_SIZE));
	if (maxlen>0) {
		if (len>maxlen) return FALSE;
	}
	if (rtable==NULL) return FALSE;
	ncpy(subj,xsubj,BFSZ-2);
	ncpy(ref,xref,BFSZ-2);
	strlwr(subj); strlwr(ref);
	for (i=0; i<ngrps; i++) {
		h = grps[i];
		/*imsg("tomail_feed, group %d (%.300s)\n",h,group_name(h));*/
		if (h<0 || h>=maxg) continue;
		for (r=rtable[h];r!=NULL;r=r->next ) {
			/*imsg("considering use %.300s %d %d\n",r->toemail,r->u->id,id);*/
			if (r->u->id==id) continue; /* Already sent this msg to this user */	
			r->u->id = id;
			/* Now check if subject or references match */
			if (r->subj!=NULL) if (strstr(subj,r->subj) ==NULL) {
				dmsg("tomail: Subject didn't match (%.90s)\n",r->subj);
				continue;
			}
			if (r->mid!=NULL) if (strstr(ref,r->mid) ==NULL) {
				dmsg("tomail: Messageid didn't match (%.90s)\n",r->mid);
				continue;
			}
			if (r->bytes>user_bytes) {
				imsg("Not sending item to (%.80s) daily tomail_user_bytes exceeded (%d)\n",r->toemail,r->bytes);
				continue;
			}
			if (r->items>user_items) {
				imsg("Not sending item to (%.80s) daily tomail_user_items exceeded (%d)\n",r->toemail,r->items);
				continue;
			}
			r->bytes += len;
			r->items++;
			tomail_que_add(r,cheat);
			status_nfeed++;
		}
	}
	return TRUE;
}
/*
	xtomail add group=group.name to=email.address [thread=<xxx@xxx.com>] [subject="this subject"]
	xtomail del group=x.y.z to=email.address
	xtomail show to=email.address
*/
int tomail_nntp_cmd(char *cmd, char *user, String *result)
{
	char *p[100];
	int n,i;
	char gname[BFSZ],toemail[BFSZ],thread[BFSZ],subj[BFSZ];
	int doadd,dodel,doshow;
	doadd = dodel = doshow = FALSE;


	if (!config_true(C_TOMAIL)) {
		str_printf(result,"500 you must set tomail true in dnews.conf\r\n");
		return TRUE;
	}
	strcpy(gname,"");
	strcpy(toemail,"");
	strcpy(thread,"");
	strcpy(subj,"");
	n = token_quote(cmd,p,99);

	for (i=0; i<n; i++) {
		if (strcmp(p[i],"xtomail")==0) ;
		else if (strcmp(p[i],"add")==0) doadd = TRUE;	
		else if (strcmp(p[i],"del")==0) dodel = TRUE;	
		else if (strcmp(p[i],"show")==0) doshow = TRUE;	
		else if (strcmp(p[i],"group")==0) {
			if (i+2>=n) {str_printf(result,"500 Parameters missing for %.300s\r\n",p[i]); return FALSE;}
			i++;
			ncpy(gname,p[++i],BFSZ-1); 			
		} else if (strcmp(p[i],"to")==0) {
			if (i+2>=n) {str_printf(result,"500 Parameters missing for %.300s\r\n",p[i]); return FALSE;}
			i++;
			ncpy(toemail,p[++i],BFSZ-1); 			
		} else if (strcmp(p[i],"thread")==0) {
			if (i+2>=n) {str_printf(result,"500 Parameters missing for %.300s\r\n",p[i]); return FALSE;}
			i++;
			ncpy(thread,p[++i],BFSZ-1); 			
		} else if (strcmp(p[i],"subject")==0) {
			if (i+2>=n) {str_printf(result,"500 Parameters missing for %.300s\r\n",p[i]); return FALSE;}
			i++;
			ncpy(subj,p[++i],BFSZ-1); 			
		} else {
			str_printf(result,"500 Invalid parameter (%.300s)\r\n",p[i]);
			return FALSE;
		}
	}
	if (doshow) return tomail_nntp_show(gname,toemail,result);
	if (strlen(user)==0) {
		str_printf(result,"500 User must be logged in to use this command\r\n");
		return FALSE;
	}
	if (doadd) return tomail_nntp_add(user, gname,toemail,subj,thread,result);
	if (dodel) return tomail_nntp_del(gname,toemail,result);
	return TRUE;
}
#define FILE_TOMAIL file_work("tomail.dat")
#define FILE_TOMAIL_NEW file_work("tomail.new")
int tomail_nntp_add(char *uname, char *gname, char *toemail, char *subject, char *thread, String *result)
{
	/* Append to a file, if added more than 'n' rules re-write file. */	
	FILE *f;
	f = fopen(FILE_TOMAIL,"a");
	if (f==NULL) {
		str_printf(result,"400 Append failed, please try later %.300s\r\n",strerror(errno));
		return FALSE;
	}
	fprintf(f,"add\t%d\t%s\t%s\t%s\t%s\t%s\t\n",lib_gmtime(),uname,gname,toemail,subject,thread);
	fclosep(f);
	str_printf(result,"200 Rule added\r\n");
	return TRUE;
}
int tomail_nntp_del(char *gname, char *toemail, String *result)
{
	FILE *f;
	/* Append to a file, if added more than 'n' rules re-write file. */	
	f = fopen(FILE_TOMAIL,"a");
	if (f==NULL) {
		str_printf(result,"400 Append failed, please try later %.300s\r\n",strerror(errno));
		return FALSE;
	}
	fprintf(f,"del\t%d\t%s\t%s\t\n",lib_gmtime(),gname,toemail);
	fclosep(f);
	str_printf(result,"200 Rule deleted\r\n");
	return TRUE;
}
int tomail_nntp_show(char *gname, char *toemail, String *result)
{
	char *p[100];
	int n;
	char bf[BFSZ];
	Torule *rlist=NULL;
	Torule *r;
	FILE *f;

	f = fopen(FILE_TOMAIL,"rb");
	if (f==NULL) {
		str_printf(result,".\r\n");
		return FALSE;
	}
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		n = token_split2(bf,p,"\t\r\n",20);
		if (n<1) continue;

		if (strcmp(p[0],"add")==0) {
			if (n<7) continue;
			if (strcmp(p[4],toemail)!=0) continue;
			if (!group_match(p[3],gname)) continue;
			tomail_addlist(&rlist,p[2],atoi(p[1]),group_find(p[3]),p[4],p[5],p[6]);
		} else if (strcmp(p[0],"del")==0) {
			if (n<4) continue;
			if (strcmp(p[3],toemail)!=0) continue;
			if (!group_match(p[2],gname)) continue;
			tomail_dellist(&rlist,group_find(p[2]),p[3]);
		}
	}
	fclosep(f);

	for (r=rlist;r!=NULL;r=r->next) {
		str_printf(result,"\t%d\t%s\t%s\t%s\t%s\t%s\t\n",r->xtime,r->uname,group_name(r->grp),r->toemail,r->subj==NULL ? "" : r->subj,r->mid==NULL ? "" : r->mid);
	}
	str_printf(result,".\r\n");
	tomail_freelist(&rlist);
	str_printf(result,".\r\n");
	return TRUE;
}
int tomail_readnew_line(char *bf);
static int last_len;
int tomail_reset_readnew(void)
{
	last_len = 0;
	return TRUE;
}
int tomail_readnew(void)
{
	/* If file size increased, then read new bit */ 
	char bf[BFSZ];
	char *s;
	FILE *f;
	f = fopen(FILE_TOMAIL,"rb");
	if (f==NULL) return FALSE;
	fseek(f,last_len,SEEK_SET);
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		s = strchr(bf,'\r'); if (s!=NULL) *s = 0;
		s = strchr(bf,'\n'); if (s!=NULL) *s = 0;
		tomail_readnew_line(bf);
	}
	last_len = ftell(f);
	fclosep(f);
	return TRUE;
}
int tomail_readnew_line(char *bf)
{
	int n;
	char *p[200];
	dmsg("tomail: Processing New line in tomail.dat %.100s\n",bf);
	n = token_split2(bf,p,"\t\r\n",20);
	if (n<2) return FALSE;
	if (strcmp(p[0],"add")==0) {
		if (n<6) return FALSE;
		tomail_rule_add(p[2],p[3],p[4],p[5],p[6],TRUE,"","","");
	}		
	if (strcmp(p[0],"del")==0) {
		if (n<4) return FALSE;
		tomail_rule_del(p[2],p[3]);
	}		
	return TRUE;
}
void tomail_rewrite(void)
{
	/* On startup, rewrite the tomail.dat file */

	char *p[100];
	int n;
	FILE *fout;
	char bf[BFSZ];
	Torule *rlist=NULL;
	Torule *r;
	FILE *f;

	imsg("tomail: rewrite starts\n");
	f = fopen(FILE_TOMAIL,"rb");
	if (f==NULL) return;
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		n = token_split2(bf,p,"\t\r\n",20);
		if (n<1) continue;
		if (strcmp(p[0],"add")==0) {
			if (n<7) continue;
			tomail_addlist(&rlist,p[2],atoi(p[1]),group_find(p[3]),p[4],p[5],p[6]);
		} else if (strcmp(p[0],"del")==0) {
			if (n<4) continue;
			tomail_dellist(&rlist,group_find(p[2]),p[3]);
		}
	}
	fclosep(f);
	fout = fopen(FILE_TOMAIL_NEW,"w");
	if (fout==NULL) {
		filemsg("Could no write tomail.new\n");
		return ;
	}
	for (r=rlist;r!=NULL;r=r->next) {
		/*imsg("add to file %s\n",r->toemail);*/
		fprintf(fout,"add\t%d\t%s\t%s\t%s\t%s\t%s\t\n",r->xtime,r->uname,group_name(r->grp),r->toemail,r->subj==NULL ? "" : r->subj,r->mid==NULL ? "" : r->mid);
	}
	fclosep(fout);
	remove(FILE_TOMAIL);
	ncpy(bf,FILE_TOMAIL,200);
	rename(FILE_TOMAIL_NEW,bf);
	tomail_freelist(&rlist);
	imsg("tomail: rewrite finished\n");
}
int tomail_addlist(Torule **rlist, char *uname, int xtime, int grp, char *tomail, char *subj, char *thread)
{
	Torule *r;
	if (grp<0) return TRUE;

	if (tomail_inlist(*rlist,uname,grp,tomail,subj,thread)) {
		imsg("addlist: removing duplicate %s %s\n",group_name(grp),tomail);
		return TRUE;
	}

	r = mymalloc(sizeof(Torule));
	memset(r,0,sizeof(Torule));
	r->uname = mystrdup(uname,SD_TOMAIL);
	r->grp = grp;
	r->toemail = mystrdup(tomail,SD_TOMAIL);
	if (strlen(subj)>0) r->subj = mystrdup(subj,SD_TOMAIL);
	if (strlen(thread)>0) r->mid = mystrdup(thread,SD_TOMAIL);
	r->next = *rlist;
	*rlist = r;
	return TRUE;
}
int strcmp_n(char *a, char *b)
{
	if (a==NULL) if (b==NULL) return 0;
	if (a==NULL) {
		if (strcmp(b,"")==0) return 0;
		else return 1;
	}
	if (b==NULL) {
		if (strcmp(a,"")==0) return 0;
		else return 1;
	}
	return strcmp(a,b);
}
int tomail_inlist(Torule *rlist, char *uname,int grp,char *tomail,char *subj, char *thread)
{
	Torule *r;
	for (r=rlist;r!=NULL;r=r->next) {
		if (r->grp==grp) if (strcmp(r->toemail,tomail)==0) if (strcmp_n(r->subj,subj)==0) if (strcmp_n(r->mid,thread)==0) {
			return TRUE;
		}
	}
	return FALSE;
}
int tomail_dellist(Torule **rlist, int grp, char *tomail) 
{
	Torule *r,*rnext,**rlast;
	rlast = rlist;
	for (r=*rlist;r!=NULL;r=rnext) {
		rnext = r->next;
		if (r->grp==grp) if (strcmp(r->toemail,tomail)==0) {
			*rlast = r->next;
			tomail_free(r);
			return TRUE;
		}
		rlast = &r->next;
	}
	return TRUE;
}
int tomail_freelist(Torule **rlist)
{
	Torule *r,*rnext;
	for (r=*rlist;r!=NULL;r=rnext) {
		rnext = r->next;
		tomail_free(r);
	}
	*rlist = NULL;
	return TRUE;
}
char *tomail_status(void)
{
	static char bf[BFSZ];
	int d = lib_gmtime() - time_started;
	if (d==0) d = 1;
	if (status_nrules==0) return "";
	lprintf(bf,BFSZ-1,"Tomail: %d rules, %d que'd, %d tried, %d sentok %2.2gk/s\r\n",
		status_nrules,	status_nfeed, status_senttry, status_sentok,(double) status_sentok/d);
	return bf;
}
void tomail_show(int chan)
{
	Torule *t;
	int h;
	char bf[BFSZ];
	if (rtable==NULL) return;
	for (h=0; h<maxg; h++) {
	 for (t=rtable[h];t!=NULL;t=t->next) {
		lprintf(bf,BFSZ-1,"  %s %s %s %d %dk\r\n",t->uname,t->toemail,group_name(t->grp),t->items,t->bytes/1000);
		chan_write_s(chan,bf);
	 }
	}
}
