/* Copyright (C) 1995 NetWin World, New Zealand, All rights reserved. */
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
/*RHP*/
#ifdef __MWERKS__
#include "Mac.h"
#endif

#define DOPGP TRUE
#ifdef VMS
#undef DOPGP
#endif
#ifdef Mac
#undef DOPGP
#endif
#ifdef OS2
#undef DOPGP
#endif
#ifdef NETWARE
#undef DOPGP
#endif


#ifdef DOPGP
#ifdef WIN32
#include <process.h>
#endif
#endif

#ifndef NO_FCNTL
#include <fcntl.h>
#else
/*RHP not for mac*/
#ifndef Mac
#include <file.h>
#endif
#endif
#include "emsg.h"
#include "dnews.h"
#include "sary.h"
#include "str.h"
#include "lib.h"
#include "group.h"
#include "hist.h"
#include "suck.h"
#include "feed.h"
#include "item.h"
#include "db.h"
#include "control.h"

/* If suck sees a message is a control item, then it calls this routine */
/*
  Possibilities are:
	cancel
	rmgroup	
	newgroup
	version
  All other control messages are logged but not actioned.
*/
#define TEMP_ITEM file_work("control.email")
#define CONTROL file_config("control.conf") 
#define VERSION_FILE file_config("version.file") 
void do_sendsys(char *to);
static char action[BFSZ],gname[BFSZ],from[BFSZ],thecmd[BFSZ];
void fix_address(char *in);
static char check_mask[BFSZ];
int cancel_original_matches(char *mid, String *canmsg, char *reason) ;
int pgp_verify(String *xitem, char *issuer);
int ctl_process(char *chead, String *xitem)
{
	int i;
	int ispgp;
	int lvl;
	char *ismod;
	char bf[BFSZ],approved[BFSZ];
	char *bol;
	char subj[BFSZ+100],title[BFSZ];
	Sary *cfile; 
	char *s2;
	int g;
	FILE *f;
	char *s;
#ifdef VMS
	char *vx="$ ";
	char *vxc="$ !";
#else
	char *vx="";
	char *vxc="";
#endif
	strcpy(check_mask,"");
	ncpy(thecmd,get_word(chead,0),50);
	strlwr(thecmd);
	if (strchr(thecmd,'<')!=NULL) {
		imsg("control: Dropping dud control message %.100s\n",chead);
		return FALSE;
	}
	dmsg("control: Control message {%.100s} \n",chead);
	if (strncmp(thecmd,"cancel",6)==0) {
	    int r;
	    r =  ctl_cancel(chead,xitem);
	    if (config_true(C_CANCEL_STORE)) return TRUE;
	}

	ncpy(from,get_ahead(xitem,"Sender:"),BFSZ-200);
	if (strlen(from)==0) ncpy(from,get_ahead(xitem,"From:"),BFSZ-200);
	fix_address(from);
	strlwr(from);
	ncpy(gname,get_word(chead,1),BFSZ-100);
	strlwr(gname);

	if (strncmp(thecmd,"checkgroups",11)==0) { /* Find first group in message to get rule base*/
		s = str_s(xitem);
		s = strstr(s,"\015\012\015\012");
		if (s==NULL) {emsg("No body in checkgroups message\n"); return FALSE; 	}
		s += 4;

		bol = s;
		for (;*bol!=0;) {
			ncpy(gname,bol,BFSZ-2);
			s = strchr(gname,' ');
			if (s!=NULL) *s = 0;
			s = strchr(gname,'\t');
			if (s!=NULL) *s = 0;
			s = strchr(gname,'\015');
			if (s!=NULL) *s = 0;
			if (strlen(gname)==0) {
				bol = strchr(bol+1,'\n');
				if (bol==NULL) break;
				bol++;
			} else break;
		}
		imsg("Checkgroups example group {%.200s} \n",gname);
	}

	/* Proces control.conf */
	cfile = s_alloc();
	strcpy(action,"mail"); /* Default action, mail it*/
	s_read(cfile,CONTROL);
	for (i=0; i<s_len(cfile); i++) {
		ctl_line(s_line(cfile,i));   
	}
	s_free(cfile);

	ispgp = FALSE;
	s = strstr(action,",pgp");
	if (s!=NULL) {
		ispgp = TRUE;
		memmove(s,s+4,strlen(s+4)+1);
	}
	s = strstr(action,"pgp,");
	if (s!=NULL) {
		ispgp = TRUE;
		memmove(s,s+4,strlen(s+4)+1);
	}
	s = strstr(action,"verify-");
	if (s!=NULL) {
		ispgp = TRUE;
		s2 = strchr(s,',');
		if (s2==NULL) {
			if (s>action) {
				s[-1] = 0;
			} else {
				strcpy(action,"doit");
			}
		} else {
			memmove(s,s2+1,strlen(s2+1)+1);
		}
	}
	lvl = 0;
	if (strcmp(action,"doit")==0) lvl = 5;
	else if (strcmp(action,"mail,doit")==0) lvl = 4;
	else if (strcmp(action,"doit,mail")==0) lvl = 4;
	else if (strcmp(action,"mail,log")==0) lvl = 3;
	else if (strcmp(action,"log,mail")==0) lvl = 3;
	else if (strcmp(action,"mail")==0) lvl = 3;
	else if (strcmp(action,"log")==0) lvl = 2;
	else if (strcmp(action,"drop")==0) lvl = 1;
	else {
		emsg("Invalid control.conf action (%.200s), must be one of drop,log,mail,doit, not a combination\n",action);
	}
	if (lvl==4) strcpy(action,"doit");

	imsg("Applying rule (%.200s) (%.200s) (%.200s) (%.200s) %d\n",thecmd,from,gname,action,lvl);
	if (ispgp) {
	 	if (!pgp_verify(xitem,"")) {
	 	 	return TRUE;
	 	}
	}
	
	if ((strcmp(thecmd,"rmgroup")==0)) {
		g = group_find(gname);
		if (g<0) {
			imsg("control: dropping rmgroup for non existent group (%.200s)\n",gname);
			return TRUE;
		}
		if (group_isdeleted(g)) {
			imsg("control: dropping rmgroup for deleted group (%.200s)\n",gname);
			return TRUE;
		}
	}
	if ((strcmp(thecmd,"rmgroup")==0) ||
		(strcmp(thecmd,"newgroup")==0)) {
		ncpy(approved,get_ahead(xitem,"Approved:"),BFSZ-200);
		fix_address(approved);
		/* Can't think of anything useful to compare this too 
		   so am just checking that it exists.  */
		if (strlen(approved)==0) {
			wmsg("control: Rejecting un-approved posting\n");
			return TRUE;
		}		
	} 


	/* DOIT */
	if (lvl<=1) return TRUE;

	if (strcmp(thecmd,"newgroup")==0) {
		strcpy(title,"");
		s = strstr(str_s(xitem),"For your newsgroups file:") ;
		if (s!=NULL) {
			s = strstr(s,gname);
			if (s!=NULL) {
				s = strchr(s,'\t');
				if (s!=NULL) {
					ncpy(title,s+1,100);
					s = strchr(title,'\015'); 
					if (s!=NULL) *s = 0;
				}
			}
		}

	}
	ismod = "y";
	if (strstr(get_word(chead,2),"moderated")!=NULL) ismod = "m";
	if (strstr(get_word(chead,2),"unmoderated")!=NULL) ismod = "y";

	/* Discard newgroup message if group exists and is of same type */	
	if (strcmp(thecmd,"newgroup")==0) {
		g = group_find(gname);
		if (g>=0) if (!group_isdeleted(g)) {
			if (*ismod=='m') {
			  if (group_ismod(g)) {
				imsg("control: ignoring newgroup %.200s m, as it already exists\n",gname);
				return TRUE;
			  }
			} else {
			  if (!group_ismod(g)) {
				imsg("control: ignoring newgroup %.200s y, as it already exists\n",gname);
				return TRUE;
			  }
			}
			
		}
	}

#ifdef NETWARE
	lprintf(bf,BFSZ-1,"c_%.6s.%.3s",thecmd,action);
#else
	lprintf(bf,BFSZ-1,"control_%.200s.%.200s",thecmd,action);
#endif
	f = xfopen(file_log(bf),"a");
	if (f==NULL) {filemsg("Unable to write log file %.200s \n",file_log(bf)); return TRUE;}
	fprintf(f,"# Received at %.200s\n",lib_email_date());
	if (strcmp(thecmd,"newgroup")==0) {
		fprintf(f,"%.200stellnews newgroup %.200s %.200s %.200s \"%.200s\"\n",vx,gname,ismod,from,title);	/* Add time stamp. */
	} else if (strcmp(thecmd,"rmgroup")==0) {
		fprintf(f,"%.200stellnews %.100s # %.200s \n",vx,chead,from);	/* Add time stamp. */
	} else {
		fprintf(f,"%.200s# Other  %.200s %.100s %.100s \n",vxc,action,from,chead);	/* Add time stamp. */
	}
	xfclose(f);

	if (lvl==2) return TRUE;

	if (lvl!=5) {
		remove(TEMP_ITEM);
		str_write(xitem,TEMP_ITEM);
		lprintf(subj,BFSZ+100-1,"Done: %.100s",chead);
		if (lvl==3) {
		f = fopen(TEMP_ITEM,"a");
		if (f!=NULL) {
			fprintf(f,"------------------------------------------------------\r\n");
			fprintf(f,"If you choose to action this message use this command:\r\n");
			if (strcmp(thecmd,"newgroup")==0) {
				fprintf(f,"tellnews newgroup %.200s %.200s %.200s \"%.200s\" \r\n",gname,ismod,from,title);
			} else if (strcmp(thecmd,"rmgroup")==0) {
				fprintf(f,"tellnews rmgroup %.200s\r\n",gname);
			} else if (strcmp(thecmd,"checkgroups")==0) {
				fprintf(f,"tellnews checkgroups filename\r\n");
			} else {
				fprintf(f,"(no equivalent command)\r\n");
			}
			fclosep(f);
		}		
		}			
		if (lvl==3) lprintf(subj,BFSZ+100-1,"Logged only: %.100s",chead);
		if (!lib_mail_msg(config_get(C_MANAGER),subj,TEMP_ITEM)) {
			emsg("Failed to send %.200s to %.200s \n",TEMP_ITEM,config_get(C_MANAGER));
		}
	}
	if (lvl==3) return TRUE;

	/*
		For your newsgroups file:
		soc.genealogy.misc	General genealogical discussions.
	*/

	if (strcmp(thecmd,"rmgroup")==0) {
		imsg("control: action=doit, rmgroup %.200s\n",gname);
		group_delete(group_find(gname));
	} else if (strcmp(thecmd,"newgroup")==0) {
		imsg("control: action=doit, newgroup %.200s %.200s from %.200s\n",gname,ismod,from);
		group_new(gname,from,ismod,title);
	} else if (strcmp(thecmd,"version")==0) {
		lprintf(bf,BFSZ-1,"Version %.200s (%.200s)",DNEWS_VERSION,lib_getlicense());
		lib_mail_msg(from,bf,VERSION_FILE);
	} else if (strcmp(thecmd,"sendsys")==0) {
		do_sendsys(from);
	} else if (strcmp(thecmd,"checkgroups")==0) check_groups(xitem);

	group_write(); 
	return TRUE;
}
#define FILE_SYS file_work("sys.tmp")
void feeds_write_sys(char *fname);
void do_sendsys(char *to)
{

	feeds_write_sys(FILE_SYS);
	lib_mail_msg(to,"Response to your sendsys request",FILE_SYS);
}
void check_groups_any(void)
{
	strcpy(check_mask,"*");
}
/* in=  cmd:mail:groups:action,pgp */
/* KNOWN BUG, BLANKS ARE NOT VALID BETWEEN COLONS*/
void ctl_line(char *in)
{
	char tmpaction[BFSZ];
	char fwild[BFSZ],gwild[BFSZ];
	char cmd[BFSZ];
	char *s;
	
	strlwr(in);
	s = strtok(in,":\n\r");	if (s==NULL) return;
	if (*s=='#') return;
	ncpy(cmd,s,90);
	s = strtok(NULL,":\n\r");	if (s==NULL) return;
	ncpy(fwild,s,BFSZ-200);	
	s = strtok(NULL,":\n\r");	if (s==NULL) return;
	ncpy(gwild,s,BFSZ-200);	
	s = strtok(NULL,":\n\r");	if (s==NULL) return;
	ncpy(tmpaction,s,BFSZ-200);	
	strlwr(gwild);
	for (s=gwild;*s!=0;s++) {
		if (*s=='|') *s = ',';
	}
	/* if group command check if gwild matches gname */
	if (strcmp(cmd,"newgroup")==0 || strcmp(cmd,"rmgroup")==0|| strcmp(cmd,"checkgroups")==0 ) {
		if (!group_match(gwild,gname)) {
			dmsg("controlx: Group didn't match {%.200s} {%.200s} \n",gname,gwild);
			return;
		}
	}
	strlwr(fwild);
	if (!match_wild(fwild,from)) {
		dmsg("controlx: from didn't match {%.200s} {%.200s} \n",fwild,from);
		return;
	}
	if (strcmp(cmd,"all")!=0) {
	if (strcmp(thecmd,cmd)!=0) {
		dmsg("controlx: Command didn't match {%.200s} {%.200s} \n",thecmd,cmd);
		return;
	}
	}
	/* Ok, we matched, so copy the action command */
	ncpy(action,tmpaction,BFSZ-200);
	if (strcmp(thecmd,"checkgroups")==0) {
		ncpy(check_mask,gwild,BFSZ-200);
		dmsg("controlx: Matched {%.200s} {%.200s} {%.200s} \n",thecmd,cmd,gname);
	}
}
/* 	if article exists 
		Verify from or senders match, then delete both.
	Otherwise, 
		Add a dummy line to history database, so if seen again
		it will not be accepted 

If returns 'true' pass this message on to our feeds, if false, then don't.
*/
static char cfrom[BFSZ];
static int is_local_post;
static int cancel_total;
int cancel_total_get(void)
{
    return cancel_total;
}
int cancel_total_clear(void)
{
    cancel_total = 0;
    return TRUE;
}
int ctl_cancel(char *cheadx, String *xitem) 
{
	char mid[BFSZ];
	char chead[BFSZ];
	int npath;
	char xpath[BFSZ];
	char xfrom[BFSZ];
	char *p[200];
	int num;
	char *s;
	int g,ng;
	char *gname;
	char *h;
	int doneit = FALSE;
	char reason[BFSZ];

	vtrace();
	if (config_true(C_CANCEL_REJECT)) return FALSE;
	if (config_true(C_CANCEL_DISABLE)) return TRUE;
	
	ncpy(chead,cheadx,BFSZ-200);
	s = strchr(chead,'<');
	if (s!=NULL) if (s>chead) if (!isspace(*(s-1))) {
		imsg("Inserting space in control message\n");
	 	memmove(s+1,s,strlen(s)+1);
		*s = ' ';
	}
	ncpy(mid,get_word(chead,1),BFSZ-100);	
	if (strlen(mid)<3) {dmsg("control: Message id short {%.100s}\n",mid); return FALSE;} /* Check it's a bit like a message id */
	if (mid[0]!='<') {dmsg("control: Message doesn't start with a < {%.200s} \n",mid); return FALSE;} /* Check it's a bit like a message id */
	vtrace();
	h = hist_exists(mid);
	vtrace();
	if (h == NULL) {
		/* Add to history so we won't accept it when it arrives.
		   Minor bug, this doesn't check that it matches!!!
		   Tough shit, it won't get anywhere if it doesn't anyway.

?????????????			Wait, wont this be lost on the next expire!!!!!!
		*/
		dmsg("control: cancel for unknown message accepted (%.100s)\n",mid);
		hist_add(mid,"1-Jan-95 1:01:01 GMT","","");
		return FALSE; 
	}
	/* Get verified sender from control message*/
	ncpy(cfrom,get_ahead(xitem,"Sender:"),BFSZ-200);
	if (strlen(cfrom)==0) ncpy(cfrom,get_ahead(xitem,"From:"),BFSZ-200);
	fix_address(cfrom);

	ncpy(xfrom,get_ahead(xitem,"From:"),BFSZ-200);
	fix_address(xfrom);


	if (strlen(config_get(C_CANCEL_USERS))>0) if (!group_match(config_get(C_CANCEL_USERS),cfrom)) if (!group_match(config_get(C_CANCEL_USERS),xfrom)) {
		imsg("cancel: Cancels only allowed from {%.200s} {%.200s} (%.200s)\n",config_get(C_CANCEL_USERS),cfrom,xfrom);
		return FALSE;
	}

	/* If local post, compare nntp-posting-host and only allow if it matches */
	if (config_true(C_CANCEL_VERIFY_HOST)) if (feed_ispost()) { /* NNTP-Posting-Host: 10.0.0.15  */
		if (!cancel_original_matches(mid,xitem,reason)) {
			emsg("local_cancel: %.200s\n",reason);
	                msg_reject(reason);
	                msg_show(reason);
			return FALSE;	
		}
	}
/*	if (strcmp(cfrom,from)!=0) {
		if (config_true(C_CANCEL_ANYONE)) {
			imsg("cancel: Cancel from wrong user, doing anyway  {%.200s} {%.200s} \n",cfrom,from);
		} else {
			imsg("cancel: Cancel failed, not from the same user {%.200s} {%.200s} \n",cfrom,from);
			return FALSE;
		}
	}
*/
	cancel_total++;
	gname = hist_groupfirst(h);
	for (ng=0;(gname!=NULL) && (ng<200);ng++) {
                g = group_find(gname);
                if (g<0) goto next2;
	        num = hist_groupnextnum();
		if (!doneit) inmsg("Cancel: %.80s:%d %.90s\n",gname,num,mid);
		doneit = TRUE;
		if (ng==0) db_cancel_fast(g,num,cfrom,FALSE);
		else       db_cancel_fast(g,num,cfrom,TRUE);
next2:	        gname = hist_groupnext();
	}
	if (config_true(C_CANCEL_REJECT_AFTER)) {
		ncpy(xpath,get_ahead(xitem,"Path:"),BFSZ-200);
                npath = token_split(xpath,p," \t,;:!@",99);
		if (npath<3) {
			imsg("Looks like a local control message so not rejecting after actioning\n");
			return TRUE;
		}
		return FALSE;
	}
	ncpy(xpath,get_ahead(xitem,"Path:"),BFSZ-200);
        npath = token_split(xpath,p," \t,;:!@",99);
	if (npath<4) {
		imsg("Probably a local cancel, so telling slaves\n");
                db_clear_cache("ctl_cancel");
                db_cancel_start();
		slave_flush(TRUE);
		slave_flush(FALSE);
	}
	return TRUE;
}


int item_super(char *mid) 
{
	int num;
	int g,ng;
	char *gname;
	char *h;
	
	h = hist_exists(mid);
	if (h == NULL) return TRUE;
	gname = hist_groupfirst(h);
	for (ng=0;(gname!=NULL) && (ng<200);ng++) {
                g = group_find(gname);
                if (g<0) goto next2;
	        num = hist_groupnextnum();
		if (ng==0) db_cancel_fast(g,num,cfrom,FALSE);
		else       db_cancel_fast(g,num,cfrom,TRUE);
next2:	        gname = hist_groupnext();
	}
	return TRUE;
}

/*
	Input:  joe <a@x.y.z>	or  a@x.y.z (joe)
	output: a@x.y.z
*/
void fix_address(char *in)
{
	char bf[200];
	char *s;

	ncpy(bf,in,199);
	
	s = strtok(bf," \t\n<>");		
	for (;s!=NULL;) {/*loop*/
		if (*s!='(') {  /* Because users can edit this bit!!!*/
			if (strstr(s,"@")!=NULL) {strcpy(in,s); return;}
		}
		s = strtok(NULL," \t\n<>()");		
	}
	dmsg("control: Invalid address {%.200s} \n",in);
	return;
}

void check_groups(String *xitem)
{
	static char *trim_ok;
	int g;
	int ndone = 0;
	int nnew = 0;
	int ngroups = group_n();
	char rest[BFSZ];
	char modflag[BFSZ];
	int dud = 0;
	char bf[BFSZ];
	char gwild[BFSZ];
	char title[900];
	char bf2[BFSZ];
	int i;
	char *s,*s2,*s3;
	int upto;
	int xlen;
	FILE *f;

	f = fopen(file_log("check.log"),"w");
	
	strcpy(gwild,"");
	strcpy(gname,"");	
	imsg("checkgroups processing\n");
	if (trim_ok==NULL) trim_ok = mymalloc2(max_groups,M_CONTROL);
	for (i=0; i<ngroups; i++) trim_ok[i] = FALSE;	      /*loop*/

	s = str_s(xitem);
	s = strstr(s,"\015\012\015\012");
	if (s==NULL) {emsg("No body in checkgroups message, Add a dummy header and blank line\n"); goto doreturn;}
	s += 4;
	upto = s - str_s(xitem) ;
	dmsg("control: Upto %d\n",upto);
	for (i=0; i<max_groups; i++) {/*loop*/
		if (dud>30) {
			imsg("Found more than %d dud lines in checkgroups, assuming we are at the end of the message, line %d\n",dud,i);
			if (f!=NULL) fprintf(f,"Found more than %d dud lines in checkgroups, assuming we are at the end of the message, line %d\n",dud,i);
			goto trimit;
		}
		s = str_s(xitem) + upto;
		s2 = strstr(s,"\015\012");
		if (s2==NULL) {
			upto = 0;	
			goto trimit;
		}
		upto = upto + s2-s+2; 
		xlen = s2 - s;
		if (xlen>300) xlen = 300;
		ncpy(bf,s,xlen);
		if (bf[0]==' ') continue; /* Comments */
		if (bf[0]=='\t') continue;
		if (xlen<2) {
			imsg("Found blank line %d, continuing\n",i);
			if (f!=NULL) fprintf(f,"Found blank line %d, continuing\n",i);
			continue;
		}
again:
		s3 = strchr(bf,'\t');
		if (s3==NULL) {
			imsg("Faulty line, no tab (%.200s) trying space\n",bf);
			s3 = strchr(bf,' ');
			if (s3==NULL) {
				imsg("No space found either\n");
				if (f!=NULL) fprintf(f,"Line with no tab or space after group name (%.200s)\n",bf);
				strcat(bf,"\tNo Description");
				goto again;
			}
		}
		*s3++ = 0;
		for (;*s3!=0;s3++) if (!isspace(*s3)) break;
		if (*s3==0) {
			imsg("No group description (%.200s)n",bf);
			if (f!=NULL) fprintf(f,"No group description (%.200s)\n",bf);
			strcpy(rest,"No Description");
		} else ncpy(rest,s3,BFSZ-200);
		s3 = strchr(bf,' '); if (s3!=NULL) *s3 = 0;
		s3 = strchr(rest,'\015'); if (s3!=NULL) *s3 = 0;
		s3 = strchr(rest,'\012'); if (s3!=NULL) *s3 = 0;

		if (strlen(bf)>100) {dud++; continue;}
		if (strlen(bf)<2) {dud++; continue;}

		if (strlen(gname)==0) {strcpy(gname,bf); strlwr(gname); }
		if (strchr(gname,'.')==NULL) {
			imsg("No full stop in group name, probably faulty line (%.200s)\n",gname);
			if (f!=NULL) fprintf(f,"No full stop in group name, probably faulty line (%.200s)\n",gname);
			dud++;
			continue;
		}
		if (strlen(gwild)==0) {
			strcpy(gwild,gname);
			s = strchr(gwild,'.');
			if (s!=NULL) *s = 0;
			strcat(gwild,".*"); 	
			strlwr(gwild);
			imsg("control: Check groups for {%.200s} \n",gwild);
			if (f!=NULL) fprintf(f,"control: Check groups for {%.200s} \n",gwild);
		}
		if (!group_match(gwild,bf)) {
			if (strchr(bf,'.')==NULL) goto isdud;
			if (strlen(bf)<3) goto isdud;
			strcpy(bf2,bf);
			s = strchr(bf2,'.');
			if (s!=NULL) *s = 0;
			strcat(bf2,".*");
			strlwr(bf2);
			if (strlen(gwild)>200) continue;
			strcat(gwild,","); strcat(gwild,bf2);
			goto isok;
					
isdud:			imsg("Not part of tree (%.200s) (%.200s)\n",gwild,bf);
			if (f!=NULL) fprintf(f,"Not part of tree (%.200s) (%.200s)\n",gwild,bf);
			dud++;
			continue;
		} 
isok:
		if (strcmp(check_mask,"")==0) strcpy(check_mask,"*");
		if (!group_match(check_mask,bf)) {
			imsg("Not part of mask in control.conf (%.200s) (%.200s)\n",check_mask,bf);
			if (f!=NULL) fprintf(f,"Not part of mask in control.conf (%.200s) (%.200s)\n",check_mask,bf);
			continue;
		} 

		g = group_find(bf);
		dmsg("control: Trim check {%.200s} %d\n",bf,g);
		if ((g>=0) && (g<ngroups)) trim_ok[g] = TRUE;
		if (g>0) if (group_isdeleted(g)) g = -1;
		if ((g>=0) && (g<ngroups)) trim_ok[g] = TRUE;
		else {
		 	nnew++;
			strcpy(modflag,"y");
			if (strstr(rest,"Moderated")!=NULL) strcpy(modflag,"m");
			if (strstr(rest,"moderated")!=NULL) strcpy(modflag,"m");
			if (strstr(rest,"unmoderated")!=NULL) strcpy(modflag,"y");
			imsg("Newgroup %.200s %.200s\n",bf,modflag);
			if (f!=NULL) fprintf(f,"Newgroup %.200s %.200s\n",bf,modflag);
			lprintf(title,900-1,"%.200s\t%.200s",bf,rest);
			group_new(bf,"checkgroups",modflag,rest);
		}
	}
trimit:;
	if (strlen(gwild)==0) {
	       strcpy(gwild,gname);
		s = strchr(gwild,'.');
		if (s!=NULL) *s = 0;
		strcat(gwild,".*"); 	
		strlwr(gwild);
		imsg("control: Check groups for {%.200s} \n",gwild);
	}
	for (i=0; i<ngroups; i++) {/*loop*/
	  if (!trim_ok[i]) {
	 	if (group_match(gwild,group_namelc(i)) && 
	 	   (group_match(check_mask,group_namelc(i))) ) {
    	                if (!group_isdeleted(i)) {
    	                	wmsg("rmgroup %.200s\n",group_name(i));
				if (f!=NULL) fprintf(f,"rmgroup %.200s\n",group_name(i));
				ndone++;
			}
                        group_setlife(i,0);
                        group_setcached(i,FALSE);
                        group_delete(i);
 		} else dmsg("control: Leaving group %.200s as it doesn't match {%.200s} \n",group_name(i),gwild);
 	  }
	}
	imsg("checkgroups completed, %d groups deleted, %d groups added \n",ndone,nnew);
	if (f!=NULL) fprintf(f,"checkgroups completed, %d groups deleted, %d groups added \n",ndone,nnew);
doreturn:
	if (f!=NULL) {
		fclosep(f);
		if (config_true(C_CHECKGROUPS_EMAIL)) {
			lib_mail_msg(config_get(C_MANAGER),"Checkgroups Log",file_log("check.log"));
		}
	}
	myfree2(trim_ok,M_CONTROL);
	trim_ok = NULL;
}
#define FILE_PGP file_work("pgp.in")

static char *h_name[200];
static char *h_value[200];
static int nhd;
char *hd_find(char *name)
{
	int i;
	for (i=0; i<nhd; i++) {
	 	if (strcmp(h_name[i],name)==0) return h_value[i];	
	}
	return "";
}
void hd_clear(void)
{
 	nhd = 0;
}
void hd_add(char *bf)
{
	char *s;
	int len;
	if (nhd>100) return;
	if (h_name[nhd]==NULL) {
	 	h_name[nhd] = mymalloc2(100,M_CONTROL);
	 	h_value[nhd] = mymalloc2(300,M_CONTROL);
		strcpy(h_name[nhd],"");
		strcpy(h_value[nhd],"");
	}
	s = strchr(bf,':');
	if (s==NULL) return;
	if (s[1]==0) return;
	len = s-bf;
	if (len>30) return;
	ncpy(h_name[nhd],bf,len);
	ncpy(h_value[nhd++],s+2,290);
}
#ifdef DOPGP
static char tzsave[BFSZ];
void save_tz(void)
{
	char *s;
	s = getenv("TZ");
	if (s==NULL) return;
	ncpy(tzsave,s,100);
}
#ifdef WIN32
#define NOUNSETENV TRUE
#endif
void restore_tz(void)
{
	static char bf[BFSZ];
	lprintf(bf,BFSZ-1,"TZ=%.200s",tzsave);
	putenv(bf);
#ifndef NOUNSETENV
	if (strlen(tzsave)==0) unsetenv("TZ");
#endif

}

#endif
int pgp_write(String *xitem);
#ifdef WIN32
#define popen _popen
#define pclose _pclose
#endif
int pgp_verify(String *xitem, char *issuer)
{
	char cmd[BFSZ];
	int isok = FALSE;
	char fnull[BFSZ];
	char fstatus[BFSZ];
	char bf[6000];
	char pgpin[BFSZ];
	char pgpcmd[BFSZ];
	char pgpoptions[BFSZ];
	char pcmd[BFSZ];
	char xissuer[BFSZ];
	FILE *f;
	strcpy(fnull,file_work("pgp.nul"));
	strcpy(fstatus,file_work("pgp.out"));
	strcpy(pcmd,file_work("pgpcmd.cmd"));
	ncpy(xissuer,issuer,200);
	strlwr(xissuer);
	remove(fstatus);
	if (strlen(issuer)>0) {
		strcpy(pgpin,str_s(xitem));
	} else {
		strcpy(pgpin,FILE_PGP);
		if (!pgp_write(xitem)) return FALSE;
	}
	str_write(xitem,file_work("pgp.tst"));

	if (strlen(issuer)==0) isok = TRUE;
#ifdef DOPGP
	save_tz();
	putenv("LANGUAGE=en");
	if (strlen(config_get(C_PGPPATH))>0) {
		lprintf(bf,6000-1,"PGPPATH=%.200s",config_get(C_PGPPATH));
		putenv(bf);
	}	
	if (strlen(config_get(C_PGPTZ))>0) {
		lprintf(bf,6000-1,"TZ=%.200s",config_get(C_PGPTZ));
		putenv(bf);
	} else 	putenv("TZ=gmt");	
	strcpy(pgpcmd,config_get(C_PGP));
	if (strlen(pgpcmd)==0) strcpy(pgpcmd,"pgp");
	lprintf(cmd,BFSZ-1,"%.200s",pgpcmd);
		
	strcpy(pgpoptions,config_get(C_PGP_OPTIONS));
	if (strlen(pgpoptions)==0) strcpy(pgpoptions,"-f");
	lprintf(cmd,BFSZ-1,"%.200s %.200s < %.200s 2>%.200s >%.200s",pgpcmd,pgpoptions
		,pgpin,fstatus,fnull);

#ifdef WIN32
	{ 
		char *s;
		for (s=cmd;*s!=0;s++) {
		 	if (*s=='/') *s = '\\';
		}
	}
/*	strcpy(bf,cmd);
	lprintf(cmd,BFSZ-1,"cmd /c %.200s",bf);
*/
	f = fopen(pcmd,"w");
	if (f==NULL) {filemsg(pcmd); return FALSE;}
	fprintf(f,"%.200s\n",cmd);
	fclosep(f);
	imsg("lib: Attempting pgp command {%.200s}\n",pcmd);
	imsg("lib: to run real command {%.200s}\n",cmd);
	myspawn(pcmd);
#else
	imsg("lib: Attempting pgp command {%.200s}\n",cmd);
	if ((f = popen(cmd,"w"))==NULL)  {
        	filemsg("lib: popen failed for cmd {%.200s} \n",cmd);
        	return FALSE; 
   	}
/*	for (;!feof(f);) {
	      if (fgets(tmp,1000,f)==NULL) if (!feof(f)) filemsg("Problem reading pipe\n");
	      if (strlen(tmp) + strlen(bf)>1000) {emsg("lib: du too much output \n"); break;}
      	      strcat(bf,tmp);
	}
*/
	pclose(f); /* error status meaningless as it's from the process no pclose */
#endif

	restore_tz();
	f = fopen(fstatus,"r");
	if (f==NULL) {
		imsg("pgp: Could not open {%.200s}, is pgp installed, and have you defined PGPPATH in dnews.conf?\n",fstatus); 
	 	return FALSE;
	}
	for (;!feof(f);) {
		strcpy(bf,"");
	 	fgets(bf,BFSZ-10,f);
		strlwr(bf);
		if (strstr(bf,xissuer)!=NULL) isok = TRUE;
		/* isok not used as issuer is not reliably matched.  */
		/*File is signed*/
		
		if (strstr(bf,"file is signed")!=NULL) {
			imsg("pgp: %.200s",bf);
			fclosep(f);  return TRUE;
		}
		if (strstr(bf,"good signature")!=NULL) {
			imsg("pgp: %.200s",bf);
			fclosep(f);  return TRUE;
		}
		if (strstr(bf,"bad signature")!=NULL) {
			emsg("pgp: %.200s",bf);
			fclosep(f); return FALSE;
		}
		dmsg("pgp: %.200s",bf);
	}
	fclosep(f);
	imsg("Fix problem then use tellnews pgp_test to retry, stored (%.200s)\n",file_work("pgp.tst"));
	imsg("pgp: Could not find status in file {%.200s}, is pgp installed?\n",fstatus); 
	f = fopen(fstatus,"r");
	if (f!=NULL) {
	 for (;!feof(f);) {
	 	if (fgets(bf,BFSZ-10,f)==NULL) break;
		imsg("pgp: status: %.200s",bf);
	 }
	 fclosep(f);
	}
	return FALSE;
#else
	return TRUE;
#endif
}

int pgp_write(String *xitem)
{
	int insig;
	char xsig[2000];
	char xheaders[BFSZ];
	char *s,*s2;
	char tmp[BFSZ];
	char xpgpsig[BFSZ];
	char version[BFSZ];
	char bf[BFSZ];
	FILE *f;
	int u;
	int len;
	int inbody;
	hd_clear();

	strcpy(xpgpsig,"");
	strcpy(xsig,"");
	insig = FALSE;

	for (u=0; u<str_len(xitem); ) {
		s = str_s(xitem)+u;
		s2 = strchr(s,'\r');
		len = s2-s;
		if (s2==NULL) break;
		if (len>BFSZ-4) len = 400;
		ncpy(bf,s,len);
		if (strlen(bf)==0) break;  /* End of header */
		if ((bf[0]==' ') || (bf[0]=='\t')) {
			if (insig) {
				if (strlen(xsig)>1000) break;
			 	strcat(xsig,bf);
			}		
		} else {
			insig = FALSE;
			hd_add(bf);
		}

		if (strncmp(bf,"X-PGP-Sig:",10)==0) {
		 	ncpy(xpgpsig,bf+11,BFSZ-200);
			insig = TRUE;
		} 
		u += s2-s+1;
		if (*s2!=0) u++;
	}	
	if (strlen(xpgpsig)==0) {
		imsg("pgp: No PGP signature present in control message, rejecting\n");
		return FALSE;
	}
	/* xpgpsig = 2.7 Subject,Control,Message-ID,Date,From,Sender */
	/* xsig = <tab>iQCVA<tab>gUBMXve<tab>xMJdO */
		
	s = strtok(xpgpsig," \n");
	if (s==NULL) {
		imsg("pgp: No PGP Version present in control message, rejecting\n");
		return FALSE;
	}
	ncpy(version,s,BFSZ-200);
	s = strtok(NULL," \n");
	if (s==NULL) {
		imsg("pgp: No PGP Headers present in control message, rejecting\n");
		return FALSE;
	}
	ncpy(xheaders,s,BFSZ-200);

	f = fopen(FILE_PGP,"w");
	if (f==NULL) {
	 	filemsg("pgp: Authentication failed, could not write %.200s\n",FILE_PGP);
		return FALSE;
	}
	fprintf(f,"-----BEGIN PGP SIGNED MESSAGE-----\n\n");
	fprintf(f,"X-Signed-Headers: %.200s\n",xheaders);
	s = strtok(xheaders,", ");
	for (;s!=NULL;) {
		fprintf(f,"%.200s: %.200s\n",s,hd_find(s));		
	 	s = strtok(NULL,", ");
	}
	inbody = FALSE;
	for (u=0; u<str_len(xitem); ) {
		s = str_s(xitem)+u;
		s2 = strchr(s,'\r');
		len = s2-s;
		if (s2==NULL) break;
		if (len>BFSZ-4) len = 400;
		ncpy(bf,s,len);
		if (strlen(bf)==0) inbody = TRUE;
		if (inbody) {
			if (bf[0]=='-') {
				ncpy(tmp,bf,BFSZ-10);
				strcpy(bf,"- ");
				strcat(bf,tmp);
			}
		 	fprintf(f,"%.200s\n",bf);
		}
		u += s2-s+1;
		if (*s2!=0) u++;
	}	

	fprintf(f,"\n-----BEGIN PGP SIGNATURE-----\n");
	fprintf(f,"Version: %.200s\n\n",version);
	s = strtok(xsig,"\t");
	for (;s!=NULL;) {
		fprintf(f,"%.200s\n",s);
	 	s = strtok(NULL,"\t");
	}
	fprintf(f,"-----END PGP SIGNATURE-----\n");
	fclosep(f);
	
	return TRUE;
}

int cancel_original_matches(char *mid, String *canmsg, char *reason) 
{
	char host[BFSZ];
	char ohost[BFSZ];
	String *origmsg;
	int grp,num;

	/* Fetch original header - and then check host matches */	
	if (!hist_find(mid,&grp,&num)) {
              lprintf(reason,BFSZ-1,"Original item not found {%.200s} \015\012",mid);
	      return FALSE;
	}
	origmsg = item_head(grp,num);
        if (origmsg==NULL) {
		lprintf(reason,BFSZ-1,"Original item not found {%.200s} \015\012",mid);
		return FALSE;
	}
	/* Check host matches */
	ncpy(host,get_ahead(canmsg,"NNTP-Posting-Host"),100);
	ncpy(ohost,get_ahead(origmsg,"NNTP-Posting-Host"),100);

	if (strcmp(host,ohost)!=0) {
              lprintf(reason,BFSZ-1,"Posting host doesn't match {%.200s} {%.200s] \015\012",host,ohost);
	      return FALSE;
	}

	return TRUE;
}
