/* Copyright (C) 1995 NetWin World, New Zealand, All rights reserved. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "group.h"
#include "item.h"
#include "dnews.h"
#include "nntp.h"
#include "hist.h"
#include "suck.h"
#include "chan.h"
#include "sary.h"
#include "str.h"
#include "lib.h"
#include "feed.h"
#include "post.h"
#include "emsg.h"
#include "sponser.h"
#include "lib_spawn.h"
int post_add_block(char *user);

/*
        Check required headers
        Add some if missing (lines for example)
        Check syntax of everything
        If ok item_suck, if not item_spew

        This routine is to stop crap from getting into the 
        news network.  It is 'nastier' than the item_suck routine
        which will virtually accept anything.

*/
#define MAX_HEADS 30
int post_moderated(String *anitem) ;
enum {HOTHER,HADD,HREP,HREQ,HOBS,HSTRIP} ;

void  tell_user(String *head, String *body, char *status);
typedef struct {char *name; int flag;} TABLE;
int post_spawn_filter(String *xitem);

static int hset[MAX_HEADS];
enum {HPATH,HNNTP,HMID,HDATE,HLINES,HORG,HDIST};

static TABLE head[]={
  {"Path:",                HADD},
  {"NNTP-Posting-Host:",   HREP},
  {"Message-ID:",          HADD},
  {"Date:",                HADD},
  {"Lines:",               HADD},
  {"Organization:",	   HADD},
  {"Distribution:",        HADD},
  {"Posting-Version:",     HOBS},
  {"Relay-Version:",       HOBS},
  {"Posted:",              HOBS},
  {"Received:",            HOBS},
  {"Date-Received:",       HOBS},
  {"From:",                HREQ},
  {"Newsgroups:",          HREQ},
  {"Subject:",             HREQ}
};

#define NHEADS 15
static int nhead = NHEADS;
static char post_err[200];
int post_is_sender(char *hcon, char *cur_email, char *cur_sender, char *cur_host);
String *post_replace(String *item);
int str_write_to(String *xitem,char *fname,char *addr);
static int modgrp;
void post_setmodgrp(int x)
{
	modgrp = x;
}
char *post_hostname(int c);
char *strip_domain(char *s);

char *mod_address(char *gname);
char *post_geterr(void)
{
	return post_err;
}
int quoted_lines(String *item);
void post_seterr(char *s)
{
        ncpy(post_err,s,100);
}
char *lib_strip_domain(char *s);
static char nntp_host[BFSZ];
static int nlines;
static int pchan;
static char newdist[BFSZ];
static char add_approved[BFSZ];
static char xtoslave[BFSZ];
int chan_write_slave(int chan, char *s);
int chan_write_slave(int chan, char *s)
{
	char bf[BFSZ];
	lprintf(bf,BFSZ-1,"%s%s",xtoslave,s);
	chan_write_s(chan,bf);
	return 1;
}
#define FILE_DISTRIB file_config("distrib.conf")

int distrib_find(char *grp, char *result, int *pri)
{
	FILE *f;
	int p,cur;
	char *s;
	int gotone = FALSE;
	char wild[BFSZ];
	char value[BFSZ];
	char bf[BFSZ];
	f = fopen(FILE_DISTRIB,"r");
	if (f==NULL) return FALSE;
	
	cur = -1;
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-10,f)==NULL) break;
		s = strtok(bf,": \r\n"); if (s==NULL) continue;
		if (*s=='#') continue;
		p = atoi(s);
		s = strtok(NULL,": \r\n"); if (s==NULL) continue;
		strcpy(wild,s);
		s = strtok(NULL,": \r\n"); if (s==NULL) continue;
		strcpy(value,s);
		if (group_match(wild,grp)) {
			if (p<cur) continue;	
			cur = p;
			gotone = TRUE;
			strcpy(result,value);
		}
	}
	fclosep(f);
	*pri = cur;
	return gotone;
}
#define FILE_SPAMMERS file_config("spammers.dat")
int user_isspammer(char *xuser)
{
	FILE *f;
	char *s;
	char user[BFSZ];
	char bf[BFSZ];
	int r = FALSE;
	ncpy(user,xuser,200);
	strlwr(user);
	f = fopen(FILE_SPAMMERS,"r");
	if (f==NULL) return FALSE;
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		strlwr(bf);
		s = strchr(bf,'#');
		if (s!=NULL) *s = 0;
		s = strtok(bf," \t\r\n");
		if (s==NULL) continue;
		if (strcmp(user,s)==0) {
			r = TRUE;
			break;
		}
	}
	fclosep(f);
	return r;
}


static int nsilent;
int post_nsilent(void)
{
	return nsilent;
}
static int isxslave;
static char cur_user[BFSZ];
static char cur_email[BFSZ];
static char cur_sender[BFSZ];
static char nntp_hostipnum[BFSZ];
static int ispostonly;
char *email_fix(char *s);
static int xismod;
char *post_addfile(void)
{	
	static char fname[BFSZ];
	ncpy(fname,file_config("post.add"),200);
	if (strlen(dom_ini_get(C_POST_ADD)) > 0) {
		ncpy(fname,dom_ini_get(C_POST_ADD),200);
	}
	return fname;
}
static char this_mid[BFSZ];
char *post_this_mid(void)
{
	return this_mid;
}	
int post_article(int chan, String *anitem, char *toslave)
{
        String *newhead;
	int r;
	int isauthed = FALSE;
	int min;
        int ismod;
	double qqr,qr;
	int nquoted;
        char bf[BFSZ];
	char bf2[BFSZ];
	char cur_host[BFSZ];
        int ngpoint;
        char mfrom[BFSZ],tomod[BFSZ];
        char *gpoint[1000];
        int g,gi;
	char opath[BFSZ];
	char msgid[BFSZ];
	char hcon[BFSZ],hsub[BFSZ];
        char grps[BFSZ],approved[BFSZ];
	char hcon_orig[BFSZ];
	FILE *f=NULL;
        time_t t;
	int butwork = FALSE;
	char *s2;
	char *blocked="ERRORRR";
	int stripdash = FALSE;
	int curpri,pri;
	int ignore_approved=FALSE;
	char addfile[BFSZ];
	char *s;
	int xslave=FALSE;
	int log_size;

	id_check(chan);

	ncpy(this_mid,"",10);
	
	ispostonly = config_true(C_POSTONLY);
	if (strlen(toslave)>0) xslave = TRUE;
	isxslave = xslave;
	imsg("Local post %d bytes\n",str_len(anitem));
	strcpy(xtoslave,toslave);

	if (strlen(toslave)==0) {
		post_spawn_filter(anitem);
		if (str_len(anitem)==0) {
			msg_reject("post_spawn_filter rejected message\n");
                        chan_write_slave(chan,"441 rejected by external filter\015\012");
			return FALSE;
		}
	}

	if (strlen(toslave)==0) if (config_true(C_POST_LOG)) {
	 	f = fopen(file_log("post.log"),"ab");
		if (f==NULL) filemsg("Couldn't append to post.log");
		else {
			fprintf(f,"---DNEWS Posted Article Begins---\015\012");
		 	fwrite(str_s(anitem),1,str_len(anitem),f);
			fclosep(f);
		}
		if(log_size=atoi(config_get(C_POST_LOG_SIZE))) {
			if ((int) lib_filesize(file_log("post.log"))>=log_size)
				emsg_rotate_file(file_log("post.log"));
		}
		else if (lib_filesize(file_log("post.log"))>100000000) {
			if(config_true(C_LOG_ONEFILE)) remove(file_log("post.log"));
			else emsg_rotate_file(file_log("post.log"));
		}
	}

	pchan = chan;
	if (!isxslave) {

	if (post_add_block(chan_getuser(chan))) goto skip_add;

	if (config_true(C_POST_ADD_GROUP)) {
		lprintf(addfile,200,"post.%.200s",grps);
		s = strchr(addfile,',');
		if (s!=NULL) *s = 0;
		f = fopen(file_config(addfile),"r");
		if (f==NULL) f = fopen(post_addfile(),"r");
	} else {
		if (sponser_append(anitem)) goto skip_add;
		f = fopen(post_addfile(),"r");
	}
	if (f!=NULL) {
		s = strstr(str_s(anitem),"\n-- ");
		if (s==NULL) s = strstr(str_s(anitem),"\n--\r");
		if (s==NULL) s = strstr(str_s(anitem),"\n--\n");
		if (s!=NULL) stripdash = TRUE;
	 	for (;!feof(f);) {
		 	if (fgets(bf,BFSZ-10,f)==NULL) break;
			s = strchr(bf,'\n');
			if (s!=NULL) *s = 0;
			if (strcmp(bf,".")==0) strcpy(bf,"..");
			if (stripdash) if (strncmp(bf,"-- ",3)==0) continue;
			strcat(bf,"\015\012");
			str_addsn(anitem,bf);
		}
		fclosep(f);
	}
skip_add:

	f = fopen(file_config("post.pre"),"r");
	if (f!=NULL) {
		String *xnew;
		int upto;
		xnew = str_new();
		upto = 0;
		for (;TRUE;) {
			if (!str_getline(anitem,&upto,bf,BFSZ-10)) {
				fclosep(f);
				str_free(xnew);
				goto giveup;
			}
			str_addsn(xnew,bf);
			str_addsn(xnew,"\r\n");
			if (strcmp(bf,"")==0) break;
		}

	 	for (;!feof(f);) {
		 	if (fgets(bf,BFSZ-10,f)==NULL) break;
			s = strchr(bf,'\n');
			if (s!=NULL) *s = 0;
			if (strcmp(bf,".")==0) strcpy(bf,"..");
			strcat(bf,"\015\012");
			str_addsn(xnew,bf);
		}
		fclosep(f);

		for (;TRUE;) {
			if (!str_getline(anitem,&upto,bf,BFSZ-10)) break;
			str_addsn(xnew,bf);
			str_addsn(xnew,"\r\n");
		}
		str_copy(anitem,xnew);
		str_free(xnew);
	}

giveup:;
	}

        ncpy(nntp_host,chan_getipname(chan),BFSZ-1);
        ncpy(nntp_hostipnum,chan_getipnum(chan),BFSZ-1);
	if (strchr(nntp_host,'.')==NULL) ncpy(nntp_host,chan_getipnum(chan),BFSZ-1); 
	if (config_true(C_POSTING_HOST_NUMBER)) {
		ncpy(nntp_host,chan_getipnum(chan),BFSZ-1); 
	}
        msg_from(chan_getipname(chan));
        strcpy(post_err,"posting failed, sorry");

        ncpy(grps,get_ahead(anitem,"Newsgroups:"),BFSZ-1);
        strlwr(grps);


	ncpy(cur_user,chan_getuser(chan),BFSZ-10);
	ncpy(cur_email,email_fix(get_ahead(anitem,"From:")),BFSZ-10);

        
	
	if (strlen(chan_getuser(pchan))>0) {
		if (user_isspammer(strip_domain(chan_getuser(pchan)))) {
			emsg("User (%s) found in %s, silently rejecting post\n",strip_domain(chan_getuser(pchan)),FILE_SPAMMERS);
			msg_reject("User (%s) found in %s, silently rejecting post\n",strip_domain(chan_getuser(pchan)),FILE_SPAMMERS);
                        chan_write_slave(chan,"240 article posted ok\015\012");
	                return TRUE;
		}
	}
	ngpoint = token_split(grps,gpoint,", \t\n",200); /*MAX_GRPS*/ 
        if (ngpoint>100) {
                emsg("post: Posting failed, too many newsgroups %d\n",ngpoint);
                lprintf(bf,BFSZ-1,"441 limit of 100 news groups in one article\015\012");
                msg_reject("%s",bf);
                chan_write_slave(chan,bf);
                return FALSE;
        }
	for (g=0; g<ngpoint; g++) {
	     if (gpoint[g]==NULL) {
                emsg("post: Posting failed, NULL news group, please report internal bug %d\n",g);
                lprintf(bf,BFSZ-1,"441 Internal bug related to news group list\015\012");
                msg_reject("%s",bf);
                chan_write_slave(chan,bf);
                return FALSE;		
	     }
             if (strlen(gpoint[g])>150) {
                emsg("post: Posting failed, News group name too long %d bytes\n",strlen(gpoint[g]));
                lprintf(bf,BFSZ-1,"441 News group name too long\015\012");
                msg_reject("%s",bf);
                chan_write_slave(chan,bf);
                return FALSE;		
	     }
	}

	ncpy(bf,str_s(anitem),80);
	s = strchr(bf,' ');
	s2 = strchr(bf,':');
	if (s!=NULL) if (s2!=NULL) if (s2>s) {
                emsg("post: Posting failed, Space in header\n");
                lprintf(bf,BFSZ-1,"441 Space in first header\015\012");
                msg_reject("%s",bf);
                chan_write_slave(chan,bf);
                return FALSE;		
	}
        
        /* Check the user has posting priv on all of these groups */
	if (xslave) goto skipcheck; /* message from slave who has already checked what the user can do */
	for (g=0; g<ngpoint; g++) {
                if (g==0) msg_group(gpoint[g]);
                if (!chan_canpostgrp(chan,gpoint[g])) {
			if (strlen(config_get(C_POST_PROT_MSG))==0) {
	                        lprintf(bf,BFSZ-1,"480 you do not have posting access to {%s}\015\012",gpoint[g]);
			} else {
	                        lprintf(bf,BFSZ-1,"480 %s {%s}\015\012",config_get(C_POST_PROT_MSG),gpoint[g]);
			}
			if (config_true(C_POST_PROT_PINE)) {
				if (strlen(chan_getuser(chan))>0) {
		                        lprintf(bf,BFSZ-1,"440 you do not have posting access to {%s}\015\012",gpoint[g]);
				}
			}
                        chan_write_slave(chan,bf);
                        msg_reject(bf);
                        return FALSE;
                }
		if (hdr_matchgroup(gpoint[g])) {
			ispostonly = TRUE;
			butwork = TRUE;
		}
        }
skipcheck:
        /* Decide if any of these groups are moderated */
        ncpy(hcon,get_ahead(anitem,"Control:"),BFSZ-100);
        ncpy(hsub,get_ahead(anitem,"Subject:"),BFSZ-100);
	if (strlen(hcon)==0) if (strncmp(hsub,"cmsg ",5)==0) ncpy(hcon,hsub+5,BFSZ-100);
	ncpy(hcon_orig,hcon,BFSZ-1);
	strlwr(hcon);

        ismod = FALSE;
        for (g=0; g<ngpoint; g++) {
                gi = group_find(gpoint[g]); 
		if (gi<0) {
			if (!config_true(C_POST_NONGROUPS)) {
				if (config_true(C_POST_NEWGROUP)) if (strstr(hcon,"newgroup")!=NULL) goto nonok; 
		                if (gpoint[g]==NULL) gpoint[g] = "null_group_bug";
				emsg("post: Cannot crosspost to nonexistent group %s set post_nongroups true to enable\n",gpoint[g]);
			        lprintf(bf,BFSZ-1,"441 cannot crosspost to nonexistent group (%s) see dnews.log for details\015\012",gpoint[g]);
				msg_reject("%s",bf);
			        chan_write_slave(chan,bf);	
				return FALSE;
			}
		}
nonok:
                if (gi>=0) if (group_ismod(gi)) {
                        ismod = TRUE;
                        modgrp = gi; /* Remeber which group was moderated */
                }
        }
	xismod = ismod;
        dmsg("post: Moderated flag is %d\n",ismod);
	
	feed_posttest(gpoint,ngpoint);
        
	ignore_approved = FALSE;
        strcpy(add_approved,"");
        if (ismod) if (config_true(C_APPROVE_AUTHORIZED) ) {
                /* if logged in user matches moderators email - domain */
		/* then add approved header, and allow message, else ignore approved */
                ncpy(tomod,mod_address(group_name(modgrp)),BFSZ-100);
                strlwr(tomod);
		ncpy(mfrom,lib_strip_domain(chan_getuser(chan)),100);
		strlwr(mfrom);
                fix_address(mfrom); fix_address(tomod);
		strcpy(tomod,lib_strip_domain(tomod));
                if (strcmp(mfrom,tomod)==0) {
                        imsg("Adding approved header for post from local authorized moderator %s\n",mfrom);
                        lprintf(add_approved,BFSZ-1,"Approved: %s\r\n",mod_address(group_name(modgrp)));
                        ismod = FALSE;
                } else {
                        imsg("Unauthorized post (%s)!=(%s) sending to moderator \n",mfrom,tomod);
			ignore_approved = TRUE;
                }
	}

        if (!ispostonly) {
                ncpy(approved,get_ahead(anitem,"Approved:"),BFSZ-1);
                /* There doesn't seem to be anything we can check this against*/
                /* so we just test for it's existence */
		if (!ignore_approved) 
			if (strlen(approved)>3) ismod = FALSE; /* Pretend it's a normal post*/ 
        }       

        if (ismod) if (config_true(C_APPROVED_AUTO) ) {
                /* if from matches a local moderator then add approved header */
                ncpy(tomod,mod_address(group_name(modgrp)),BFSZ-100);
                strlwr(tomod);
                ncpy(mfrom,get_ahead(anitem,"From:"),BFSZ-100);
                strlwr(mfrom);
                fix_address(mfrom); fix_address(tomod);
                if (strcmp(mfrom,tomod)==0) {
                        imsg("Adding approved header for post from local moderator %s\n",mfrom);
                        lprintf(add_approved,BFSZ-1,"Approved: %s\r\n",mfrom);
                        ismod = FALSE;
                } else {
                        imsg("Post to moderated group from (%s) moderator (%s) \n",mfrom,tomod);
                }
	}

        ncpy(hcon,get_ahead(anitem,"Control:"),BFSZ-100);
        ncpy(hsub,get_ahead(anitem,"Subject:"),BFSZ-100);
	if (strlen(hcon)==0) if (strncmp(hsub,"cmsg ",5)==0) ncpy(hcon,hsub+5,BFSZ-100);
	ncpy(hcon_orig,hcon,BFSZ-1);
	strlwr(hcon);
	if (!config_true(C_POST_SUPERSEDES)) {
	        ncpy(bf,get_ahead(anitem,"Supersedes:"),BFSZ-100);
		if (strlen(bf)>0) { blocked = "supersedes"; goto blockit;	}
	}
	if (!config_true(C_POST_SENDSYS)) {
	  if (strstr(hcon,"sendsys")!=NULL) { 
		  blocked = "sendsys"; goto blockit;	
	  }
	}
	if (!config_true(C_POST_VERSION)) {
	  if (strstr(hcon,"version")!=NULL) { 
		  blocked = "version"; goto blockit;	
	  }
	}


	if (xslave) if (strlen(get_ahead(anitem,"X-Authenticated-User:"))>0) isauthed = TRUE;

	if (config_true(C_POST_CANCEL_OWNER)) {
	  if (is_slave()) goto xxx;
	  imsg("cancel: isauthed %d user (%s)\n",isauthed,chan_getuser(chan));
	  if (!isauthed && (strlen(chan_getuser(chan))==0)) {
		  if (strstr(hcon,"cancel")!=NULL) {
			ncpy(cur_sender,email_fix(get_ahead(anitem,"Sender:")),BFSZ-10);	  
			ncpy(cur_host,get_ahead(anitem,"NNTP-Posting-Host:"),BFSZ-10);	  
			if (strlen(cur_host)==0) ncpy(cur_host,nntp_host,100);
			if (!post_is_sender(hcon_orig,cur_email,cur_sender,cur_host)) {
				blocked = "cancel"; 
				emsg("post: Blocked local cancel message because user not authorized and not sender, post_cancel_authorized is true\n");
				lprintf(bf,BFSZ-1,"441 sorry cancel blocked, authentication required for cancels or must be sender\015\012");
				msg_reject("%s",bf);
				chan_write_slave(chan,bf);	
				return FALSE;		  
			} else {
				imsg("cancel: user not logged in, but message sent by same person so cancelling\n");
			}
		  }
	  } 
	  goto xxx;
	}

	if (!config_true(C_POST_CANCEL)) {
	  if (strstr(hcon,"cancel")!=NULL) { 
		  blocked = "cancel"; goto blockit;	
	  }
	}

	if (config_true(C_POST_CANCEL_AUTHORIZED)) {
	  if (strlen(chan_getuser(chan))==0) {
		  if (strstr(hcon,"cancel")!=NULL) { 
			blocked = "cancel"; 
			emsg("post: Blocked local cancel message because user not authorized, post_cancel_authorized is true\n");
			lprintf(bf,BFSZ-1,"441 sorry cancel blocked, authentication required for cancels\015\012");
			msg_reject("%s",bf);
			chan_write_slave(chan,bf);	
			return FALSE;		  
		  }
	  }
	}
xxx:;
	/*
	if (!config_true(C_POST_APPROVED)) {
          ncpy(bf,get_ahead(anitem,"Supersedes:"),BFSZ-100);
	  if (strstr(hcon,"approved")!=NULL) { 
		  blocked = "approved"; goto blockit;	
	  }
	}
	*/
	if (!config_true(C_POST_NEWGROUP)) {
	  if (strstr(hcon,"newgroup")!=NULL) { 
		  blocked = "newgroup"; goto blockit;	
	  }
	}
	if (!config_true(C_POST_RMGROUP)) {
	  if (strstr(hcon,"rmgroup")!=NULL) { 
		  blocked = "rmgroup"; goto blockit;	
	  }
	}
	goto noblock;
blockit:
	emsg("post: Blocked local control message (%s) to enable add to dnews.conf post_%s true\n",blocked,blocked);
	lprintf(bf,BFSZ-1,"441 sorry control (%s) not enabled, ask news manager to enable, see dnews.log for instructions\015\012",blocked);
	msg_reject("%s",bf);
	chan_write_slave(chan,bf);	
	return FALSE;
noblock:
	strcpy(newdist,"");
	curpri = -1;
	if (lib_exists(FILE_DISTRIB)) for (g=0; g<ngpoint; g++) {
                if (distrib_find(gpoint[g],bf,&pri)) {
			if (pri>curpri) {
				curpri = pri;
				strcpy(newdist,bf);
			}
		}
        }


        
	nlines = body_lines(anitem);
	qr = atof(config_get(C_POST_QUOTED_LIMIT));
	min = atoi(config_get(C_POST_QUOTED_MIN));
	if (min==0) min = 35;
	if (qr>0) {
		nquoted = quoted_lines(anitem);
		if (nlines>min) {
			qqr = (double) nquoted / (double) nlines;
			if (qqr>qr) {
				if (strlen(config_get(C_POST_QUOTED_MESSAGE))>0) 
					lprintf(bf2,BFSZ-1,"%s %d/%d %2.2f, limit is %2.2f\n",config_get(C_POST_QUOTED_MESSAGE),
						nquoted,nlines,qqr,qr);
				else 
					lprintf(bf2,BFSZ-1,"Ratio of lines starting with > to normal lines too high %d/%d %2.2f, limit is %2.2f\n",
						nquoted,nlines,qqr,qr);
				lprintf(bf,BFSZ-1,"441 %s\015\012",bf2);
				chan_write_slave(chan,bf);
				msg_reject("%s",bf2);
			        return FALSE;
					
			}
		}
	}

	if (strlen(xtoslave)>0) {
		head[1].flag = HADD;		
	}

	if (strlen(xtoslave)==0) {
		char subj[BFSZ];
		char fname[BFSZ];
		char subject[BFSZ];
		int part,partmax;
	        ncpy(subject,get_ahead(anitem,"Subject:"),BFSZ-100);
		if (parts_subject(subject, subj, fname, &part, &partmax)) {
			imsg("Found multi part message %d/%d \n",part,partmax);
			if (part>2) {
				pf_addone(chan_getipnum(chan),-1);
				pf_addone(chan_getuser(chan),-1);
			}
		}
	}	
	newhead = post_replace(anitem);
	/* newhead = post_wildrep(anitem); */
	if (strlen(xtoslave)>0) {
		head[1].flag = HREP;		
	}
        if (newhead==NULL) {
                dmsg("post: replace didn't like that message \n");
                lprintf(bf,BFSZ-1,"441 %s\015\012",post_err);
                chan_write_slave(chan,bf);
                msg_reject("Replace failed, %s",bf);
                return FALSE;
        }
  
        if (!head_replace(anitem,newhead)) {
                dmsg("post: message had no body \n");
                chan_write_slave(chan,"441 no body found\015\012");
                msg_reject("Most likely it had no body");
                return FALSE;
        }

	ncpy(this_mid,get_ahead(anitem,"Message-ID:"),BFSZ-1);

	if (!xslave) if (config_true(C_POST_REC)) {
//		struct tm atm;
		char str[BFSZ];
		t = time(NULL);
	 	f = fopen(file_log("post.rec"),"a");
		lprintf(bf,BFSZ-1,safe_ctime(&t,str));
		s = strchr(bf,'\n');
		if (s!=NULL) *s = 0;
		if (f==NULL) filemsg("Couldn't append to post.rec");
		else {
			fprintf(f,"%s	",bf);
			fprintf(f,"%s	",struntab(get_ahead(anitem,"Message-ID:")));
			fprintf(f,"%s:",chan_getuser(chan));
			fprintf(f,"%s:%s	",chan_getipname(chan),chan_getipnum(chan));
			fprintf(f,"%s	",struntab(get_ahead(anitem,"From:")));
			fprintf(f,"%s	",struntab(get_ahead(anitem,"Subject:")));
			if (config_true(C_POST_REC_NONEWSGROUPS)) {
    			    fprintf(f,"	\n");
			} else {
    			    fprintf(f,"%s	\n",struntab(get_ahead(anitem,"Newsgroups:")));
			}
			fclosep(f);

		}
	}


	if (is_reader()) { /* Readonly slave or master */
		return readonly_post(chan,anitem);
	}
	if (is_slave()) {
		imsg("Posting to Master dnews process\n");
		if (!slave_post(chan,anitem)) {
			emsg("slave: Slave not connected to master, cannot post messages");
			chan_write_slave(chan,"441 posting failed - slave not connected to master, report to news admin\015\012");
		}
		return TRUE;
	}

	if (config_true(C_POSTONLY_IHAVE)) {
		butwork = TRUE;
	}

	if (ispostonly) {
		if (!feed_posttest(gpoint,ngpoint)) {
			if (!butwork) {
			 	imsg("Not posting upstream because groups don't match feed rule (probably local groups) or no posting feed\n");
	                        chan_write_slave(chan,"441 article didn't match groups in feed rule, or not posting feed\015\012");
	                        return TRUE; 
			}
		}
                if (post_local(anitem,NULL,FALSE)) {
                        imsg("post: Article posted to slave posting mechanism \n");
                        msg_ok("Posted to internal posting feed");
                        feed_doit_if();
                        chan_write_slave(chan,"240 article posted ok\015\012");
                        return TRUE; 
                } else {
                        lprintf(bf,BFSZ-1,"441 %s\015\012",post_err);
                        msg_reject("Post internal failed: %s",post_err);
                        chan_write_slave(chan,bf);
                        return FALSE;
                }
        }


        dmsg("post: It is a local post  (maybe as well) \n");
        if (ismod) {
		feed_setpost(TRUE);
                ncpy(opath,get_ahead(anitem,"Path:"),BFSZ-100);
                ncpy(msgid,get_ahead(anitem,"Message-ID:"),BFSZ-100);
		if (!feed_me(msgid,"",gpoint,NULL,opath,str_len(anitem),anitem)) {
itfailed:		if (is_silent()) {
				nsilent++;
				lprintf(bf,BFSZ-1,"240 article posted ok\015\012");
				emsg("Silently rejected message %.100s %.100s\n",msgid,hsub);
			} else {
		                lprintf(bf,BFSZ-1,"441 %s\015\012",post_err);
			}
                        chan_write_slave(chan,bf);
			feed_setpost(FALSE);
			return FALSE;
		}
#ifdef SPAWN_FILTER
		 if (strlen(config_get(C_FILTER_CMD))>0) {
			r = filter_isok(str_s(anitem),str_len(anitem));
			if (r>=400) goto itfailed; 
		 }
#endif
		
		
		feed_setpost(FALSE);
                if (post_moderated(anitem)) {
                        chan_write_slave(chan,"240 article posted ok\015\012");
                        msg_ok("Post moderated succeeded");
                        feed_doit_if();
                        return TRUE;
                } else {
                        lprintf(bf,BFSZ-1,"441 %s\015\012",post_err);
                        msg_reject("Post moderated failed: %s",post_err);
                        chan_write_slave(chan,bf);
                        return FALSE;
                }
        }
        
        /* Item suck will call reject or ok itself. */
	feed_setpost(TRUE);
	

        if (item_suck(anitem)) {
		feed_setpost(FALSE);
                chan_write_slave(chan,"240 article posted ok\015\012");
                feed_doit_if();
		return TRUE;
        } else {
		feed_setpost(FALSE);
                imsg("post: posting failed, %s \n",post_err);
		if (is_silent()) {
			nsilent++;
			emsg("Silently rejecting post (%s) (%s)\n",hsub,post_err);
			lprintf(bf,BFSZ-1,"240 article posted ok\015\012");
		} else {
			if (config_true(C_POST_IGNOREDUP)) {
		               if (strstr(bf,"Already")!=NULL) 
					lprintf(bf,BFSZ-1,"240 %s\015\012",post_err);
			       else 
					lprintf(bf,BFSZ-1,"441 %s\015\012",post_err);
			} else {
		                lprintf(bf,BFSZ-1,"441 %s\015\012",post_err);
			}
		}
		chan_write_slave(chan,bf);
                return FALSE;
        }
}
int post_is_sender(char *hcon, char *cur_email, char *cur_sender, char *cur_host)
{
	/* cancel <messageid> */
	char sender[BFSZ];
	char from[BFSZ];
	char mid[BFSZ];
	char *s;
	int grp,num;
	char new_host[BFSZ];
	String *head;
	s = strchr(hcon,'<');
	if (s==NULL) return FALSE;
	ncpy(mid,s,BFSZ-1);
	s = strchr(mid,'>'); 
	if (s==NULL) return FALSE;
	s++; *s = 0;

        if (!hist_find(mid,&grp,&num)) {
		imsg("cancel: no such message so can't match sender %.100s\n",mid);
		return FALSE;
	}
        head = item_head(grp,num);
	if (head==NULL) {
		imsg("cancel: no such message body so can't match sender %.100s\n",mid);
		return FALSE;
	}
	ncpy(sender,email_fix(get_ahead(head,"Sender:")),BFSZ-10);	  
	ncpy(from,email_fix(get_ahead(head,"From:")),BFSZ-10);	  
	ncpy(new_host,get_ahead(head,"NNTP-Posting-Host:"),BFSZ-10);	  

	if (strcmp(cur_host,new_host)!=0) {
		imsg("cancel: nntp-posting-host didn't match (%s) (%s)\n",cur_host,new_host);
		return FALSE;
	}
	
	if (strcmp(sender,"<>")!=0) if (strcmp(sender,cur_sender)==0) {
		imsg("cancel: Sender matched (%s)\n",sender);
		return TRUE;
	}
	if (strcmp(from,"<>")!=0) if (strcmp(from,cur_email)==0) {
		imsg("cancel: From matched (%s)\n",from);
		return TRUE;
	}
	imsg("cancel: Sender didn't match (%.100s) (%.100s)  (%.100s) (%.100s)\n",sender,from,cur_sender,cur_email);
	return FALSE;
}
int post_add_block(char *user)
{
	FILE *f;
	char bf[BFSZ];
	char *s;
	int ret = FALSE;
	char fname[BFSZ];
	ncpy(fname,config_get(C_POST_ADD_BLOCK),100);
	if (strlen(fname)==0) return FALSE;
	f = fopen(fname,"r");
	if (f==NULL) {
		filemsg("Could not read (%s)\n",fname);
		return FALSE;
	}
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		s = strtok(bf," \r\n\t");
		if (s==NULL) continue;
		if (strcmp(s,user)==0) ret = TRUE;
	}
	fclose(f);
	return ret;
}
#define reterr(x) {strcpy(post_err,x); return NULL; }
int body_lines(String *item)
{
        char *s = str_s(item);
        int n=0;

        for (;s!=NULL && *s!=0;) {
                if (strncmp(s,EOL,strlen(EOL))==0) break;
                s = strstr(s+1,EOL);
                if (s!=NULL) s+=strlen(EOL);            
        }
        for (;s!=NULL && *s!=0;) {
                n++;
                s = strstr(s,EOL);
                if (s!=NULL) s+=strlen(EOL);            
        }
	if (n>2) n--;
        return n;
}
int quoted_lines(String *item)
{
        char *s = str_s(item);
        int n=0;

        for (;s!=NULL && *s!=0;) {
                if (strncmp(s,EOL,strlen(EOL))==0) break;
                s = strstr(s+1,EOL);
                if (s!=NULL) s+=strlen(EOL);            
        }
        for (;s!=NULL && *s!=0;) {
		if (*s=='>') n++;
		if (*s=='|') n++;
                s = strstr(s,EOL);
                if (s!=NULL) s+=strlen(EOL);           
        }
        return n;
}
char *strip_domain(char *s)
{
	static char bf[BFSZ];
	if (s==NULL) return s;
	if (!config_true(C_AUTH_STRIP_DOMAIN)) return s;
	ncpy(bf,s,200);
	s = strchr(bf,'@');
	if (s!=NULL) *s = 0;
	return bf;
}
char *lib_strip_domain(char *s)
{
	static char bf[BFSZ];
	if (s==NULL) return s;
	ncpy(bf,s,200);
	s = strchr(bf,'@');
	if (s!=NULL) *s = 0;
	return bf;
}
char *get_path_post(void)
{
	static char bf[BFSZ];
	ncpy(bf,config_get(C_PATH_POST),BFSZ-100);
	if (strstr(bf,"!")!=NULL) return bf;
	if (strstr(bf,"not-for-mail")!=NULL) return bf;
	if (strstr(bf,".")==NULL) return bf;
	strcat(bf,"!not-for-mail");
	return bf;
}
String *post_wildrep(String *item)
{
	return item;
}
char *fix_subj_odp(char *s)
{
	static char bf[BFSZ];
	static char lbf[BFSZ];
	ncpy(bf,s,200);
	s = strchr(bf,'\n'); if (s!=NULL) *s = 0;
	s = strchr(bf,'\r'); if (s!=NULL) *s = 0;
	ncpy(lbf,bf,200);
	strlwr(lbf);
	s = bf;
	s = strstr(lbf,"odp:");
	if (s==NULL) return bf;
	s = bf + (s-lbf);
	strcpy(s,"Re:");
	memmove(s+3,s+4,strlen(s+4)+1);
	return bf;
}
String *post_replace(String *item)
{
        static String *newhead;
	char xbf[BFSZ];
	char *e,*s = str_s(item);
        char bf[600];
        char h1[60];
        int i;
	int noskip = FALSE;
/*	char xtmp[BFSZ];*/
	int skip=FALSE;
	int remove;

        str_lock(item);
        if (newhead==NULL) newhead= str_new();
        str_clearbig(newhead);

        for (i=0; i<nhead;i++) hset[i] = FALSE;

        for (;s!=NULL && *s!=0;) {
                 /*ncpy(xtmp,s,20);
		 imsg("Header {%s}\n",xtmp);
		*/
                if (strncmp(s,EOL,strlen(EOL))==0) break;
		if ((*s!=' ') && (*s!='\t')) skip = FALSE;
		remove = FALSE;
                for (i=0; i<nhead; i++) {
			strcpy(h1,head[i].name);
                        if (strncmpnc(s,"message-id",strlen("message-id"))==0) {
			 	strncpy(s,"Message-ID",strlen("Message-ID"));
			}
                        if (strncmpnc(s,h1,strlen(h1))==0) {
                                if (head[i].flag==HOBS) 
                                        reterr("cannot post, obsolete headers found");
                                if (hset[i]) reterr("cannot post, duplicate header");
                                hset[i] = TRUE;
				if (head[i].flag==HREP) remove=TRUE;
				if (config_true(C_ORG_REPLACE)) {
					if (strcmp(head[i].name,"Organization:")==0) {
						remove = TRUE;
						hset[i] = FALSE;
					}
				}
                        }


			if (strlen(config_get(C_POST_STRIP))>0) {
			    char buf[BFSZ];
			    char buf2[BFSZ];
			    char *cp;
			    ncpy(buf,config_get(C_POST_STRIP),BFSZ-10);
			    cp = strtok(buf," \t:,");
			    while( cp != NULL ) {
				ncpy(buf2,cp,BFSZ-10);
				strcat(buf2,":");
				if (strncmpnc(buf2,s,strlen(buf2))==0) {
					dmsg("post: post_strip removing (%s)\n",buf2);
					remove = TRUE;
				}
				cp = strtok(NULL," \t:,");
			    }
			}



			if ((strlen(chan_getuser(pchan))>0) || config_true(C_STRIP_AUTH)) {	
				if (strncmpnc(s,"X-Authenticated-User:",strlen("X-Authenticated-User:"))==0) {
					remove = TRUE;
				}
			}
			if (!xismod) if (strlen(cur_user)>0) if (config_true(C_POST_FROM_ADDUSER)) {
				if (strncmpnc(s,"From:",strlen("From:"))==0) {
					remove = TRUE;
				}
			}
			if (config_true(C_POST_FORCE_DATE)) {
				if (strncmpnc(s,"Date:",strlen("Date:"))==0) {
					remove = TRUE;
				}
			}

			if (strlen(config_get(C_POST_FROM))>0) {	
				if (strncmpnc(s,"From:",strlen("From:"))==0) {
					remove = TRUE;
				}
				if (strncmpnc(s,"Reply-to:",strlen("Reply-to:"))==0) {
					remove = TRUE;
				}
				if (strncmpnc(s,"Sender:",strlen("Sender:"))==0) {
					remove = TRUE;
				}
			}
			if (config_true(C_STRIP_PATH)) {	
				if (strncmpnc(s,"Path:",strlen("Path:"))==0) {
					remove = TRUE;
				}
			}
			if (!isxslave) if (strncmpnc(s,"X-Trace:",strlen("X-Trace:"))==0) {
				if (!remove) imsg("Removing X-Trace header which should not be on post\n");
				remove = TRUE;
			}
			if (config_true(C_STRIP_AUTH_POST) || config_true(C_POST_CANCEL_OWNER) ) {
			if (!isxslave) if (strncmpnc(s,"X-Authenticated-User:",strlen("X-Authenticated-User:"))==0) {
				if (!remove) imsg("Removing X-Authenticated-User header, strip_auth_posts \n");
				remove = TRUE;
			}
			}
                }
		if (config_true(C_FIX_RE)) {
			/* Search for Odp: and replace with Re: */
			if (strncmpnc(s,"Subject: ",strlen("Subject: "))==0) {
				lprintf(xbf,BFSZ-1,"Subject: %.200s\r\n",fix_subj_odp(s+9));
				str_adds(newhead,xbf,strlen(xbf));					
				remove = TRUE;
				noskip = TRUE;
			}
		}
                e = strstr(s+1,EOL); if (e==NULL) reterr("cannot post, no body found"); 
                if (remove || skip) {
                	imsg("Removing line from header %.20s\n",s);
		  	skip = TRUE;
			if (noskip) skip = FALSE;
			noskip = FALSE;
                } else str_adds(newhead,s,e-s+2);
                s = strstr(s+1,EOL);
                if (s!=NULL) s+=strlen(EOL);            
        }

        if (!ispostonly || config_true(C_POSTONLY_IHAVE)) {
	  if (!config_true(C_STRIP_PATH)) {
           if (!hset[HPATH]) { /* Suck fills in the name */
		if (strlen(config_get(C_PATH_POST))>0) {
			lprintf(bf,600-1,"Path: %s\015\012",get_path_post());   str_adds(newhead,bf,strlen(bf));	
		} else {
	                lprintf(bf,600-1,"Path: \015\012",nntp_host);   str_adds(newhead,bf,strlen(bf));
		}
           } 
	  } else {
		if (strlen(config_get(C_PATH_POST))>0) {
			lprintf(bf,600-1,"Path: %s\015\012",get_path_post());   str_adds(newhead,bf,strlen(bf));	
		}
	  }
	}

        if (strlen(xtoslave)==0){
		if (hset[HNNTP]) emsg("Posting host already set, this user may be attempting a spam or other...,%s\n",nntp_host);
		if (config_true(C_ANON_POSTING_HOST)) {
			lprintf(bf,600-1,"NNTP-Posting-Host: %s\015\012",dom_ini_get(C_MYNAME));
			str_adds(newhead,bf,strlen(bf)); 
		} else {
			if (!config_true(C_STRIP_POSTING_HOST)) if(!(config_true(C_STRIP_POSTING_HOST_AUTH) && (strlen(chan_getuser(pchan))>0))) {
				// Chrisp changed to hide the posting host. 
				if (config_true(C_POSTING_HOST_HIDE)) {
					lprintf(bf,600-1,"NNTP-Posting-Host: %s.%s\015\012",hide_user(nntp_host),dom_ini_get(C_MYNAME));
				} else {
					lprintf(bf,600-1,"NNTP-Posting-Host: %s\015\012",nntp_host);
				}
				str_adds(newhead,bf,strlen(bf)); 
			}
		}
	}


	if (feed_thisposting() || config_true(C_NNTP_XPOSTING)) {
		if (!config_true(C_POST_NO_ORIGINAL_HOST)) {
		if (!config_true(C_ANON_POSTING_HOST)) {
                 lprintf(bf,600-1,"X-Original-NNTP-Posting-Host: %s\015\012",nntp_host);
                 str_adds(newhead,bf,strlen(bf)); 
		}}
	}
        if (!hset[HMID]) {
	       ncpy(this_mid,lib_messageid(),BFSZ-1);
               lprintf(bf,600-1,"Message-ID: %s\015\012",this_mid);
               str_adds(newhead,bf,strlen(bf));
        } 

	if (strlen(config_get(C_POST_FROM))>0) {	
               lprintf(bf,600-1,"From: %s\015\012",config_get(C_POST_FROM));
               str_adds(newhead,bf,strlen(bf));
	}
	if (!xismod) if (strlen(cur_user)>0) if (config_true(C_POST_FROM_ADDUSER)) {		
               lprintf(bf,600-1,"From: \"%s\" %s\015\012",cur_user,cur_email);
               str_adds(newhead,bf,strlen(bf));
	}

        if (!hset[HDATE] || config_true(C_POST_FORCE_DATE) ) {
                lprintf(bf,600-1,"Date: %s\015\012",lib_strdate());
                str_adds(newhead,bf,strlen(bf));
        } 
	if (!isxslave) if (!config_true(C_STRIP_POSTING_HOST)) if(!(config_true(C_STRIP_POSTING_HOST_AUTH) && (strlen(chan_getuser(pchan))>0))) {
	  if (!config_true(C_POST_NOTRACE)) {
		  char xhost[BFSZ];
/*                lprintf(bf,600-1,"X-Trace: %s, %s\015\012",lib_strdate(),nntp_host);*/
		  ncpy(xhost,nntp_host,BFSZ-1);
		  if (!config_true(C_XTRACE_IPNAME)) ncpy(xhost,nntp_hostipnum,BFSZ-1);
                lprintf(bf,600-1,"X-Trace: %s %d %s (%s)\015\012",lib_hostnamex(),lib_gmtime(),xhost,lib_strdate());
		str_adds(newhead,bf,strlen(bf));
	  } 
	}

	if (feed_thisposting()) if (!isxslave) if (!config_true(C_STRIP_POSTING_HOST)) if(!(config_true(C_STRIP_POSTING_HOST_AUTH) && (strlen(chan_getuser(pchan))>0))) {
	  if (!config_true(C_POST_NOTRACE)) {
		if (!config_true(C_POST_NO_ORIGINAL_HOST)) {
		 if (!config_true(C_ANON_POSTING_HOST)) {
                  lprintf(bf,600-1,"X-Original-Trace: %s, %s\015\012",lib_strdate(),nntp_host);
                  str_adds(newhead,bf,strlen(bf));
		 }
		}
	  }
	}


        if (strlen(add_approved)>0) {
                str_adds(newhead,add_approved,strlen(add_approved));
        } 

        if (!hset[HDIST]) {
		if (strlen(newdist)>0) {
	                lprintf(bf,600-1,"Distribution: %s\015\012",newdist);
			str_adds(newhead,bf,strlen(bf));
		}
        } 
        if (!hset[HORG]) {
		if (strlen(dom_ini_get(C_ORG))>0) {
                	lprintf(bf,600-1,"Organization: %s\015\012",dom_ini_get(C_ORG));
                	str_adds(newhead,bf,strlen(bf));
		}
        } 
        if (!hset[HLINES]) {
                lprintf(bf,600-1,"Lines: %d\015\012",nlines);
                str_adds(newhead,bf,strlen(bf));
        } 
	if (config_true(C_PIDENT)) {
                lprintf(bf,600-1,"XPident: %s\015\012",chan_getid(pchan));
                str_adds(newhead,bf,strlen(bf));
	}
	if (!config_true(C_STRIP_AUTH)) {	
 	  if (strlen(chan_getuser(pchan))>0) {
		        lprintf(bf,600-1,"X-Authenticated-User: %s\015\012",hide_user(strip_domain(chan_getuser(pchan))));
                str_adds(newhead,bf,strlen(bf));
	  }
	}
	if (!isxslave) {
	 if (strlen(dom_ini_get(C_POST_ADDHEAD1))>0) {
                lprintf(bf,600-1,"%s\015\012",dom_ini_get(C_POST_ADDHEAD1));
                str_adds(newhead,bf,strlen(bf));
	 }
	 if (strlen(dom_ini_get(C_POST_ADDHEAD2))>0) {
                lprintf(bf,600-1,"%s\015\012",dom_ini_get(C_POST_ADDHEAD2));
                str_adds(newhead,bf,strlen(bf));
	 }
	 if (strlen(dom_ini_get(C_POST_ADDHEAD3))>0) {
                lprintf(bf,600-1,"%s\015\012",dom_ini_get(C_POST_ADDHEAD3));
                str_adds(newhead,bf,strlen(bf));
	 }
	 if (strlen(dom_ini_get(C_POST_ADDHEAD4))>0) {
                lprintf(bf,600-1,"%s\015\012",dom_ini_get(C_POST_ADDHEAD4));
                str_adds(newhead,bf,strlen(bf));
	 }
	 if (strlen(dom_ini_get(C_POST_ADDHEAD5))>0) {
                lprintf(bf,600-1,"%s\015\012",dom_ini_get(C_POST_ADDHEAD5));
                str_adds(newhead,bf,strlen(bf));
	 }
	}


        str_adds(newhead,EOL,2);
        str_unlock(item);

        for (i=HDATE+1;i<nhead; i++) {
                if (!hset[i] && (head[i].flag==HREQ)) {
                        lprintf(post_err,BFSZ-1,"Required header missing {%s}",
                                head[i].name);
                        return NULL;
                }
        }
        return newhead;
}
#define DNEWS_LOCAL_COUNT file_work("local.cnt")
char *file_local(int x);
char *file_local(int x)
{
   static char bf[300];
#ifdef VMS
   lprintf(bf,300-1,"%slocal_%d.itm",config_getu(C_WORKAREA),x);
#else
   lprintf(bf,300-1,"%slocal_%d.itm",config_getu(C_WORKAREA),x);
#endif
   return bf;
}
static int lcount;
int post_local(String *xitem, char *addr, int ismod)
{
        FILE *f;
        char gnum[100];
        char fname[400];
        char bf[500];
	int i;
	static int done;
        lcount = 0;
        f = xfopen(DNEWS_LOCAL_COUNT,"r");
        if (f!=NULL) {
                fgets(bf,199,f);
                lcount = atoi(bf);
                xfclose(f); f = NULL;
        }
	if (!done) if (lcount>500) {
		done = TRUE;
		if (lcount>90000) lcount = 90000;
		imsgf("Clearing local*.itm files %d\n",lcount);
		for (i=0; i<lcount; i++) {
			remove(file_local(i));
		}
		imsgf("Clearing local*.itm files %d finished\n",lcount);
		lcount = 1;
	}
        remove(DNEWS_LOCAL_COUNT);
        f = xfopen(DNEWS_LOCAL_COUNT,"w");
        if (f!=NULL) {
                fprintf(f,"%d\n",lcount+1);
                xfclose(f); f = NULL;
        }

        strcpy(fname,file_local(lcount));
        lib_createdir(fname);
        dmsg("post: Posting item to file Length %d \n",str_len(xitem)); 
        if (!str_write_to(xitem,fname,addr)) {
                emsg("post: Unable to write article file %s \n",fname);
                return FALSE;
        }
        lprintf(gnum,100-1,"#local:%d",lcount);
        if (!feed_local(gnum,ismod)) {
		imsg("post: Local post failed for some reason\n");	
	} else {
		imsg("post: local post succeded it seems\n");
	}
        return TRUE;
}
int str_write_to(String *xitem, char *fname, char *addr)
{
        FILE *f;
        int chunk;
        static int sz=0;
        int len = str_len(xitem);
        
        sz = 0;

        f = xfopen(fname,"wb");
        if (f==NULL) return FALSE;
        
        if (addr!=NULL) fprintf(f,"To: %s\015\012",addr);
        for (;sz<len; ) {
                chunk = 100;
                if (sz+chunk>len) chunk = len-sz;
                dmsg("post: Writing buffer %d bytes \n",chunk);
                if (fwrite(str_s(xitem)+sz,1,chunk,f)!= (size_t) chunk) {
                        xfclose(f); return FALSE;
                }
                sz += chunk;
        }
        xfclose(f);
        return TRUE;
}
int post_delete(int n)
{
        char fname[400];
        strcpy(fname,file_local(n));
        remove(fname);
        return TRUE;
}
String *post_getlocal(int n)
{
        char fname[400];
        static String *lcl;

        strcpy(fname,file_local(n));
        if (lcl==NULL) lcl = str_new();
        str_clear(lcl);
        if (str_read(lcl,fname)) return lcl;
        filemsg("Can't read {%s} \n",fname);
        return NULL;
}
#define TEMP_FILE file_work("postmod.tmp")
int post_moderated(String *anitem) 
{
        char bf[BFSZ],addr[BFSZ],subj[BFSZ],gname[BFSZ];
        char *s;
	char mid[BFSZ];

	ncpy(mid,get_ahead(anitem,"Message-ID:"),100);
	if (strstr(mid,"<fake@netwin.com.nz>")!=NULL) {
		emsg("Ooops, thats a fake downloading message, %s I wont send that to the moderator\n",mid);
		return TRUE;
	}

        ncpy(bf,mod_address(group_name(modgrp)),BFSZ-1);
        ncpy(gname,group_name(modgrp),BFSZ-1);
        for (s=gname; *s!=0; s++) if (*s=='.') *s = '-';
        lprintf(addr,BFSZ-1,bf,gname);

        remove(TEMP_FILE);
        str_write(anitem,TEMP_FILE);
        ncpy(subj,get_ahead(anitem,"Subject:"),100);
        wmsg("post: Mail file to {%s} subj {%s} \n",addr,subj);

        if (feed_smtp()) { /* if an smtp feed is defined, use it*/
                post_local(anitem,addr,TRUE);
                return TRUE;
        } 
        if (!lib_mail_file(addr,subj,TEMP_FILE)) return FALSE;
        tell_user(anitem,NULL,"Mailed to moderator");
        return  TRUE;
}
void report_spammer(String *anitem, char *reason)
{
	FILE *f;
	char subj[BFSZ];
	if (config_true(C_SPAM_NOREPORT)) return;
	if (config_true(C_SPAM_REPORT_BRIEF)) {
		imsg("Reporting Spammer to brief log file %s %s\n",config_get(C_MANAGER),SPAM_BRIEF);
		f = fopen(SPAM_BRIEF,"a");
		if (f==NULL) return;
		fprintf(f,"From: %.100s\n",get_ahead(anitem,"From:"));
		fprintf(f,"Subj: %.100s\n",get_ahead(anitem,"Subject:"));
		fprintf(f,"Newsgroups: %.100s\n",get_ahead(anitem,"Newsgroups:"));
		fprintf(f,"Reason: %.100s\n\n",reason);
		fprintf(f,"Date: %.100s\n",lib_email_date());
		fclose(f);
		return ;
	}
	imsg("Reporting Spammer to %s\n",config_get(C_MANAGER));
	f = fopen(TEMP_FILE,"w");
	if (f==NULL) return;
	imsg("Reporting Spammer2 to %s\n",config_get(C_MANAGER));
	if (strlen(config_get(C_FROM))==0) {
		fprintf(f,"From: %s\n",config_get(C_MANAGER));
	} else {
		fprintf(f,"From: %s\n",config_get(C_FROM));
	}
	fprintf(f,"To: %s\n",config_get(C_MANAGER));
	fprintf(f,"Date: %s\n",lib_email_date());
	lprintf(subj,BFSZ-1,"Local Spammers post blocked %.80s\n",get_ahead(anitem,"From:"));
	fprintf(f,"Subject: %s\n",subj); 
	fprintf(f,"\n");
	fprintf(f,"The following LOCAL POST was blocked by the spam rules\n");
	fprintf(f,"    %.90s\n",reason);
	fprintf(f,"You either have a local spammer who needs to be found\n");
	fprintf(f,"Or your spam rules, filter.dat etc may need adjusting \n");
	fprintf(f,"Examine dnews.in and dnews.log for more details\n");
	fprintf(f,"%s\n",msg_reason());
	fprintf(f,"Set spam_noreport true in dnews.conf to stop these emails\n");
	fprintf(f,"First 2000 characters of spam item follows\n");
	fprintf(f,"\n");
	fprintf(f,"%.2000s",str_s(anitem));
	fprintf(f,"\n");
	fclosep(f);
	lib_mail_file(config_get(C_MANAGER),subj,TEMP_FILE);
	imsg("Reporting Spammer3 to %s\n",config_get(C_MANAGER));

}
#ifdef NETWARE
#define MODERATORS file_config("moderate.con")
#else
#define MODERATORS file_config("moderators.conf")
#endif
char *mod_address(char *gname)
{
        static Sary *afile;
        int n;
        int i;
        char bf[BFSZ],gwild[BFSZ];
        static char address[BFSZ];              
        char *p[100];
        
        if (afile!=NULL) s_free(afile);
        afile = s_alloc();
        s_read(afile,MODERATORS);

        strcpy(address,config_get(C_MANAGER));

        for (i=0; i<s_len(afile); i++) {
                ncpy(bf,s_line(afile,i),BFSZ-1);
                strlwr(bf);

                n = token_split(bf,p,": \t\n",90);
                if (n!=2) continue;
                if (strncmp(p[0],"#",1)==0) continue;
                if (strncmp(p[0]," ",1)==0) continue;

                ncpy(gwild,p[0],BFSZ-1);
                ncpy(address,p[1],BFSZ-1);
        
                if (group_match(gwild,gname)) {
                        ncpy(address,p[1],BFSZ-1);
                        return address;
                }
        }
        return address;
}

int post_spawn_filter(String *xitem)
{
	char f1[BFSZ];
	char f2[BFSZ];
	char cmd[BFSZ];
	char bf[BFSZ];
	FILE *f;
	int r;
	int pid;
	if (strlen(config_get(C_POST_SPAWN))==0) return FALSE;

	ncpy(f1,file_work("posttmp.in"),BFSZ-1);
	ncpy(f2,file_work("posttmp.out"),BFSZ-1);
	remove(f2);
	lprintf(cmd,300,"%s %s %s",config_get(C_POST_SPAWN),f1,f2);
	
	f = fopen(f1,"wb");
	if (f==NULL) {
		filemsg("Could not write file (%s)\n",f1);
		return FALSE;
	}
	if ((int) fwrite(str_s(xitem),1,str_len(xitem),f)!=str_len(xitem)) {
		filemsg("Could not write file completely (%s)\n",f1);
		fclose(f);
		return FALSE;
	}
	fclose(f);

	imsgf("post_spawn (%s)\n",cmd);
	pid = lib_spawn_cmd(cmd);
	lib_spawn_wait_finished(pid);
	imsgf("post_spawn_returned\n");
	str_clear(xitem);
	f = fopen(f2,"rb");
	if (f==NULL) {
		imsgf("post_spawn did not create an output file\n");
		return TRUE;
	}
	for (;!feof(f);) {
		r = fread(bf,1,BFSZ-1,f);
		if (r<=0) break;
		str_adds(xitem,bf,r);
	}
	fclose(f);
	return TRUE;
}

static int h_key;
char *h_table="abcdefghijklmnopqrstuvwxyz0123456789$-_";
char *unhide_user(char *user)
{
	char *s;
	static char bf[BFSZ];
	int c;
	int k;
	int r;
	char *out=bf;
	int max = strlen(h_table);
	h_key = atoi(config_get(C_HIDE_KEY));
	if (h_key==0) return user;
	srand(h_key);
	if (strncmp(user,"$$",2)!=0) return user;
	user += 2;
	for (s=user;*s!=0;s++) {
		c = *s;
		for (k=0; h_table[k]!=0; k++) if (h_table[k]==c) {c = k; break;}
		if (h_table[k]==0) c = k-1;
		r = rand()%max;
		if (r<0) r = -r;
		c += max;
		c -= r;
		if (c<0) c += max;
		c = c % max;
		if (c<0) c = 0;
		*out++  = h_table[c];
	}
	*out++ = 0;
	return bf;
}
char *hide_user(char *user)
{
	static char bf[BFSZ];
	char *s;
	int c;
	int k;
	int r;
	char *out=bf;
	int max = strlen(h_table);
	h_key = atoi(config_get(C_HIDE_KEY));
	if (h_key==0) return user;
	srand(h_key);
	*out++ = '$';
	*out++ = '$';
	for (s=user;*s!=0;s++) {
		c = tolower(*s);
		for (k=0; h_table[k]!=0; k++) if (h_table[k]==c) {c = k; break;}
		if (h_table[k]==0) c = k-1;
		r = rand()%max;
		if (r<0) r = -r;
		c += r;
		c = c % max;
		if (c<0) c = 0;
		*out++  = h_table[c];
	}
	*out++ = 0;
	return bf;
}

