/* Copyright (C) 1995 NetWin, New Zealand, All rights reserved. */
/*
  What is quoted printable format, must detec 

Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

   =3D --> = 
   =  --> newline


Test on cern on hortnet. test turning logging off
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#ifdef WIN32
#include <windows.h>
#include <io.h>
#endif
#ifdef OS2
#include <io.h>
#endif
#include <fcntl.h>
#include "dnews.h"
#include "conf.h"
#include "lib.h"
#include "emsg.h"
#include "nntp.h"
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
static int dodebug=FALSE;
/*
Commands that we need to support
	xover comp.os.vms [nnn-nnn]  Default is last 20 messages in group 
	article comp.os.vms nnn
	post message
	list comp.os.*  		Just show the ones at that level
*/
int decode_base64(char *buf, char *out);
int decode_uu(char *line, char *out);

void show_dline(char *s);
void r_listall(void);
void netwin_advert(void);
void f_follow(int follow);
char *xstrtok(char *line, char *in);
char *towww(char *s);
void r_find(void);  /* from=nnn   max=nnn*/
void f_find(void);
void show_xsearch(void);
void f_search(void);
void r_search(void);
void f_post(void);
void h_printf(char * arg_list, ...);
void db_setscript(char *script);
int show_headfile(char *type, char *title);
void show_end(char *type);
int  show_top(char *type, char * arg_list, ...);
void show_article2(void);
char *lib_encode(char *s);
char *value_encode(char *s);
char *mygetenv(char *v);
char *query_find(char *s);
int query_get(void);
void getword(char *word, char *line, char stop) ;
char x2c(char *what) ;
char *email_name(char *email);
void unescape_url(char *url) ;
void plustospace(char *str) ;
void tpl_quote_body(void);
int tpl_show_file(char *xfname);

char *tpl_replace(char *bf);
void tpl_init(void);
char *tpl_replace_vars(char *orig);
void tpl_replace_var(char *bf, int len, char *var);
void tpl_show_list(void);
void tpl_init_item(void);
void tpl_show_item(void);
void tpl_init_sbar(int from, int to, int total, char *key);


void tpl_show_group(void);
void tpl_set_buttons(void);
void tpl_show(char *xfname);

void var_set(char *var, char *val);
char *var_find(char *var);


void show_sbar(int from, int to, int total, char *key);
void form_get(void);
char *form_find(char *s);
void show_article(void);


static char path_script[BFSZ];
static char rootname[BFSZ];
int ini_read(char *s);
int ini_find(char *s);
char *ini_get(int x);
void r_post(void);
void zmsg(char * arg_list, ...);

void r_article(void);
int read_lines(void);
int read_status(void);
int read_xover(void);
void show_xover(void);
void r_xover(void);  /* from=nnn   max=nnn*/
void r_list(void);
char *mygetenv(char *s);

char *h_date_trim(char *instr); 
char *h_trim(char *s, int len);
char *h_from_trim(char *s);


#ifdef WIN32
#define FIX_STDOUT TRUE
#endif

#ifdef OS2
#define FIX_STDOUT TRUE
#define _setmode setmode
#endif

void xover_add(char *item, char *subject, char *from, char *lines, char *date, char *mid, char *ref);
void xover_showord(void);
 
enum {I_HEADERS, I_XOVER_PRE, I_XOVER_POST, I_NEWSHOST, I_DEBUG, I_PAGESIZE
	, I_GROUPS, I_ALLOW, I_SORT, I_NOADVERT, I_SEARCH, I_CGI_PATH, I_TEMPLATES
	, I_POST_GROUPS, I_POST_USERS};
char *ini_names[]={
	"headers","xover_pre","xover_post","newshost", "debug", "pagesize"
	,"groups", "allow" , "sort", "noadvert", "search", "cgi_path"
	,"templates" 
	,"post_groups", "post_users"
	, NULL
};

#define PAGESIZE page_size()
int page_size(void)
{
	int i = atoi(ini_get(I_PAGESIZE));
	if (i==0) i = 40;
	return i;
}

static int x_item[2000];
static char *x_score[2000];
static char *x_group[2000];
static double x_f[2000];
static int x_fi[2000];
static int x_depth[2000];
static char *x_subject[2000];
static char *x_subjectsrt[2000];
static char *x_from[2000];
static char *x_date[2000];
static char *x_ref[2000];
static char *x_lines[2000];
static char *x_mid[2000];
static int nitem;

void emsg_reset(void);

static int chan;
static char thisuser[BFSZ];
static char group[BFSZ];
static int gfrom,gto,gmax,group_from,group_to;
static int dosort;
static char log_file[BFSZ];
static int search_result = FALSE;
int main(int argc, char *argv[])
{
	char method[BFSZ];
	char cmd[BFSZ];

	strcpy(log_file,"dnewsweb.log");
	db_setscript(mygetenv("SCRIPT_NAME"));
	sprintf(log_file,"%s.log",rootname);
	if (!ini_read(argv[0])) return 0;
	if (strlen(ini_get(I_CGI_PATH))>0) {
	 	strcpy(path_script,ini_get(I_CGI_PATH));
	}
	
	if (strcmp("true",ini_get(I_DEBUG))==0) dodebug = TRUE;
	if (strcmp("true",ini_get(I_SORT))==0) dosort = TRUE;
	if (!dodebug) emsg_reset();
	imsg("DNEWSWEB, Startup, Looking for winsock dll\n");
	nntp_startup();
	nntp_alwaysblock();

	strcpy(thisuser,mygetenv("REMOTE_USER"));
	strcpy(method,mygetenv("REQUEST_METHOD")); strlwr(method);
	query_get(); /* Get the ?xxx=yyy... parameters  */
	if (strcmp(method,"post")==0) {
imsg("Processing POST\n");
		printf("Pragma: cache\n");
		printf("Content-type: text/html\n\n");
	        chan = nntp_open(ini_get(I_NEWSHOST));
	        if (chan<0) { goto failed;}
	        nntp_setblock(chan,TRUE); /* Set to blocking IO */

		form_get(); /* get any form fields */
		strcpy(cmd,form_find("cmd"));
	     	if (strcmp(cmd,"post")==0) r_post();
	} else {
imsg("Processing command (%s)\n",query_find("cmd"));
		strcpy(cmd,query_find("cmd"));
		if (strcmp(cmd,"article")!=0) {
			printf("Pragma: cache\n");
			printf("Content-type: text/html\n\n");
		}
	        chan = nntp_open(ini_get(I_NEWSHOST));
	        if (chan<0) { goto failed;}
	        nntp_setblock(chan,TRUE); /* Set to blocking IO */

		     if (strcmp(cmd,"xover")==0) r_xover();
		else if (strcmp(cmd,"list")==0) r_list();
		else if (strcmp(cmd,"listall")==0) r_listall();
		else if (strcmp(cmd,"article")==0) r_article();
		else if (strcmp(cmd,"post")==0) f_post();
		else if (strcmp(cmd,"search")==0) f_search();
		else if (strcmp(cmd,"f_search")==0) f_search();
		else if (strcmp(cmd,"r_search")==0) r_search();
		else if (strcmp(cmd,"r_find")==0) r_find();
		else if (strcmp(cmd,"xsearch")==0) r_find();
		else if (strcmp(cmd,"f_find")==0) f_find();
		else if (strcmp(cmd,"follow")==0) f_follow(TRUE);
		else r_list();
	}
	imsg("Program finished ok\n");
	return 0;
failed:
	if (strcmp(cmd,"article")==0) {
		printf("Pragma: cache\n");
		printf("Content-type: text/html\n\n");
	}
	printf("Could not open connection to your news server (%s). \n",ini_get(I_NEWSHOST));
	printf("It may be down at the moment, in which case you could try\n");
	printf("again later.  Also check you have given the correct name in dnewsweb.ini\n");
	return 0;
}

/*
        sprintf(bf,"group rec.humor\015\012"); nntp_write_s(c,bf,strlen(bf)); 
        sprintf(bf,"article\015\012"); nntp_write_s(c,bf,strlen(bf)); 
        sprintf(bf,"quit\015\012"); nntp_write_s(c,bf,strlen(bf)); 
        n = nntp_read(c,out,1000);
        for (;n>=0;) {
		out[n] = 0;
                printf("%s",out);
                n = nntp_read(c,out,1000);
              
        }
	return 0;
*/

static String *instr;
int read_lines(void)
{
	int n;
	char bf[3000];
        char *s;
wantmore:
        n = nntp_read(chan,bf,2000);
	if (n<0) return FALSE;
	bf[n] = 0;
	str_addsn(instr,bf);

	s = str_s(instr) + str_len(instr) - 1;
	dmsg("Last char {%d}\n",*s);
	if (*s != '\012') goto wantmore;
	return TRUE;
}
static char nntp_status[BFSZ];
int group_match(char *a, char *b);
int group_allow(char *group)
{
	char bf[BFSZ];
	char g[BFSZ];
	ncpy(g,group,BFSZ-1);
	ncpy(bf,ini_get(I_ALLOW),BFSZ-1);
	if (!group_match(bf,g)) {
		printf("Sorry, you are not permitted access to (%s) ",group);
		printf("via this DNEWSWEB gateway\n");
		return FALSE;
	}
	return TRUE;
}
int read_status(void)
{
	char *s,*s2;
	int slen;
	if (instr==NULL) instr = str_new();
	str_copysn(instr,"");	
	read_lines();
	s = str_s(instr);
	s2 = strchr(s,'\012');
	if (s2==NULL) return FALSE;
	*s2 = 0;
	ncpy(nntp_status,s,BFSZ-1);
	slen = s2-s+1;
	if (str_len(instr)>(slen)) {
	 	memmove(s,s2+1,str_len(instr)-slen+1);
		str_setlen(instr,str_len(instr)-slen);
	} else 	str_copysn(instr,"");	
	if (nntp_status[0]=='2') return TRUE;
	if (nntp_status[0]=='3') return TRUE;
	return FALSE;
}
char *get_line(String *instr)
{
	static char bf[BFSZ];
	char *s;
	char *s2;
	int slen;
	s = str_s(instr);
	if (s==NULL) return NULL;
	s2 = strchr(s,'\012');
	if (s2==NULL) return NULL;
	*s2 = 0;
	ncpy(bf,s,BFSZ-1);
	slen = s2-s+1;
	if (str_len(instr)>(slen)) {
	 	memmove(s,s2+1,str_len(instr)-slen+1);
		str_setlen(instr,str_len(instr)-slen);
	} else 	str_copysn(instr,"");	

	return bf;
}
int read_xover(void)
{
	char *s;
	dmsg("inside read_xover\n");
	if (instr==NULL) instr = str_new();
	for (;TRUE;) {
		dmsg("inside read_xover\n");
		if (str_len(instr)==3) if (strcmp(str_s(instr),".\015\012")==0) break;
		if (str_len(instr)>=5) {
			s = str_s(instr) + str_len(instr) - 5;
			if (strcmp(s,"\015\012.\015\012")==0) break;
			if (strcmp(s,"\015\012.\015\012")==0) break;
		}

		read_lines();
	}
	if (str_len(instr)>3) str_setlen(instr,str_len(instr)-3);
        return TRUE;
}
void show_xline(char *s);
void show_xbar(void)
{
	printf("<table border>"); 
	printf("<tr>");
	if (gfrom>group_from) { 
		printf("<td><a href=\"%s?cmd=xover&group=%s&from=%d\">Previous Page</a>",path_script,towww(group),gfrom-gmax);
	}
	if (gto<group_to) { 
		printf("<td><a href=\"%s?cmd=xover&group=%s&from=%d\">Next Page</a>",path_script,towww(group),gto);
		printf("<td><a href=\"%s?cmd=xover&group=%s\">Recent Items</a>",path_script,towww(group));
	} 
	printf("<td><a href=\"%s?cmd=list\">Standard Groups</a>",path_script);
	printf("<td><a href=\"%s?cmd=listall\">All Groups</a>",path_script);
	printf("<td><a href=\"%s?cmd=post&group=%s\">Post</a>",path_script,towww(group));
	printf("<td><a href=\"%s?cmd=f_find&group=%s\">Search Group</a>",path_script,towww(group));
	printf("</table> \n");
}
void tpl_init_sbar(int from, int to, int total, char *key)
{
	char bf[BFSZ];
	if (from>1) { 
		sprintf(bf,"%s?cmd=r_search&key=%s&from=%d&to=%d",path_script,lib_encode(key),from-20,from);
		var_set("b_prev",bf);
	}
	if (to<total) { 
		printf(bf,"%s?cmd=r_search&key=%s&from=%d&to=%d",path_script,lib_encode(key),to,to+20);
		var_set("b_next",bf);
	} 
	sprintf(bf,"%d",from);
	var_set("from",bf);
	sprintf(bf,"%d",to);
	var_set("to",bf);
}
void show_sbar(int from, int to, int total, char *key)
{
	printf("<table border>"); 
	printf("<tr>");
	if (from>1) { 
		printf("<td><a href=\"%s?cmd=r_search&key=%s&from=%d&to=%d\">Previous Page %d-%d</a>",path_script,lib_encode(key),from-20,from,from-20,from);
	}
	if (to<total) { 
		printf("<td><a href=\"%s?cmd=r_search&key=%s&from=%d&to=%d\">Next Page %d-%d</a>",path_script,lib_encode(key),to,to+20,to,to+20);
	} 
	printf("<td><a href=\"%s?cmd=list\">Standard Groups</a>",path_script);
	printf("<td><a href=\"%s?cmd=listall\">All Groups</a>",path_script);
	printf("<td><a href=\"%s?cmd=f_find&group=%s\">Search Group</a>",path_script,towww(group));
	printf("</table> \n");
}
void show_xover(void)
{ 
	char *s;


	s = get_line(instr);
	for (;s!=NULL;) {
		dmsg("Show sxline %s\n",s);
		show_xline(s);
		s = get_line(instr);
	}
}
void show_search(void)
{ 
	char *s;

	s = get_line(instr);
	for (;s!=NULL;) {
		show_xline(s); /* decode xover information */
		s = get_line(instr);
	}
	show_xsearch();
}


void show_xsearch(void)
{
	int i;
	char bf[BFSZ];
	char *s;
	printf("<ul>\n");
	for (i=0; i<nitem; i++) {
		ncpy(bf,x_group[i],BFSZ-1);
		s = strchr(bf,':'); if (s!=NULL) {*s=0; s++;} else s = bf;
  		printf("<li>Score %s, <a href=\"%s?cmd=article&group=%s&item=%s\">",x_score[i],path_script,towww(bf),s);
		printf("%s</a> - %s (%s) <br>\n",value_encode(x_subject[i]), email_name(x_from[i]),x_lines[i]);
	}
	printf("</ul>\n");

}


void show_dlist(void)
{ 
	char *s;


	s = get_line(instr);
	for (;s!=NULL;) {
		show_dline(s);
		s = get_line(instr);
	}
}
void netwin_advert(void)
{
	printf("<hr> <FONT SIZE=-2>\n");
	printf("<a href=\"http://netwinsite.com/\"> ");
	printf("DNEWSWEB %s Copyright &copy; <I>NetWin Ltd</I>\n",DNEWS_VERSION);
	printf("</a> \n");
	printf("</FONT> \n");
}
int head_get(char *field, char *val, char *line)
{
 	char bf[BFSZ];
	ncpy(bf,line,BFSZ-1);
	strlwr(bf);
	if (strncmp(bf,field,strlen(field))==0) {
		ncpy(val,line+strlen(field)+1,BFSZ-1);
	}
	return TRUE;
}
#define MAXIMG 500000
void show_article_part(int wanted);
void show_article_part(int wanted)
{ 
	char *s;
	char *s2;
	int gothead;       
	int n;
	char subject[BFSZ];
	int isuu = FALSE;
	char from[BFSZ];
	char date[BFSZ];
	char boundary[BFSZ];
	char content[BFSZ];
	char newsgroups[BFSZ];
	char *out;
	int nout;
	int item = atoi(query_find("item"));
	int related = item;
	int got=0;

	if (wanted==99) wanted = 0;
	out = mymalloc(MAXIMG);
	strcpy(boundary,"none");
	strcpy(subject,"");
	strcpy(from,"");
	strcpy(content,"");
	strcpy(date,"");
	strcpy(newsgroups,"");
	related = item ;
	if (related<1) related = 1;
#ifdef FIX_STDOUT
	_setmode(_fileno(stdout),_O_BINARY);
#endif
/*	 printf("Content-type: text/html\n\n"); */
/*  fopen
	printf("MIME-Version: 1.0\n"); */

/*	printf("Content-Type: image/x-xbitmap\n");
	printf("\n");
	printf("#define back_width 20\n");
	printf("#define back_height 23\n");
	printf("static char back_bits[] = {\n");
	printf(" 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0f,0x00,0x80,0x0f, \n");
	printf(" 0x00,0x80,0x0f,0x00,0x80,0x0f,0x00,0x80,0x0f,0x00,0x80,0x0f,0x60,0x80,0x0f, \n");
	printf(" 0x70,0x80,0x0f,0x78,0x00,0x00,0xfc,0xff,0x0f,0xfe,0xff,0x07,0xff,0xff,0x03, \n");
	printf(" 0xfe,0xff,0x01,0xfc,0xff,0x00,0x78,0x00,0x00,0x70,0x00,0x00,0x60,0x00,0x00, \n");
	printf(" 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; \n");
return;
	printf("Content-type: image/jpeg; name=\"test.jpg\"\n");
	f = fopen("test.jpg","rb");
	if (f==NULL) {perror("test.jpg"); return;}
	n = fread(bf,1,100000,f);

	printf("Content-length: %d\n",n); 
	printf("\n");
	fwrite(bf,1,n,stdout);
	return;
*/




	s = get_line(instr);		 	
	for (;s!=NULL;) {
		s2 = strchr(s,'\015') ;
		if (s2!=NULL) *s2 = 0;
		s2 = strchr(s,'\012') ;
		if (s2!=NULL) *s2 = 0;
		if (strlen(s)==0) break;
		s2 = strstr(s,"boundary=");
		if (s2==NULL) s2 = strstr(s,"Boundary=");
		if (s2!=NULL) {
		 	s2 = strchr(s2,'"');
			strcpy(boundary,"--");
			if (s2!=NULL) ncpy(boundary+2,s2+1,BFSZ-10);
			s2 = strchr(boundary,'"');
			if (s2!=NULL) *s2 = 0;
			head_get("content-type:",content,s);
		}
		/* head_get("subject:",subject,s); */
		s = get_line(instr);
	}
	nout = 0;
	if (wanted==0) {
		emsg("Content-type: %s\n",content);
		printf("Content-type: image/jpeg; name=\"test.jpg\"\n");
		gothead = TRUE;
	}
	for (;s!=NULL;) {
		s2 = strchr(s,'\015') ;
		if (s2!=NULL) *s2 = 0;
		s2 = strchr(s,'\012') ;
		if (s2!=NULL) *s2 = 0;
		
		if (!isuu) {
		 if (strncmp(s,"begin",5)==0) {
		 	isuu = TRUE;

			strlwr(s);
			if (strstr(s,".gif")!=NULL)  {
				printf("Content-type: image/GIF\n");
			} else {
				printf("Content-type: image/JPEG\n");
			}
		  	
		 }
		} else { 
		  if (isuu) {
			if (strncmp(s,"end",3)==0) break;
			if (strcmp(s,"`")==0) break;
			n = decode_uu(s,out+nout);
			/* emsg("Decode %d bytes {%s} \n",n,s);*/
			nout += n;
		  }
		}
		
		if (!isuu) {
		if (strstr(s,boundary)!=NULL) {gothead = FALSE; got++;}
		else if (got==wanted) {
		 if (strlen(s)==0) gothead = TRUE;
		 else {
			if (!gothead) printf("%s\n",s);
			else {
			  n = decode_base64(s,out+nout);
			  if (n<0) emsg("Base 64 encoding is corrupt {%s} \n",s);
			  nout += n;
			}
		 }
		} 
		if (nout<0) break;
		}
		s = get_line(instr);
	}

	printf("Content-length: %d\n",nout); 
	printf("\n");
	fwrite(out,1,nout,stdout);
	return;
}
void show_article2(void)
{ 
	char *s,*x;
	char subject[BFSZ];
	static char from[BFSZ];
	static char date[BFSZ];
	static char mime[BFSZ];
	static char content[BFSZ];
	static char encoding[BFSZ];
	char bf[BFSZ];
	int isimage = FALSE;
	char *s2;
	int gothead = FALSE;
	int multi,got,istext;
	static char boundary[BFSZ];
	char newsgroups[BFSZ];
	int item = atoi(query_find("item"));
	int related = item;
	int part = atoi(query_find("part"));
	if (part>0) { show_article_part(part);	return;}

	multi = got = istext = FALSE;
	strcpy(subject,"");
	strcpy(from,"");
	strcpy(date,"");
	strcpy(newsgroups,"");
	related = item;

	printf("Pragma: cache\n");
	printf("Content-type: text/html\n\n");

	show_top("article","Newsgroup Article %s:%s ",group,query_find("item")); 
	printf("<table border>\n"); 
	printf("<tr> \n");
	printf("<td><a href=\"%s?cmd=xover&group=%s&related=%d\">Related Items</a>\n",path_script,towww(group),related);
	printf("<td><a href=\"%s?cmd=xover&group=%s\">Recent Items</a>\n",path_script,towww(group));
	printf("<td><a href=\"%s?cmd=list\">Standard Groups</a>\n",path_script);
	printf("<td><a href=\"%s?cmd=listall\">All Groups</a>\n",path_script);
	printf("<td><a href=\"%s?cmd=follow&group=%s&item=%s\">Post Followup</a>\n",path_script,towww(group),query_find("item"));
	printf("</table> \n");



	s = get_line(instr);
	for (;s!=NULL;) {
		if (strcmp(s,"\015")==0) break;
		head_get("subject:",subject,s);
		head_get("date:",date,s);
		head_get("from:",from,s);
		head_get("newsgroups:",newsgroups,s);
		head_get("mime-version:",mime,s);
		head_get("content-type:",content,s);
		head_get("content-transfer-encoding:",encoding,s);

		s2 = strstr(s,"boundary=");
		if (s2==NULL) s2 = strstr(s,"Boundary=");
		if (s2!=NULL) {
		 	s2 = strchr(s2,'"');
			strcpy(boundary,"--");
			if (s2!=NULL) ncpy(boundary+2,s2+1,BFSZ-10);
			s2 = strchr(boundary,'"');
			if (s2!=NULL) *s2 = 0;
		}

		s = get_line(instr);
	}

	printf("<table >"); 
	printf("<tr> <td align=right><b>Subject:</b><td>%s\n",value_encode(subject));
	printf("<tr> <td align=right><b>Date:</b><td>%s\n",date);
	printf("<tr> <td align=right><b>From:</b><td>%s\n",value_encode(from));
	printf("<tr> <td align=right><b>Newsgroups:</b><td>%s\n",newsgroups);
	printf("</table> \n");

	strlwr(encoding);
	multi = FALSE;
	if (strlen(mime)>0) if (strlen(boundary)>0) multi = TRUE;

	if (!multi) if (strstr(encoding,"base64")!=NULL) {
		printf("<img src=\"%s?cmd=article&group=%s&item=%d&part=99\">",path_script,towww(group),item);
		goto endbit;
	}
	got = 0;
	if (multi) {
	  for (;s!=NULL;) {
		s2 = strchr(s,'\015') ;	if (s2!=NULL) *s2 = 0; s2 = strchr(s,'\012') ; if (s2!=NULL) *s2 = 0;
		if (strstr(s,boundary)!=NULL) {
			if (istext) printf("</pre>\n");
			gothead = FALSE; istext = FALSE; got++;
			isimage = FALSE;
		}
		if (strlen(s)==0) {
			if (istext) printf("<pre>\n");
			gothead = TRUE;
			if (isimage) 
			  printf("<img src=\"%s?cmd=article&group=%s&item=%d&part=%d\">",path_script,towww(group),item,got);
		}
		else {
			if (!gothead) {
				/* printf("%s<br>\n",s);*/
				strlwr(s);
				if (strstr(s,"text/")!=NULL) istext = TRUE;
				if (strstr(s,"image/")!=NULL) isimage = TRUE;
			} else {
				if (istext) {
					printf("%s",value_encode(s));
				}
			}
		}
		s = get_line(instr);
	  }
 	  goto endbit;
	}

	printf("<pre>\n");
	s = get_line(instr);
	isimage = FALSE;
	for (;s!=NULL;) {
		if (strncmp(s,"begin ",6)==0) {
			ncpy(bf,s,BFSZ-1);
			x = strtok(bf," \012\015"); if (x==NULL) goto skip;
			x = strtok(NULL," \012\015"); if (x==NULL) goto skip;
			if (atoi(x)<100) goto skip;
			x = strtok(NULL," \012\015"); if (x==NULL) goto skip;
			strlwr(x);
			if (strstr(x,".jpg")!=NULL) goto isok;
			if (strstr(x,".jpeg")!=NULL) goto isok;
			if (strstr(x,".gif")!=NULL) goto isok;
			goto skip;
isok:			isimage = TRUE;
			printf("<img src=\"%s?cmd=article&group=%s&item=%d&part=100\">",path_script,towww(group),item,got);
    		}
skip:		if (!isimage) printf("%s",value_encode(s));
		if (isimage) {
		 	if (strncmp(s,"end",3)==0) isimage = FALSE;
		}
		s = get_line(instr);
	}
	printf("</pre>\n");

endbit:
	printf("\n");
	printf("<table border>"); 
	printf("<tr> ");
	printf("<td><a href=\"%s?cmd=xover&group=%s&related=%d\">Related Items</a>\n",path_script,towww(group),related);
	printf("<td><a href=\"%s?cmd=xover&group=%s\">Recent Items</a>",path_script,towww(group));
	printf("<td><a href=\"%s?cmd=list\">All Groups</a>",path_script);
	printf("<td><a href=\"%s?cmd=follow&group=%s&item=%s\">Post Followup</a>",path_script,towww(group),query_find("item"));
	printf("</table> \n");
	show_end("article");
}
static char msgid[BFSZ];
static	char subject[BFSZ];
static	char from[BFSZ];
static	char h_ref[BFSZ];
void show_article1(void)
{ 
	char *s;
	char date[BFSZ];
	char bf[4000];
	char newsgroups[BFSZ];
	strcpy(subject,"");
	strcpy(from,"");
	strcpy(date,"");
	strcpy(msgid,"");
	strcpy(newsgroups,"");

	s = get_line(instr);
	for (;s!=NULL;) {
		if (strcmp(s,"\015")==0) break;
		head_get("subject:",subject,s);
		head_get("date:",date,s);
		head_get("from:",from,s);
		head_get("references:",h_ref,s);
		head_get("message-id:",msgid,s);
		head_get("newsgroups:",newsgroups,s);
		s = get_line(instr);
	}
	var_set("h_subject",subject);
	var_set("h_date",date);
	var_set("h_from",from);
	sprintf(bf,"%s %s",h_ref,msgid);
	var_set("h_references",bf);
	var_set("h_message-id",msgid);
	var_set("h_newsgroups",newsgroups);
}
void show_article3(void)
{ 
	char *s;
	s = strchr(from,'\015'); 
	if (s!=NULL) *s = 0;
	printf("%s wrote:",from);
	s = get_line(instr);
	for (;s!=NULL;) {
		printf(">%s",s);
		s = get_line(instr);
	}
}
void f_post(void)
{
	f_follow(FALSE);
}
void f_postXX(void)
{
	ncpy(group,query_find("group"),BFSZ-1);
	printf("<html><head><title> Post new item to group %s  </title>",group); 
	printf("</head><body>"); 
	printf("<form method=post action=\"%s\"> \n",path_script);
	printf(" <input type=hidden name=\"cmd\" value=\"post\"> \n");
	printf(" <input type=hidden name=\"group\" value=\"%s\"> \n",group);
	printf("Newsgroup: %s<br>\n",group);
	printf("From: <input name=\"from\" size=20 > (Give EMail address)<br>\n");
	printf("Subject <input name=\"subject\" size=50 > (Required)<br>\n");
	printf("Please enter your message text here: (press enter at the end of each line)<br>\n");
	printf("<textarea name=\"message\" cols=65 rows=8>  </textarea> <br>\n");
 	printf("<input type=submit name=\"action\" value=\"Post Message\" >\n");
	printf("</form>\n");
	netwin_advert();
	printf("</body>"); 
	printf("</html>"); 
}
void f_search(void)
{
	tpl_init();
	tpl_show("search.tpl");
/*
	if (!show_top("fsearch","Search Entire News Database Using: DNEWS Full Text Search Engine"))
		printf("<h1> Search All News Items</h1>"); 
	printf("<form method=get action=\"%s\"> \n",path_script);
	printf(" <input type=hidden name=\"cmd\" value=\"r_search\"> \n");
	printf(" <input type=hidden name=\"from\" value=\"1\"> \n");
	printf(" <input type=hidden name=\"to\" value=\"20\"> \n");
	printf("Key Words: <input name=\"key\" size=40 > <br>\n");
 	printf("<input type=submit name=\"action\" value=\"Search\" >\n");
	printf("</form>\n");
	printf("<p>\n");
	printf("A higher weighting\n");
	printf("is given to words early in your list of key words.  \n");
	printf("Some common words like 'and' may return '0' matches, this\n");
	printf("is normal, these are not kept in the index as they have\n");
	printf("no search value.<br>\n");
	show_end("fsearch");
*/
}
void f_find(void)
{
	tpl_init()		;
	tpl_show("xsearch.tpl");
}
void f_find_xold(void)
{
	if (!show_top("ffind","Search News Group Headers"))
		printf("<h1> Search headers in %s </h1>",query_find("group")); 
	printf("<form method=get action=\"%s\"> \n",path_script);
	printf(" <input type=hidden name=\"cmd\" value=\"r_find\"> \n");
	printf(" <input type=hidden name=\"group\" value=\"%s\"> \n",query_find("group"));
	printf("Search String: <input name=\"key\" size=40 > <br>\n");
 	printf("<input type=submit name=\"action\" value=\"Search\" >\n");
	printf("</form>\n");
	printf("<p>\n");
	printf("The search is performed on the headers in the news group.\n");
	printf("It is not case sensitive.  If you give several words they\n");
	printf("will only match if the same 'phrase' appears in the news headers<br>\n");
	show_end("ffind");
}
void r_search(void)  /* from=nnn   max=nnn*/
{
	char bf[BFSZ];
	int from = atoi(query_find("from"));
	int to = atoi(query_find("to"));
	char key[BFSZ],xkey[BFSZ],zkey[BFSZ];
	char *s;
	int total;
	int i;
	char *kw[100];
	int nkw;
	int matches[100];

	strcpy(key,query_find("key"));
	strcpy(zkey,key);
	s = strtok(key,",+ \t\n");
	strcpy(xkey,"");
	for (nkw=0;s!=NULL;) {
		strcat(xkey,s);
		kw[nkw++] = strdup(s);
		s = strtok(NULL,",+ \t\n");
		if (s!=NULL) strcat(xkey,",");
	}

	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
        sprintf(bf,"xdsearch %d-%d %s\015\012",from,to,xkey); nntp_write_s(chan,bf,strlen(bf)); 
dmsg("Wait for dindex  status\n");
	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}

	s = strchr(nntp_status,':');
	if (s!=NULL) total = atoi(s+1);
	s = strchr(s+1,':');
	if (s!=NULL) s = strtok(s,": \015\012");
	for (i=0;(s!=NULL) && (i<nkw);i++) {
		matches[i] = atoi(s);
		s = strtok(NULL," \015\012");
	}
	


   	read_xover();


	tpl_init();
	sprintf(bf,"%d",total);
	var_set("total_matches",bf);
	strcpy(bf,"");
	for (i=0; i<nkw; i++) sprintf(bf+strlen(bf),"%s %d, ",kw[i],matches[i]);
	var_set("summary",bf);
	search_result = TRUE;
	tpl_init_sbar(from,to,total,zkey);
	tpl_show("results.tpl");


/*	show_sbar(from,to,total,zkey);
	show_search();
	show_sbar(from,to,total,zkey);

	show_end("search");
*/
        sprintf(bf,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
	read_status(); /* Wait for the channel to close */
} 
void f_follow(int follow)
{
	char bf[BFSZ];
	int item;
	if (follow) {
		item = atoi(query_find("item"));
		ncpy(group,query_find("group"),BFSZ-1);
		if (!group_allow(group)) return;

		if (!read_status()) return;
	        sprintf(bf,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
		if (!read_status()) return;
	        sprintf(bf,"article %d\015\012",item); nntp_write_s(chan,bf,strlen(bf)); 
		if (!read_status()) return;
	   	read_xover();
	}

	ncpy(group,query_find("group"),BFSZ-1);
	if (!group_allow(group)) return;

	if (follow) {
		show_article1();
	}

	if (!follow) strcpy(subject,"");
	if (follow) {
	 	strcpy(bf,subject);
		strlwr(bf);
		if (strstr(bf,"re:")==NULL) {
			strcpy(bf,subject);
			sprintf(subject,"Re: %s",bf); 	
		}
		var_set("h_subject",subject);
	}
	if (follow) tpl_show("follow.tpl");
	else tpl_show("post.tpl");
        if (follow) {
        	sprintf(bf,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
		read_status(); /* Wait for the channel to close */
	}
}
void show_xline(char *s)
{
	/* item subject from date messageid ref byte lines */
 	char item[BFSZ];
	char subject[BFSZ];
	char from[BFSZ];
	char date[BFSZ];
	char msgid[BFSZ];
	char ref[BFSZ];
	char bytes[BFSZ];
	char lines[BFSZ];
	char score[BFSZ];
	char group[BFSZ];
	int i;

	strcpy(score,"NONE");
	strcpy(group,"NONE");
	strcpy(item,"");
	strcpy(subject,"");
	strcpy(from,"");
	strcpy(date,"");
	strcpy(msgid,"");
	strcpy(ref,"");
	strcpy(bytes,"");
	strcpy(lines,"");
	s = xstrtok(s,"\t\015\012"); if (s==NULL) goto done;
 	ncpy(item,s,BFSZ-1);
	s = xstrtok(NULL,"\t\015\012"); if (s==NULL) goto done;
 	ncpy(subject,s,BFSZ-1);
	s = xstrtok(NULL,"\t\015\012"); if (s==NULL) goto done;
 	ncpy(from,s,BFSZ-1);
	s = xstrtok(NULL,"\t\015\012"); if (s==NULL) goto done;
 	ncpy(date,s,BFSZ-1);
	s = xstrtok(NULL,"\t\015\012"); if (s==NULL) goto done;
 	ncpy(msgid,s,BFSZ-1);
	s = xstrtok(NULL,"\t\015\012"); if (s==NULL) goto done;
 	ncpy(ref,s,BFSZ-1);
	s = xstrtok(NULL,"\t\015\012"); if (s==NULL) goto done;
 	ncpy(bytes,s,BFSZ-1);
	s = xstrtok(NULL,"\t\015\012"); if (s==NULL) goto done;
 	ncpy(lines,s,BFSZ-1);

	for (i=0; i<10 && s!=NULL; i++) {
		s = xstrtok(NULL,"\t\015\012"); if (s==NULL) goto done;
		if (strncmp(s,"fts_score:",10)==0) ncpy(score,s+11,10);
		if (strncmp(s,"fts_group:",10)==0) ncpy(group,s+11,80);
	}

done:;

	xover_add(item,subject,from,lines,date,ref,msgid);
	x_score[nitem-1] = strdup(score);
	x_group[nitem-1] = strdup(group);
}
char *xstrtok(char *line, char *in)
{
	static int end=FALSE;
	static char *last;
	char *s;
	if (line==NULL) {
		line = last;
		if (end) return NULL;
	} else end=FALSE;

	s = strchr(line,'\t');
	if (s==NULL) {
		end = TRUE;
		s = strchr(line,'\015');
		if (s!=NULL) *s = 0;
		return line;
	}
	*s = 0;
	last = s+1;
	return line;
}
char *lib_nore(char *s)
{
	static char bf[BFSZ];
 	ncpy(bf,s,BFSZ-1);
	strlwr(bf);
	s = bf;
	if (strncmp(s,"re:",3)==0) s += 3;
	if (*s==' ') s++;
	return s;
}
void xover_add(char *item, char *subject, char *from, char *lines, char *date, char *ref, char *mid)
{
	if (strcmp(item,".")==0) return;
	if (nitem>1800) return;
	x_item[nitem] = atoi(item);
	x_subject[nitem] = strdup(subject);
	x_subjectsrt[nitem] = strdup(lib_nore(subject));
	x_from[nitem] = strdup(from);
	x_date[nitem] = strdup(date);
	x_lines[nitem] = strdup(lines);
	x_ref[nitem] = strdup(ref);
	x_mid[nitem] = strdup(mid);
	nitem++;
}
int xover_cmp(const void *a,const void *b)
{
 	int *aa = (int *) a;
	int *bb = (int *) b;
	if (x_f[*aa] < x_f[*bb]) return -1;
	if (x_f[*aa] > x_f[*bb]) return 1;
	return 0;
}
int xover_cmp_old(const void *a,const void *b)
{
 	int *aa = (int *) a;
	int *bb = (int *) b;
	return strcmp(x_subjectsrt[*aa],x_subjectsrt[*bb]);
}
void xover_showord(void)
{
	int p[2000]; 
	int i,ii;
	for (i=0; i<nitem; i++) p[i] = i;

	if (dosort) qsort(p,nitem,sizeof(int),xover_cmp);
	printf("<ul>\n");
	for (i=0; i<nitem; i++) {
		ii = p[i];
		printf("<li><a href=\"%s?cmd=article&group=%s&item=%d\">",path_script,towww(group),x_item[ii]);
		printf("%s</a> - %s (%s) <br>\n",value_encode(x_subject[ii]), email_name(x_from[ii]),x_lines[ii]);
	}
	printf("</ul>\n");

}
void r_find(void)  /* from=nnn   max=nnn*/
{
	char bf[BFSZ];
	char find[BFSZ];

	ncpy(find,query_find("key"),BFSZ-1);
	ncpy(group,query_find("group"),BFSZ-1);
	if (!group_allow(group)) return;

	if (!read_status()) {zmsg("News host not responding1: %s",nntp_status); return;}
        sprintf(bf,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding2: %s",nntp_status); return;}
        sprintf(bf,"xdfind 1- %s\015\012",lib_encode(find)); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding3: %s",nntp_status); return;}
	read_xover();
	show_xover();

	tpl_init();
	tpl_set_buttons();

	var_set("keywords",find);
	tpl_show("xresults.tpl");
/*	
	if (!show_top("find"," Search results %s ",group))
		printf("<h1> Search Results: %s</h1>",group); 
	show_xbar();

	xover_showord();
	show_xbar();

	show_end("find");

*/
        sprintf(bf,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
	read_status(); /* Wait for the channel to close */
}
void r_xover(void)  /* from=nnn   max=nnn*/
{
	char bf[BFSZ];
	char rsubject[BFSZ];
	char rmsgid[BFSZ];
	int from = atoi(query_find("from"));
	int max = atoi(query_find("max"));
	int to,xfrom,xto,lastfrom;
	char *s;
	int related;
dmsg("called r_xover");
	related = atoi(query_find("related"));
	
	ncpy(group,query_find("group"),BFSZ-1);
	if (!group_allow(group)) return;

	if (max==0) max = PAGESIZE;
	if (max>1000) max = 1000;
	gmax = max;

dmsg("Wait for hello\n");
	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
        sprintf(bf,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
	if (related>0) {
	        sprintf(bf,"xover %d\015\012",related); nntp_write_s(chan,bf,strlen(bf)); 
		if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
		read_xover();

		s = get_line(instr);
		s = xstrtok(s,"\t");
		if (s!=NULL) s = xstrtok(NULL,"\t");
		if (s!=NULL) {
			strlwr(s);
			if (strncmp(s,"re:",3)==0)  s += 3; 
			if (*s == ' ') s++;
			ncpy(rsubject,s,BFSZ-1);
		}
		if (s!=NULL) s = xstrtok(NULL,"\t");
		if (s!=NULL) s = xstrtok(NULL,"\t");
		if (s!=NULL) s = xstrtok(NULL,"\t");
		if (s!=NULL) ncpy(rmsgid,s,BFSZ-1);

		str_clear(instr);
	        sprintf(bf,"xdfind 1- %s %s\015\012",lib_encode(rsubject),lib_encode(rmsgid)); nntp_write_s(chan,bf,strlen(bf)); 
		if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
	} else {
		group_from = xfrom = atoi(get_word(nntp_status,2));
		group_to = xto = atoi(get_word(nntp_status,3));
		if (from==0) from = xto-max;
		if (from<xfrom) from = xfrom;
		to = from+max;
		gfrom = from; gto = to;
again:
	        sprintf(bf,"xover %d-%d\015\012",gfrom,gto); nntp_write_s(chan,bf,strlen(bf)); 
		if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
	}
   	read_xover();
	show_xover();
	lastfrom = gfrom;
	if (related==0) if (nitem<(max-max/2)) {
	 	if (gfrom>group_from) gfrom -=max;
		if (gfrom<group_from) gfrom = group_from;
		gto = gfrom-1;
		if (gfrom!=lastfrom) goto again;
	}

	if (related>0) sprintf(bf,"Related items in %s",group);
	else sprintf(bf,"Items in %s",group);

	tpl_set_buttons();
	tpl_show("items.tpl");
        sprintf(bf,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
	read_status(); /* Wait for the channel to close */
} 
void r_listall(void)  /* from=nnn   max=nnn*/
{
	char bf[BFSZ];
	ncpy(group,query_find("group"),BFSZ-1);

	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
        sprintf(bf,"xdlist %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {
		printf("Sorry, this server does not support the nntp DLIST command<br>");
		printf("Try DNEWS 2.5 or later<br>");
		return;
	}
   	read_xover();
	tpl_init();
	tpl_show("list.tpl");
        sprintf(bf,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
	read_status(); /* Wait for the channel to close */
} 
void r_article(void)  /* from=nnn   max=nnn*/
{
	char bf[BFSZ];
	int item = atoi(query_find("item"));
	ncpy(group,query_find("group"),BFSZ-1);
	if (!group_allow(group)) return;

	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
        sprintf(bf,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
        sprintf(bf,"article %d\015\012",item); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
   	read_xover();
	show_article();
        sprintf(bf,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
	read_status(); /* Wait for the channel to close */
}
char *post_template(char *xfname);

void r_post(void)  /* from=nnn   max=nnn*/
{
	char uinfo[BFSZ];
	char ruser[BFSZ];
	char bf[BFSZ];
	char *s;
	char xfile[BFSZ];

	strcpy(ruser,mygetenv("REMOTE_ADDR"));

	if (strlen(ini_get(I_POST_USERS))>0) if (!group_match(ini_get(I_POST_USERS),ruser)) {
		printf("Sorry, you (%s) have no permission to post.\n<br>",ruser);
		return;
	}
	
	tpl_init();
	if (strlen(form_find("from"))==0) {
		var_set("missing","from");
		tpl_show("required.tpl");
		return;
	}
	if (strlen(form_find("subject"))==0) {
		var_set("missing","subject");
		tpl_show("required.tpl");
		return;
	}

	strcpy(bf,form_find("required"));
	s = strtok(bf,",");
	for (;s!=NULL;) {
		if (strlen(form_find(s))==0) {
			var_set("missing",s);
			tpl_show("required.tpl");
			return;
		}
		var_set(s,form_find(s));
		s = strtok(NULL,",");
	}
	strcpy(bf,form_find("optional"));
	s = strtok(bf,",");
	for (;s!=NULL;) {
		if (strlen(form_find(s))!=0) {
			var_set(s,form_find(s));
		}
		s = strtok(NULL,",");
	}


	ncpy(group,form_find("group"),BFSZ-1);

	if (strlen(ini_get(I_POST_GROUPS))>0) if (!group_match(ini_get(I_POST_GROUPS),group)) {
		printf("Sorry, posting is not permitted to (%s) \n<br>",group);
		return;
	}

	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
        sprintf(bf,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding: %s",nntp_status); return;}
        sprintf(bf,"post\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("Post status bad %s\n",nntp_status); return;}

	dmsg("From %s\n",form_find("from"));   
   	sprintf(bf,"From: %s\015\012",form_find("from"));      nntp_write_s(chan,bf,strlen(bf)); 
   	sprintf(bf,"Subject: %s\015\012",form_find("subject"));      nntp_write_s(chan,bf,strlen(bf)); 
   	sprintf(bf,"Newsgroups: %s\015\012",form_find("group"));     nntp_write_s(chan,bf,strlen(bf)); 

   	strcpy(uinfo,mygetenv("REMOTE_ADDR")); strcat(uinfo," ");
 	strcat(uinfo,mygetenv("REMOTE_HOST")); strcat(uinfo," ");
 	strcat(uinfo,mygetenv("REMOTE_IDENT")); strcat(uinfo," ");
 	strcat(uinfo,mygetenv("REMOTE_USER")); strcat(uinfo," ");
     	sprintf(bf,"X-User-Info: %s\015\012",uinfo);     nntp_write_s(chan,bf,strlen(bf)); 
   	if (strlen(form_find("ref"))>0) {
   		sprintf(bf,"References: %s\015\012",form_find("ref"));     nntp_write_s(chan,bf,strlen(bf)); 
	}
   	sprintf(bf,"\015\012");       nntp_write_s(chan,bf,strlen(bf)); 

	dmsg("Message \n");   
	s = form_find("message");
	dmsg("message %s\n",form_find("message"));   

	strcpy(xfile,form_find("template"));
	if (strlen(xfile)>0) {
		var_set("message",s);
		s = post_template(xfile);
	}

	if (s!=NULL) nntp_write_s(chan,s,strlen(s));
   	sprintf(bf,"\015\012.\015\012");       nntp_write_s(chan,bf,strlen(bf)); 
	read_status(); /* Wait for the channel to close */

	var_set("post_status",nntp_status);
	if (atoi(nntp_status)==240) {
		var_set("ok","TRUE");
	}
	tpl_show("posted.tpl");
        sprintf(bf,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
	read_status(); /* Wait for the channel to close */
} 
void show_dline(char *s)
{
	char bf[BFSZ];
	char gname[BFSZ];
	char mod[BFSZ];
	char from[BFSZ];
	char to[BFSZ];
	char items[BFSZ];

	ncpy(bf,s,BFSZ-1);
	s = strtok(bf," "); if (s==NULL) return;
	ncpy(gname,s,BFSZ-1);
	s = strtok(NULL," "); if (s==NULL) return;
	ncpy(to,s,BFSZ-1);
	s = strtok(NULL," "); if (s==NULL) goto doit;
	ncpy(from,s,BFSZ-1);
	s = strtok(NULL," "); if (s==NULL) goto doit;
	strcpy(mod,"");
	if (*s=='m') strcpy(mod,"(Moderated)");
	s = strtok(NULL," "); if (s==NULL) goto doit;
	ncpy(items,s,BFSZ-1);
doit:;
	if (gname[0]=='+') {
		strcat(to,".");
		if (group_match(ini_get(I_ALLOW),to)) {
			printf("<li><a href=\"%s?cmd=listall&group=%s\">%s.. %s groups</a> <br>",path_script,towww(to),to,from);
		}
	} else {
		if (group_match(ini_get(I_ALLOW),gname)) {
			printf("<li><a href=\"%s?cmd=xover&group=%s\">%s, %s items %s</a> <br>",path_script,towww(gname),gname,items,mod);
		}
	}
}

void r_list(void)
{
	tpl_init();
	tpl_show("top.tpl");
	dmsg("top done\n");
}
/* ---------------------------------------- xxx.ini file processing -- */
void db_setscript(char *script)
{
	char *last;
	char *s;
	char bf[BFSZ];
	strcpy(path_script,script);
	ncpy(bf,script,BFSZ-1);
	last = bf-1;
	for (s=bf;*s!=0;s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	ncpy(rootname,last+1,BFSZ-1);
	s = strchr(rootname,'.');
	if (s!=NULL) *s = 0;
	if (strlen(rootname)==0) strcpy(rootname,"dnewsweb");
}


char *ini_values[200];
int ini_find(char *s)
{
	int i;
	for (i=0; ini_names[i]!=NULL; i++) {
	 	if (strcmp(ini_names[i],s)==0) return i;
	}
	return -1;
}
char *ini_get(int x)
{
 	if (ini_values[x]!=NULL) return ini_values[x];
	return "";
}
int ini_read(char *epath)
{
	char bf[BFSZ];
	char bf2[BFSZ];
	char bf3[BFSZ];
	char xroot[BFSZ];
	char *last;
	FILE *f;
	int i;
	char *s;

	db_setscript(mygetenv("SCRIPT_NAME"));
	sprintf(bf,"%s.ini",rootname);
	f = fopen(bf,"r");
	strcpy(bf3,bf);
	if (f==NULL) {
		imsg("ini file not in working dir, searching in path {%s} \n",epath);
		strcpy(xroot,epath);
		last = xroot;
		for (s=xroot; *s!=0; s++) {
			if (*s=='/') last = s;
			if (*s=='\\') last = s;
		}
		*last = 0;
		sprintf(bf,"%s/%s.ini",xroot,rootname);
		imsg("Looking in (%s) for ini file\n",bf);
		f = fopen(bf,"r");
		strcpy(bf2,bf);
	}
	if (f==NULL) {
		sprintf(bf,"/etc/%s.ini",rootname);
		imsg("Last resort, looking in (%s) for ini file\n",bf);
		f = fopen(bf,"r");
	}
	if (f==NULL) {
		printf("Pragma: cache\n");
		printf("Content-type: text/html\n\n");
	 	emsg("Unable to find ini file in <br> {%s} \n",bf3);
		emsg("Also looked in {%s} <br> and {%s} <br>\n",bf2,bf);
#ifdef WIN32
		GetCurrentDirectory(200,bf);
	 	emsg("Place dnewsweb.ini file in this directory {%s} \n",bf);
#endif
		return FALSE;
	}	
	for (;!feof(f);) {
          if (fgets(bf,BFSZ-10,f)!=NULL) {
		s = strtok(bf," \t\n"); if (s==NULL) continue;
		if (*s==0 || *s=='#') continue;
		i = ini_find(s);
		if (i<0) { emsg("Unknown ini file setting {%s} \n",s); continue; }
		s = strtok(NULL,"\n"); if (s==NULL) continue;
		ini_values[i] = strdup(s);
	  }
	} 
	fclose(f);	
	return TRUE;
}
 
/* ---------------------- Missing bits that are needed ---------------------- */
#define OUTSIZE 5000
static char output[OUTSIZE];
void emsg_do(int x, char *s);
void emsg(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        if (len>=OUTSIZE) emsg_do(D_ERROR,"FATAL, EMSG ERROR TOO LONG\n");
        emsg_do(D_ERROR,output);
}
void filemsg(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        if (len>=OUTSIZE) emsg_do(D_ERROR,"FATAL, EMSG ERROR TOO LONG\n");
        emsg_do(D_ERROR,output);
}
void imsg(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        if (len>=OUTSIZE) emsg_do(D_ERROR,"FATAL, EMSG ERROR TOO LONG\n");
        emsg_do(D_INFO,output);
}
void h_printf(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        if (len>=OUTSIZE) emsg_do(D_ERROR,"FATAL, EMSG ERROR TOO LONG\n");
	printf("%s",output);
        emsg_do(D_DEBUG,output);
}
void zmsg(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        if (len>=OUTSIZE) emsg_do(D_ERROR,"FATAL, EMSG ERROR TOO LONG\n");
	printf("Pragma: cache\n");
	printf("Content-type: text/html\n\n");
	printf("%s",output);
        emsg_do(D_INFO,output);
}
void wmsg(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        if (len>=OUTSIZE) emsg_do(D_ERROR,"FATAL, EMSG ERROR TOO LONG\n");
        emsg_do(D_INFO,output);
}
void dmsg(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	if (!dodebug) return;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        if (len>=OUTSIZE) emsg_do(D_ERROR,"FATAL, EMSG ERROR TOO LONG\n");
        emsg_do(D_DEBUG,output);
}
void emsg_reset(void)
{
	remove(log_file);
}
void emsg_do(int x, char *s)
{
	time_t ltime;
	char bf[BFSZ];
	FILE *f;
        time( &ltime );
	strcpy(bf,ctime(&ltime));
	bf[19] = 0;

 	if (x==D_ERROR) printf("<br> Error: %s <br>",s);
	f = fopen(log_file,"a");
	if (f==NULL) {perror(log_file); return; }
	fprintf(f,"%s, %s",bf+4,s);
	fclose(f);		
}
/* ---------------------- Missing bits that are needed ---------------------- */
char *group_name(int i)
{
        return "(none)";
}
#ifndef unix
int lib_mail_file(char *to, char *subj, char *fname)
{
        return TRUE;
}
int lib_freespace(char *s)
{
        return 100000;
}
#endif
int diskuse_get(void)
{
        return 0;
}
char *nt_pcname(void);

char *lib_findfile(char *x)
{
        return NULL;
}
void dnews_manager_report(char *s)
{
        
}
int post_local(String *xitem, char *addr, int ismod)
{
        return TRUE;
}
void dnews_exit(char *reason, char *file, int line)
{
        printf("Crashing\n");
        exit(1);
}



#ifdef WIN32
char *nt_pcname(void)
{
	return "pcname";
}
#endif
/* ------------------ library of stuff for web --------------------*/

static char *query_name[1000];
static char *query_val[1000];
static int nquery;
/* Query string */
char *lib_encode(char *s)
{
   char *ss=s;
  for (;*s!=0;s++) {
    if (isspace(*s)) *s = '+';
  }
  return ss;
}
char *value_encode(char *s)
{
 	static char bf[2000];
	int i;
	char *out=bf;
	for (i=0; i<2000 && *s!=0;i++,s++) {
	 	if (*s=='<') { strcpy(out,"&lt;"); out += strlen(out); }
		else if (*s=='>') { strcpy(out,"&gt;"); out += strlen(out); }
		else if (*s=='&') { strcpy(out,"&amp;"); out += strlen(out); }
	 	else *out++ = *s;
	}
	*out++ = 0;
	return bf;
}
/*
Same as value encode except.
user@a.b.c  --> <a href="mailto:user@a.b.c"> user@a.b.c </a>
http://netwin.co.nz  --> <a href="http://netwin.co.nz"> htp://netwin.co.nz</a>


*/
char *ref_encode_http(char *instr);
char *ref_encode_mail(char *instr);
char *ref_encode(char *s);
char *ref_encode(char *s)
{
	static char bf[5000];
	strcpy(bf,s);
	s = ref_encode_mail(bf);
	s = ref_encode_http(s);
	return s;
}
char *ref_encode_mail(char *instr)
{
	char bf[BFSZ];
	char bf2[BFSZ];
	char *s,*s1,*s2;
	s = instr;
again:
	s = strchr(s,'@');
	if (s==NULL) return instr;
	if (s>instr) if (s[-1]<0) {
		s++;
		goto again;
	}
	for (s1=s;s1>instr;s1--) {
		if (isspace(*s1)) {
			s1++;
			break;
		}
	}
	for (s2=s;*s2!=0;s2++) {
		if (isspace(*s2)) break;
	}
	s2--;
	ncpy(bf,s1,s2-s1+1);
	sprintf(bf2,"<a href=\"mailto:%s\">%s</a>",bf,bf);
	memmove(s1+strlen(bf2),s2+1,strlen(s2+1)+1);
	strncpy(s1,bf2,strlen(bf2));
	s = s1 + strlen(bf2);
	goto again;
} 
char *ref_encode_http(char *instr)
{
	char *s,*s1,*s2;
	char bf[BFSZ];
	char bf2[BFSZ];
	s = instr;
again:
	s = strstr(s,"http:");
	if (s==NULL) return instr;
	if (s>instr) if (s[-1]<0) {
		s++;
		goto again;
	}
	s1 = s;
	for (s2=s;*s2!=0;s2++) {
		if (isspace(*s2)) break;
	}
	s2--;
	ncpy(bf,s1,s2-s1+1);
	sprintf(bf2,"<a href=\"%s\">%s</a>",bf,bf);
	memmove(s1+strlen(bf2),s2+1,strlen(s2+1)+1);
	strncpy(s1,bf2,strlen(bf2));
	s = s1 + strlen(bf2);
	goto again;
} 
char *decode_base64s(char *s);
char *decode_base64s(char *s)
{
	static char bf[2000];
	int n = decode_base64(s,bf);
	bf[n] = 0;
	return bf;
}
char *decode_quote(char *s)
{
 	static char bf[2000];
	int i;
	char *out=bf;
	for (i=0; i<2000 && *s!=0;i++,s++) {
		if (*s<0) {
			*out++ = *s++;
			*out++ = *s;
		} else if (strncmp(s,"=3D",3)==0) *out++ = '=';
		else if (*s=='=') *out++ = '\n';
		else *out++ = *s;
	}
	*out++ = 0;
	return bf;
}
char *text_encode(char *s)
{
 	static char bf[2000];
	int i;
	char *out=bf;
	for (i=0; i<2000 && *s!=0;i++,s++) {
		if (*s<0) {
			*out++ = *s++;
			*out++ = *s;
		} else if (*s=='<') { strcpy(out,"&lt;"); out += strlen(out); }
		else if (*s=='>') { strcpy(out,"&gt;"); out += strlen(out); }
		else if (*s=='&') { strcpy(out,"&amp;"); out += strlen(out); }
	 	else *out++ = *s;
	}
	*out++ = 0;
	return ref_encode(bf);
}

char *mygetenv(char *v)
{
	char *s = getenv(v);
	if (s==NULL) return "";
	return s;
}
char *query_find(char *s)
{
 	int i;
	for (i=0; i<nquery; i++) if (strcmp(query_name[i],s)==0) return query_val[i];
	return "";
}
int query_get(void)
{
	char *q;
	char *cl;
	char bf[10000],val[2000],name[BFSZ];
	int x;

	q =  mygetenv("QUERY_STRING");
	if (q==NULL) return FALSE;
	strcpy(bf,q); cl = bf;
    	for(x=0;cl[0] != '\0';x++) {
    		getword(val,cl,'&');
    		plustospace(val);
        	unescape_url(val);
        	getword(name,val,'=');
		query_name[x] = strdup(name);
		query_val[x] = strdup(val);
		/*printf("[%d] {%s} = {%s} \n",x,query_name[x],query_val[x]);*/
    	}
	nquery = x;
        return TRUE;
}
void getword(char *word, char *line, char stop) 
{
    int x = 0,y;

    for(x=0;((line[x]) && (line[x] != stop));x++)
        word[x] = line[x];

    word[x] = '\0';
    if(line[x]) ++x;
    y=0;

    while(line[y++] = line[x++]);
}
char x2c(char *what) 
{
    register char digit;

    digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
    digit *= 16;
    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
    return(digit);
}

void unescape_url(char *url) 
{
    register int x,y;

    for(x=0,y=0;url[y];++x,++y) {
        if((url[x] = url[y]) == '%') {
            url[x] = x2c(&url[y+1]);
            y+=2;
        }
    }
    url[x] = '\0';
}

void plustospace(char *str) 
{
    register int x;

    for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' ';
}


static char *form_name[1000];
static char *form_val[1000];
static int nform;
char *makeword(char *line, char stop) ;
char *fmakeword(FILE *f, char stop, int *cl);
void convert_crlf(char *s); /* turn crlf into lf's */
void form_get(void)
{
	int x,cl;
	char *s = mygetenv("CONTENT_LENGTH");
	if (s==NULL) return;
	cl = atoi(s);
	for(x=0;cl && (!feof(stdin));x++) {
	        form_val[x]= fmakeword(stdin,'&',&cl);
	        plustospace(form_val[x]);
	        unescape_url(form_val[x]);
	        form_name[x] = makeword(form_val[x],'=');
		convert_crlf(form_val[x]);
	}
	nform = x;
}
void convert_crlf(char *s)
{
	char *out=s;
	for (;*s!=0;s++) {
		if (*s == '\015') s++;
		*out++ = *s;
	}
	*out++ = 0;
}
char *makeword(char *line, char stop) 
{
    int x = 0,y;
    char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1));

    for(x=0;((line[x]) && (line[x] != stop));x++)
        word[x] = line[x];

    word[x] = '\0';
    if(line[x]) ++x;
    y=0;

    while(line[y++] = line[x++]);
    return word;
}

char *fmakeword(FILE *f, char stop, int *cl) 
{
    int wsize;
    char *word;
    int ll;

    wsize = 8024;
    ll=0;
    word = (char *) malloc(sizeof(char) * (wsize + 1));

    while(1) {
        word[ll] = (char)fgetc(f);
        if(ll==wsize) {
            word[ll+1] = '\0';
            wsize+=8024;
            word = (char *)realloc(word,sizeof(char)*(wsize+1));
        }
        --(*cl);
        if((word[ll] == stop) || (feof(f)) || (!(*cl))) {
            if(word[ll] != stop) ll++;
            word[ll] = '\0';
            return word;
        }
        ++ll;
    }
}


char *form_find(char *s)
{
 	int i;
	for (i=0; i<nform; i++) if (strcmp(form_name[i],s)==0) return form_val[i];
	return "";
}
char *form_find_i(char *s, int i)
{
	char bf[BFSZ];
 	sprintf(bf,"%s%d",s,i); s = bf;
	for (i=0; i<nform; i++) if (strcmp(form_name[i],s)==0) return form_val[i];
	return "";
}

char *email_name(char *email)
{
	static char bf[BFSZ];
	char *s2;
	char *s;
	s = strchr(email,'(');
	if (s!=NULL) {
		ncpy(bf,s+1,BFSZ-1);
		s = strchr(bf,')');
		if (s!=NULL) *s = 0;
	}
	ncpy(bf,email,BFSZ-1);
	s = strchr(bf,'@');
	s2 = strchr(bf,' ');
	if (s==NULL) s = s2;
	if (s2!=NULL) if (s2<s) s = s2;
	if (s!=NULL) *s = 0;
	return bf;
}


int group_match(char *wild, char *grp22)
{
        char bf[10000];
        char grp[BFSZ];
        int m=TRUE,lastm;
        char *s;

        ncpy(grp,grp22,BFSZ-1); strlwr(grp);
        lastm = FALSE;
        ncpy(bf,wild,10000-1);
        s = strtok(bf,",");
        for (;s!=NULL;) {
                m = TRUE;
                if (*s=='!') {
                        s++;
                        m = FALSE;
                }
                if (match_wild(s,grp)) { lastm = m; } 
                s = strtok(NULL,",");
        }
        return lastm;
}


int  show_top(char *type, char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        char output[BFSZ];
        va_start(arg_ptr, arg_list);
        format = arg_list;
        vsprintf(output, format, arg_ptr);

	if (show_headfile(type,output)) return TRUE;
	printf("<html><head><title>%s</title>",output);
	printf("</head><body>"); 
	return FALSE;
}
void show_end(char *type)
{
	show_headfile(type,NULL);
	if (strlen(ini_get(I_NOADVERT))==0) netwin_advert();
	printf("</body>"); 
	printf("</html>"); 
}
int show_headfile(char *type, char *title)
{
	char fname[BFSZ];
	char bf[BFSZ],bf2[BFSZ];
	char *s;
	FILE *f;
	char *ext = ".top";
	if (title==NULL) ext = ".end";
	if (strlen(ini_get(I_HEADERS))==0) return FALSE;
	sprintf(fname,"%s/%s%s",ini_get(I_HEADERS),type,ext);
	dmsg("Top file (%s)",fname);
	f = fopen(fname,"r");
	if (f==NULL) {
		if (title!=NULL) printf("Top or End file missing {%s} <br>\n",fname);	
		return FALSE;
	}
	for (;!feof(f);) {
	 	if (fgets(bf,BFSZ-10,f)==NULL) break;
		if (title!=NULL) {
		  s = strstr(bf,"$title$");
		  if (s!=NULL) {
		 	strcpy(bf2,s+7);
			strcpy(s,title);
			strcat(s,bf2);
		  }
		}
		printf("%s",bf);
	}	
	fclose(f);
	return TRUE;
}

int tpl_show_file(char *xfname)
{
	FILE *f;
	char fname[BFSZ];
	char bf[BFSZ];

	if (strlen(ini_get(I_TEMPLATES))>0) 
	sprintf(fname,"%s/%s",ini_get(I_TEMPLATES),xfname);
	else  sprintf(fname,"%s/%s",ini_get(I_HEADERS),xfname);

	f = fopen(fname,"r");
	if (f==NULL) {
		printf("Include file missing {%s} <br>\n",fname);	
		return FALSE;
	}

	for (;!feof(f);) {
	 	if (fgets(bf,BFSZ-10,f)==NULL) break;
		printf("%s",bf);
	}	
	fclose(f);
	return TRUE;
}


char *towww(char *s)
{
  static char bf[BFSZ],*out;
  for (out=bf;*s!=0;s++) {
    	if (isspace(*s)) *s = '+';
  	if (*s=='+') {sprintf(out,"%%%x",*s); out += strlen(out);}
 	else if (*s=='&') {sprintf(out,"%%%x",*s); out += strlen(out);}
	else *out++ = *s;
  }
  *out++ = 0;
  return bf;
}
char *post_template(char *xfname)
{
 	FILE *f;
	char fname[BFSZ];
	char bf[BFSZ];
	static char msg[20000];
	char *s;

	if (strlen(ini_get(I_TEMPLATES))>0) 
		sprintf(fname,"%s/%s",ini_get(I_TEMPLATES),xfname);
	else  sprintf(fname,"%s/%s",ini_get(I_HEADERS),xfname);

	tpl_init();
	f = fopen(fname,"r");
 	if (f==NULL) {
		printf("Cannot open template {%s}\n",fname);
		return NULL;
	}

	strcpy(msg,"");
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-10,f)==NULL) break;
		s = strchr(bf,'\n');
		if (s!=NULL) *s = 0;
		s = strchr(bf,'\r');
		if (s!=NULL) *s = 0;

		s = tpl_replace(bf);
		strcat(msg,s);
		strcat(msg,"\n");

	}
	return msg;
}
void tpl_show(char *xfname)
{
 	FILE *f;
	char fname[BFSZ];
	char bf[BFSZ];
	char *s;

	dmsg("tpl_show called (%s)\n",xfname);
	sprintf(fname,"%s/%s",ini_get(I_HEADERS),xfname);
	dmsg("tpl_show called (%s)\n",fname);

	tpl_init();
	dmsg("tpl_show init done\n");
	f = fopen(fname,"r");
 	if (f==NULL) {
		printf("Cannot open {%s} \n",fname);
		return;
	}

	for (;!feof(f);) {
		if (fgets(bf,BFSZ-10,f)==NULL) break;
		s = strchr(bf,'\n');
		if (s!=NULL) *s = 0;
		s = strchr(bf,'\r');
		if (s!=NULL) *s = 0;

		s = tpl_replace(bf);
		printf("%s\n",s);

	}
	dmsg("tpl_show done\n");
}
static int tpl_mode;
static char tpl_item[2000];
static char tpl_dir[2000];
static char* tpl_indent_on[20];
static char* tpl_indent_off[20];
enum {T_NORM,T_ITEM,T_DIR,T_LIST,T_IF};
void tpl_setindent(char *s)
{
	int i;
 	/* %%indent%%1%%on%%off%% */
	int n;
	char *s2;
	char *s3,*s4;

	s += 10;
	n = atoi(s); if (n<0) return;
	if (n>9) return;
	s2 = strstr(s,"%%"); if (s2==NULL) return;
	s2 += 2;
	s3 = strstr(s2,"%%"); if (s3==NULL) return;
	*s3 = 0;
	s3 += 2;
	s4 = strstr(s3,"%%"); if (s4==NULL) return;
	*s4 = 0;
	tpl_indent_on[n] = mystrdup(s2);
	tpl_indent_off[n] = mystrdup(s3);
	for (i=n+1; i<10; i++) {
	 	if (tpl_indent_on[i]==NULL) {
	 		tpl_indent_on[i] = "";
	 		tpl_indent_off[i] = "";
		}
	}
	return;
}
void tpl_set_buttons(void)
{
	char bf[BFSZ];
	if (gfrom>group_from) { 
	  	sprintf(bf,"%s?cmd=xover&group=%s&from=%d",path_script,towww(group),gfrom-gmax);
		var_set("b_prev",bf);
	}
	if (gto<group_to) { 
		sprintf(bf,"%s?cmd=xover&group=%s&from=%d",path_script,towww(group),gto);
		var_set("b_next",bf);
		sprintf(bf,"%s?cmd=xover&group=%s",path_script,towww(group));
		var_set("b_recent",bf);
	} 
	sprintf(bf,"%s?cmd=list",path_script);
	var_set("b_topgroups",bf);
	sprintf(bf,"%s?cmd=listall",path_script);
	var_set("b_allgroups",bf);
	sprintf(bf,"%s?cmd=post&group=%s",path_script,towww(group));
	var_set("b_post",bf);
	sprintf(bf,"%s?cmd=f_find&group=%s",path_script,towww(group));
	var_set("b_search",bf);
}

void tpl_init(void)
{
	char bf[BFSZ];
	tpl_mode = T_NORM;
	strcpy(tpl_item,"");
	sprintf(bf,"%s?cmd=list",path_script);
	var_set("b_topgroups",bf);
	sprintf(bf,"%s?cmd=listall",path_script);
	var_set("b_allgroups",bf);
	sprintf(bf,"%s?cmd=xover",path_script);
	var_set("b_group",bf);
	sprintf(bf,"%s",path_script);
	var_set("b_action",bf);
	sprintf(bf," <FONT SIZE=-2> \n <a href=\"http://netwinsite.com/\"> DNEWSWEB 4.0-Beta-13 Copyright &copy; <I>NetWin Ltd</I>\n </a></FONT> ",path_script);
	var_set("copyright",bf);

	ncpy(group,query_find("group"),BFSZ-1);
 	var_set("group",group);
	if (strlen(group)==0) {
		ncpy(group,form_find("group"),BFSZ-1);
	 	var_set("group",group);
	}

	sprintf(bf,"%s?cmd=f_find&group=%s",path_script,towww(group));
 	var_set("b_searchgrp",bf);
	var_set("action",path_script);
	if (strlen(ini_get(I_SEARCH))>0) {
		sprintf(bf,"%s?cmd=f_search",path_script);
		var_set("b_search",bf);
	}
}
/*
	Add options like
	        %%if var_name
			xxxx
		%%endif
*/

char *tpl_replace(char *bf)
{
	char *s,*s2;
	char var[BFSZ];	
	static int lvl;
	switch (tpl_mode) {
	  case T_IF:
 		if (strncmp(bf,"%%ifdef%%",9)==0) {
	  		lvl++;
		} else if (strcmp(bf,"%%endif%%")==0) {
			lvl--;
			if (lvl==0) tpl_mode = T_NORM;
		}
		if (lvl==0) if (strcmp(bf,"%%else%%")==0) tpl_mode = T_NORM;
		break;
	  case T_NORM:
 		if (strcmp(bf,"%%begin_item%%")==0) {
			tpl_mode = T_ITEM;
			strcpy(tpl_item,"");
			return "";
 		} else if (strncmp(bf,"%%else%%",9)==0) {
			tpl_mode = T_IF;
			lvl = 1;
 		} else if (strncmp(bf,"%%ifdef%%",9)==0) {
			// %%if%%var%%
			s = bf+9;
			s2 = strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(var,s,s2-s);
			if (strcmp(var_find(var),"")==0) {
				tpl_mode = T_IF;
				lvl = 1;
				dmsg("Variable undefined {%s}\n",var);
			} else dmsg("Variable is defined {%s}\n",var);
 		} else if (strncmp(bf,"%%ifgroup%%",11)==0) {
			// %%if%%var%%
			s = bf+11;
			s2 = strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(var,s,s2-s);
			if (!group_match(var,group)) {
				tpl_mode = T_IF;
				lvl = 1;
				dmsg("Group didn't match  {%s} {%s}\n",var,group);
			} else dmsg("Group matched {%s} {%s}\n",var,group);
 		} else if (strncmp(bf,"%%include%%",11)==0) {
			// %%if%%var%%
			s = bf+11;
			s2 = strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(var,s,s2-s);
			tpl_show_file(var);
 		} else if (strcmp(bf,"%%quote_body%%")==0) {
			tpl_quote_body();
			return "";
 		} else if (strcmp(bf,"%%show_item%%")==0) {
			tpl_show_item();
			return "";
		} else if (strcmp(bf,"%%begin_dir%%")==0) {
			tpl_mode = T_DIR;
			return "";
		} else if (strncmp(bf,"%%indent%%",10)==0) {
		   	tpl_setindent(bf);
			return "";
		} else return tpl_replace_vars(bf);
		break;
	  case T_ITEM:
		if (strcmp(bf,"%%end_item%%")==0) {
		 	tpl_mode = T_NORM;
			tpl_show_group();
			return "";
		}
		strcat(tpl_item,bf);
		break;		
	  case T_DIR:
		if (strcmp(bf,"%%end_dir%%")==0) {
		 	tpl_mode = T_NORM;
			tpl_show_list();
			return "";
		}
		break;		
	}
	return "";
}

static char *var_names[200];
static char *var_values[200];
static int nvar;
char *var_find(char *var)
{
 	int i;
	for (i=0; i<nvar; i++) {
	 	if (strcmp(var_names[i],var)==0) return var_values[i];
	}
	return "";
}
void var_set(char *var, char *val)
{
	int i;
	if (val==NULL) return;
	for (i=0; i<nvar; i++) {
	 	if (strcmp(var,var_names[i])==0) {
			myfree(var_values[i]);
			goto setit;
		}
	}
	i = nvar++;
	var_names[i] = mystrdup(var);
setit:
	var_values[i] = mystrdup(val);
}
char *tpl_replace_vars(char *orig)
{
	char var[BFSZ];
	char *s=orig;
	char *s2;
again:
	s = strstr(s,"%%");
	if (s==NULL) return orig;
	s += 2;
	s2 = strstr(s,"%%");
	if (s2==NULL) return orig;
	ncpy(var,s,s2-s);
	tpl_replace_var(s-2,(s2+2) - (s-2) ,var_find(var));
	s -= 2;
	goto again;
}
void tpl_replace_var(char *bf, int len, char *var)
{
	int nlen = strlen(var);	   
//abc%%old%%rest_
//123456789012345
	memmove(bf+nlen,bf+len,strlen(bf+len)+1);
	strncpy(bf,var,nlen);
}
// ref = <ref1> <ref2>
// mid = <ref1>
int ref_find(char *ref)
{	
	int i;
	int j;
	char bf[BFSZ];
	int np;
	char *p[100];
	strcpy(bf,ref);
	np = token_split(bf,p," ",20);

	for (j=np-1; j>=0; j--) {
		for (i=0; i<nitem; i++) {
			if (strcmp(p[j],x_mid[i])==0) return i;
		}
	}
	return -1;
}
int ref_depth(char *ref)
{
 	char bf[BFSZ];
	char *s;
	int tot;
	ncpy(bf,ref,BFSZ-1);
	s = strtok(bf," ");
	for (tot=1; s!=NULL; tot++) {
	 	s = strtok(NULL," ");
	}
	if (tot>9) tot = 9;
	return tot;
}
void tpl_show_group(void)
{
	int p[2000]; 
	char bf[BFSZ];
	char *s;
	int repeat;
	int nloop;
	int j;
	int z;
	int depth,d,newdepth;
	int i,ii;
	for (i=0; i<nitem; i++) p[i] = i;

	for (i=0; i<nitem; i++) x_f[i] = -1;
	for (i=0; i<nitem; i++) {
		x_fi[i] = i;
		if (strlen(x_ref[i])>0) {
			x_depth[i] = ref_depth(x_ref[i]);
		 	j = ref_find(x_ref[i]);
			if (j>=0) x_fi[i] = j; 
			else x_f[i] = i;
		} else x_f[i] = i;
		/* dmsg("[%d] points to %d\n",i,x_fi[i]);*/
	}

	for (repeat=TRUE;repeat && (nloop<10);nloop++) {
	  repeat = FALSE;
	  for (i=0; i<nitem; i++) {
	 	if (x_f[i] <0) {
	 	 	x_f[i] = x_f[x_fi[i]] + i/(double) pow(50,x_depth[i]); 
			if (x_f[i]<0) repeat = TRUE;
			/* dmsg("[%d] set to %d = %g %g [%g]+%g %g\n",i,x_fi[i],x_f[i],(double) pow(50,x_depth[i]),x_f[x_fi[i]],i/(double) pow(50,x_depth[i]),(double) pow(50,(double) x_depth[i]))	;*/
	 	} 
	  }
	}
	if (!search_result) {
		qsort(p,nitem,sizeof(int),xover_cmp);
		printf("%s\n",tpl_indent_on[1]);
	}
	depth = 1;
	for (i=0; i<nitem; i++) {
		ii = p[i];
		if (search_result) goto skip;
		newdepth = ref_depth(x_ref[ii]);
		for (d = depth+1; d <= newdepth; d++) {
			printf("%s\n",tpl_indent_on[d]);
		}
		for (d = depth; d > newdepth; d--) {
			printf("%s\n",tpl_indent_off[d]);
		}
		depth = newdepth;
skip:		sprintf(bf,"%s?cmd=article&group=%s&item=%d",path_script,towww(group),x_item[ii]);
		var_set("b_item",bf);
		var_set("h_subject",value_encode(x_subject[ii]));
		if (depth>1) {
			z = (depth-1) * 2;
			if (z>10) z=10;
			memset(bf,'-',z);
			bf[z] = '>';
			bf[z+1] = 0;
			var_set("arrows",value_encode(bf));
		}
		var_set("h_from",value_encode(x_from[ii]));
		var_set("h_lines",x_lines[ii]);
		var_set("h_score",x_score[ii]);
		var_set("h_group",value_encode(x_group[ii]));
		var_set("h_date",value_encode(x_date[ii]));
		var_set("h_date_trim",value_encode(h_date_trim(x_date[ii])));
		var_set("h_from_trim",value_encode(h_from_trim(x_from[ii])));
		var_set("h_subject_60",value_encode(h_trim(x_subject[ii],60)));
		var_set("h_subject_40",value_encode(h_trim(x_subject[ii],40)));
		var_set("h_subject_30",value_encode(h_trim(x_subject[ii],30)));
		strcpy(bf,tpl_item);
		s = tpl_replace_vars(bf);
		printf("%s\n",s);
	}
	if (search_result) return;
	for (d = depth; d > 1; d--) {
		printf("%s\n",tpl_indent_off[d]);
	}
	printf("%s\n",tpl_indent_off[1]);
}
/*
x y <a@b>	= x y
a@b		= a
a@b (name)	= name
*/
char *h_from_trim(char *s)
{
	static char bf[BFSZ];
	char *s2;
	strcpy(bf,s);
	/* a@b (name) */
	s = strchr(bf,'(');
	if (s!=NULL) { 
		s2 = strchr(s,')');
		if (s2==NULL) return bf;
		*s2 = 0;
		return s+1;
	}
	/* x y <a@b> = x y*/
	s = strchr(bf,'<');
	if (s!=NULL) {
	 for (s=bf;*s!=0; s++) if (!isspace(*s)) break;
	 if (*s!='<') {
		s2 = strchr(s,'<');
		if (s2==NULL) return bf;
		*s2 = 0;
		return s;
	 } else { 	/* <a@b>*/
		s2 = strchr(s,'>');
		if (s2==NULL) return bf;
		*s2 = 0;
		return s+1;
	 }
	}

	/*a@b*/
	for (s=bf;*s!=0; s++) if (!isspace(*s)) break;
	for (s2=s;*s2!=0;s2++) if (isspace(*s2)) break;
	*s2 = 0;
	return s;
}
char *h_trim(char *s, int len)
{
	static char bf[BFSZ];
	ncpy(bf,s,len);
	if (strlen(s)>len) strcat(bf,"*");
	return bf;
}
/* Tue, 3 Jan 98 23:33:33 GMT */
/* 3 Jan 93 */

char *h_date_trim(char *instr) 
{
	static char bf[BFSZ];
	char *s;
	s = strchr(instr,',');
	if (s==NULL) s = instr; else s++;
	for (;*s==' ';) s++;
	strcpy(bf,s);
	s = strchr(bf,' '); if (s==NULL) return bf;
	s = strchr(s+1,' '); if (s==NULL) return bf;
	s = strchr(s+1,' '); if (s==NULL) return bf;
	*s = 0;
	return bf;
}
void tpl_show_list(void)
{
	show_dlist();

}
void tpl_show_top(void)
{
	printf("show list of top news groups\n");
}

void show_article(void)
{ 
	int part = atoi(query_find("part"));
	if (part>0) { show_article_part(part);	return;}
	tpl_init_item();
	tpl_show("item.tpl");
}
static char boundary[BFSZ];
static int isimage = FALSE;
static char mime[BFSZ];
static char xhtml[BFSZ];
static char encoding[BFSZ];
void tpl_init_item(void)
{
	char *s,*x;
	char subject[BFSZ];
	static char from[BFSZ];
	static char date[BFSZ];
	static char ref[BFSZ];
	static char content[BFSZ];
	char bf[BFSZ];
	char *s2;
	int gothead = FALSE;
	int multi,got,istext;
	char newsgroups[BFSZ];


	int item = atoi(query_find("item"));
	int related = item;

	multi = got = istext = FALSE;
	strcpy(subject,"");
	strcpy(from,"");
	strcpy(date,"");
	strcpy(newsgroups,"");
	related = item;

	printf("Pragma: cache\n");
	printf("Content-type: text/html\n\n");

	var_set("group",group);
	var_set("item",query_find("item"));
	sprintf(bf,"%s?cmd=xover&group=%s&related=%d",path_script,towww(group),related);
	var_set("b_related",bf);
	sprintf(bf,"%s?cmd=xover&group=%s",path_script,towww(group));
	var_set("b_recent",bf);
	sprintf(bf,"%s?cmd=list",path_script);
	var_set("b_topgroups",bf);
	sprintf(bf,"%s?cmd=listall",path_script);
	var_set("b_allgroups",bf);
	sprintf(bf,"%s?cmd=post&group=%s",path_script,towww(group),query_find("item"));
	var_set("b_post",bf);
	sprintf(bf,"%s?cmd=follow&group=%s&item=%s",path_script,towww(group),query_find("item"));
	var_set("b_follow",bf);

	s = get_line(instr);
	for (;s!=NULL;) {
		if (strcmp(s,"\015")==0) break;
		head_get("subject:",subject,s);
		head_get("date:",date,s);
		head_get("from:",from,s);
		head_get("references:",ref,s);
		head_get("newsgroups:",newsgroups,s);
		head_get("mime-version:",mime,s);
		head_get("x-html:",xhtml,s);
		head_get("content-type:",content,s);
		head_get("content-transfer-encoding:",encoding,s);

		s2 = strstr(s,"boundary=");
		if (s2==NULL) s2 = strstr(s,"Boundary=");
		if (s2!=NULL) {
		 	s2 = strchr(s2,'"');
			strcpy(boundary,"--");
			if (s2!=NULL) ncpy(boundary+2,s2+1,BFSZ-10);
			s2 = strchr(boundary,'"');
			if (s2!=NULL) *s2 = 0;
		}
		s = get_line(instr);
	}

	var_set("h_from",value_encode(from));
	var_set("h_references",value_encode(ref));
	var_set("h_subject",value_encode(subject));
	var_set("h_date",value_encode(date));
	var_set("h_newsgroups",value_encode(newsgroups));
	var_set("h_content-type",value_encode(content));
}
void tpl_quote_body(void)
{ 
	char *s;

	if (instr==NULL) return;
	s = strchr(from,'\015'); 
	if (s!=NULL) *s = 0;
	printf("%s wrote:\n",from);
	s = get_line(instr);
	for (;s!=NULL;) {
		printf(">%s",s);
		s = get_line(instr);
	}
}
/*
	For clarinet *.web groups

  <img src='*clari.net  --> <img src='news:*clari.net
  <a href='*clari.net'  --> <a href='news:*clari.net'
*/
char *clari_encode(char *ss);
void tpl_show_item(void)
{
	static int multi;
	int istext = FALSE;
	int ishtml = FALSE;
	char *x;
	int gothead = FALSE;
	int isquoted=FALSE;
	int isbase64=FALSE;
	char bf[BFSZ];
	int got;
	char *s;
	int item = atoi(query_find("item"));
	char *s2;
	int israw = FALSE;
	strlwr(encoding);
	multi = FALSE;
	if (strlen(xhtml)>0) israw = TRUE;
	if (strlen(mime)>0) if (strlen(boundary)>0) multi = TRUE;

	if (!multi) if (strstr(encoding,"base64")!=NULL) {
		printf("<img src=\"%s?cmd=article&group=%s&item=%d&part=99\">",path_script,towww(group),item);
		goto endbit;
	}
	got = 0;
	if (multi) {
	  for (;s!=NULL;) {
		s2 = strchr(s,'\015') ;	if (s2!=NULL) *s2 = 0; s2 = strchr(s,'\012') ; if (s2!=NULL) *s2 = 0;
		if (strstr(s,boundary)!=NULL) {
			if (istext) if (!ishtml) printf("</pre>\n"); 
			gothead = FALSE; istext = FALSE; got++;
			isimage = FALSE;
		}
		if (strlen(s)==0) {
			if (istext) { 
				if (!ishtml) printf("<pre>\n");
				else printf("<hr>");
			}
			gothead = TRUE;
			if (isimage) 
			  printf("<img src=\"%s?cmd=article&group=%s&item=%d&part=%d\">",path_script,towww(group),item,got);
		}
		else {
			if (!gothead) {
				/* printf("%s<br>\n",s);*/
				strlwr(s);
				if (strstr(s,"text/")!=NULL) istext = TRUE;
				if (strstr(s,"image/")!=NULL) isimage = TRUE;
				if (strstr(s,"text/html")!=NULL) ishtml = TRUE;
				if (strstr(s,"base64")!=NULL) isbase64 = TRUE;
				if (strstr(s,"quoted-printable")!=NULL) isquoted = TRUE;
			} else {
				if (ishtml) {
					if (isquoted) printf("%s",decode_quote(s));
					else if (isbase64) printf("%s",decode_base64s(s));
					else printf("%s",s);
				} else if (istext) {
					printf("%s",text_encode(s));
				}
			}
		}
		s = get_line(instr);
	  }
 	  goto endbit;
	}

	if (!israw) printf("<pre>\n");
	s = get_line(instr);
	isimage = FALSE;
	for (;s!=NULL;) {
		if (strncmp(s,"begin ",6)==0) {
			ncpy(bf,s,BFSZ-1);
			x = strtok(bf," \012\015"); if (x==NULL) goto skip;
			x = strtok(NULL," \012\015"); if (x==NULL) goto skip;
			if (atoi(x)<100) goto skip;
			x = strtok(NULL," \012\015"); if (x==NULL) goto skip;
			strlwr(x);
			if (strstr(x,".jpg")!=NULL) goto isok;
			if (strstr(x,".jpeg")!=NULL) goto isok;
			if (strstr(x,".gif")!=NULL) goto isok;
			goto skip;
isok:			isimage = TRUE;
			printf("<img src=\"%s?cmd=article&group=%s&item=%d&part=100\">",path_script,towww(group),item,got);
    		}
skip:	
		if (!israw) {
			if (!isimage) printf("%s",text_encode(s));
		} else {
			printf("%s",clari_encode(s));
		}
		if (isimage) {
		 	if (strncmp(s,"end",3)==0) isimage = FALSE;
		}
		s = get_line(instr);
	}
	if (!israw) printf("</pre>\n");
endbit:;
}
void clari_insert(char *s, char *in);
void clari_insert(char *s, char *in)
{
	memmove(s+strlen(in),s,strlen(s)+1);
	strncpy(s,in,strlen(in));
}
char *clari_encode(char *ss)
{
	char *s2;
	char *s=ss;
again:
	s = strstr(ss,"<IMG SRC=\'");
	if (s!=NULL) {
		s2 = strchr(s,':');
		if (s2==NULL) {clari_insert(s+10,"news:"); goto again;}
		if ((s2-s)>17) {clari_insert(s+10,"news:"); goto again;}
	}
	s = strstr(ss,"<A HREF=\'");
	if (s!=NULL) {
		s2 = strchr(s,':');
		if (s2==NULL) {clari_insert(s+9,"news:"); goto again;}
		if ((s2-s)>17) {clari_insert(s+9,"news:"); goto again;}
	}
	return ss;
}
/*  <img src='*clari.net  --> <img src='news:*clari.net
  <a href='*clari.net'  --> <a href='news:*clari.net'
*/

 
/*
Things to do:
	Change all output sections so they use the replace system.
	Threading algorithym
	Fix dnewsweb known bugs (allow etc...)
	Test all image combinations, allow html etc.
	Add 	%%ifdef variable  		to file
*/
