/* Copyright (C) 1995 NetWin, New Zealand, All rights reserved. */
/*
define all form and query variables starting gl_ 
as template variables. 
rcmd_catchup and logoff
test if browse and search cause subscaribe to occur. 
Document changes. ini settings etc.
Walk thru test
Test ecapsulated mime.
----  
Test with old templates
Add two functions to authenticaton

  
	If first char of message is a capital M and if subject
	contains (nn/nn) or [nn/nn] and if length greather than 10K 
	then display it as an attachment.
	
	  IF 'getparts' then search xover for items with similar subject
	  minus the [] or () part, 
	  Then fetch each message in order (if all found)
	  Then store uu data to pid.tmp file
	  decode it
	  Send it to user.
	  
		
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#ifdef WIN32
/* define vsnprintf */
#define vsnprintf _vsnprintf
#endif


#ifdef isunix
#include <unistd.h>
#endif
#ifdef WIN32
#include <windows.h>
#include <io.h>
#else
#include <signal.h>
#include <errno.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 docache=FALSE;
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
*/
void post_log_do(char *arg_list,...);
void display_info(char *epath);
static int total_read;
char *decode_basic(char *rest);
char *rfc1522s(char *in);
void tpl_show_latest(void);
void show_error(void);
char *pad_right(char *s, int n);
int decode_base64(char *buf, char *out);
void boundary_add(char *s);
int decode_uu(char *line, char *out);
static char tplname[BFSZ];
char *user_get(char *s);
void r_login(int nooutput);
void f_login(void);
void r_parts(void);
void r_user(void);

void f_cancel(void);		
char *tohtml(char *s);

void cmd_tomail_add(void);
void cmd_tomail_del(void);
void cmd_tomail_add_thread(void);

void cmd_next(void);
void cmd_last(void);
void cmd_thread_next(void);
void cmd_thread_last(void);
void group_fetch(void);
void do_login(char *bf);
void f_config(void);
void r_config(void);
char *eoltospace(char *s);
int nntp_read_timeout(int c, char *bf, int sz);
static int done_content;
void do_content(void);
void r_xdebug(void);
char *get_line_head(String *instr);
char *either_find(char *s);
int parts_subject(char *subject, char *subj, char *fname, int *parti, int *partmax);
char *cookie_date_plus(int tplus);

char *attach_fname(void);
char *attach_type(void);
char *attach_showname(void);
char *get_quote(char *s);
char *temp_fname(void);
int open_status(void);
void cgi_addvar(char *name, char *value);
void cmd_up(void);
void cmd_search_down(void);
void cmd_search_up(void);
char *get_cookie(char *var);
int date_tonum(char *instr);


void cmd_down(void);
void cmd_latest(void);
void cmd_search(void);
void cmd_related(void);
static int isquery;
void get_line_clear(void);
void show_dline(char *s);
void r_logout(void);
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 set_id(void);
void group_add(char *s);
void r_subscribe(void);
char *int_str(int i);
int xuser_clear(void);
void var_set_log(char *var, char *val, char *info);
void do_content_force(void);
static int no_up_button;

void group_del(char *s);
void r_grpsearch(void);
void show_xsearch(void);
void f_search(void);
static int page_from;
static int page_to;
void r_search(void);
void tpl_show_item_image(void);
void f_post(void);
void h_printf(char * arg_list, ...);
int tcp_printf(int chan, char * arg_list,...);
void get_utag(char *ut);
void set_utag(char *user, char *dom);
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, int len) ;
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 *file_tpl(char *fname);
int lib_file_length(char *fname);
void do_wrap(char *msg);
char *fix_crlf(char *msg);
void tpl_set_latest_vars(void);
void r_showfile(void);



int JLLgetindentlen(char *readpos);
int JLLgetindentonline(char *indent, char *readpos);
void JLLoutput(void (*outputfn)(char *), char *thisline);
int JLLprintword(char *readpos, char *indent, char *thisline, char *nextword, int wrap, int wordsfoundonline, void (*outputfn)(char *));
int JLLgetnextword(char *nextword, char *readpos);
char *JLLfindbeginningofline(char *readpos);
void JLLformat_wrap_output(void (*outputfn)(char *),char *readpos,int wrap);
int JLLmovereadposontonextword(char *readpos);
int JLLgetlenofnextline(char *readpos);

void JLLnntp_write_s(char *s);
void JLLcomment_write_s(char *s);
void JLLtext_encode(char *s);
int JLLchan;
int real_item;

#define xisspace(x) ((x==' ') || (x=='\t'))


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);
char *cr_encode(char *s);
char *real_from(char *xin);
char *cr_decode(char *s);
void tpl_init_item(void);
void tpl_show_item(void);
void tpl_init_sbar(int from, int to, int total, char *key);
void user_set(char *var, char *val);
void show_text(char *s, char *xeol);
char *show_text_fn(char *s);

char *head_decode(char *s);
char *attach_justname(void);

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

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


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

static int b_item= -1;
char *ref_encode(char *s);

static char path_script[BFSZ];
static char path_attach[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 r_catchup(void);
void zmsg(char * arg_list, ...);

void r_raw(void);
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_xthread(void);  /* from=nnn   max=nnn*/
void r_list(void);
char *mygetenv(char *s);

static char ukey[BFSZ];
char *h_from_hide(char *s);

char *h_date_numeric(char *instr); 
char *h_date_trim(char *instr);
char *h_trim(char *s, int len);
char *h_from_trim(char *s);
#define MAX_ITEMS 4010


#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 *ref, char *mid, char *bytes, int titems);
void xover_add_all(char *item, char *subject, char *from, char *lines, char *date, char *ref, char *mid, char *bytes, int titems);
void xover_showord(void);
int ip_banned(char *ip);

char *tpl_get_nitems(char *bf);
char *tpl_get_lastxhdr(char *bf);

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,I_LOG_FILE,I_SORT_REV,I_WRAP_POST,I_WRAP_TEXT
, I_CONTENT_TYPE, I_ADD_MID, I_TELLNEWS_PASS, I_PRE_DISABLE
, I_WORKAREA, I_POP_HOST, I_SORT_ORDER
, I_AUTO_SUBSCRIBE
, I_POST_DOMAIN
, I_POST_REQUIRE_LOGIN
, I_REQUIRE_LOGIN
, I_WEB_LOGIN
, I_POP_AUTHENT
, I_LOGIN_TIMEOUT
, I_ADD_BR
, I_KEEP_FORMATING
, I_POST_NOCACHE
, I_SPACES
, I_IGNORE_ADDRESS
, I_FROM_IP
, I_FROM_FORCE
, I_REMOVE_NOCACHE
, I_USER_CLEAR
, I_GROUP_LIST
, I_AUTH_NNTP
, I_IGNORE_ID
, I_FORMAT_HTML
, I_LINK_ADD
, I_NON_ASCII
, I_USE_XACTIVE
, I_DEFAULT_USER
, I_POST_LOGIN
, I_LATEST_PATH
, I_FROM_NOCHECK
, I_TOMAIL
, I_DUMP
, I_LATEST_IP
, I_WEB_LOGIN_NOCHECK
, I_ISTEXT_IMG
, I_ISBINARY_IMG
, I_ISBINARY_SIZE
, I_USE_HOME
, I_REMOVE_NAME
, I_FROM_REMOTE_USER
, I_FROM_OPTIONAL
, I_SHOWINI
, I_ALLOW_SCRIPT
, I_ALLOW_FORM
, I_ALLOW_JAVA
, I_FROM_WEB_LOGIN
, I_FROM_ALLOW_EMAIL
, I_LOGIN_UTAG
, I_LOGIN_COOKIE
, I_SUBSCRIBE_MANUAL
, I_ADD_HEADER
, I_VAR_HEADER
, I_POST_ANONYMOUS
, I_USER_STRIP_DOMAIN
, I_USER
, I_PASS
, I_NO_DOUBLE_SPACE
, I_NO_MAILTO
, I_NEXTLAST_REF
, I_SORT_POST_DATE
, I_POST_LOGIN_POST
, I_POST_NOT_LOGGED_IN_ACTION
, I_POST_REQUIRE_LOGIN_AFTER
, I_WINDOW_DAYS
, I_THREAD_NO_SUBJECT
, I_XOVER_CHUNK
, I_IP_BAN_FILE
, I_DISABLE_REPLYTO
, I_DISABLE_SENDER
, I_LOGUSE
, I_LOGGEDIN
, I_CGI_ATTACH
, I_UUENCODE_ATTACH
, I_PAGE_EXACT
, I_GROUP_DIR_GROUPS
, I_GROUP_DIR_ITEMS
, I_POST_LOG_FILE
, I_SORT_SUBJ
};
char *ini_names[]={
	"headers","xover_pre","xover_post","newshost", "debug", "pagesize"
        ,"groups", "allow" , "sort", "noadvert", "search", "cgi_path"
        ,"templates" 
        ,"post_groups", "post_users","log_file"
        ,"sort_rev","wrap_post","wrap_text"
        ,"content_type", "add_mid", "tellnews_pass"
        ,"pre_disable", "workarea", "pop_host"
        ,"sort_order"
        ,"auto_subscribe"
        ,"post_domain"
        ,"post_require_login"
        ,"require_login"
        ,"web_login"
        ,"pop_authent"
        ,"login_timeout"
        ,"add_br"
        ,"keep_formating"
        ,"post_nocache"
        ,"spaces"
        ,"ignore_address"
		,"from_ip"
		,"from_force"
		,"remove_nocache"
		,"user_clear"
		,"group_list"
		,"auth_nntp"
		,"ignore_id"
		,"format_html"
		,"link_add"
		,"non_ascii"
		,"use_xactive"
		,"default_user"
		,"post_login"
		,"latest_path"
		,"from_nocheck"
		,"tomail"
		,"dump"
        ,"latest_ip"
		,"web_login_nocheck"
		,"istext_img"
		,"isbinary_img"
		,"isbinary_size"
		,"use_home"
		,"remove_name"
		,"from_remote_user"
		,"from_optional"
        ,"showini"
        ,"allow_script"
        ,"allow_form"
        ,"allow_java"
		,"from_web_login"
		,"from_allow_email"
		,"login_utag"
        ,"login_cookie"
		,"subscribe_manual"
		,"add_header"
		,"var_header"
		,"post_anonymous"
		,"user_strip_domain"
		,"user"
		,"pass"
        ,"no_double_space"
        ,"no_mailto"
		,"nextlast_ref"
		,"sort_post_date"
		,"post_login_post"
		,"post_not_logged_in_action"
		,"post_require_login_after"
		,"window_days"
		,"thread_no_subject"
		,"xover_chunk"
		,"ip_ban_file"
		,"disable_replyto"
		,"disable_sender"
		,"loguse"
		,"loggedin"
		,"cgi_attach"
		,"uuencode_attach"
		,"page_exact"
		,"group_dir_groups"
		,"group_dir_items"
		,"post_log_file"
		,"sort_subj"
		, NULL
};
#define strdup d_strdup
#define PAGESIZE page_size()
char *content_type(void);
char *content_type(void)
{
	static char bf[BFSZ];
	strcpy(bf,ini_get(I_CONTENT_TYPE));
	if (strlen(bf)==0) strcpy(bf,"text/html");
	return bf;
}
char *d_strdup(const char *s);
char *d_strdup(const char *s)
{
	char *n = malloc(strlen(s)+1);
	strcpy(n,s);
	return n;
}
int page_size(void)
{
	int i = atoi(ini_get(I_PAGESIZE));
	if (i==0) i = 30;
	return i;
}
static char ltemplate[BFSZ];
static char auth_username[BFSZ]="";
static int body_lines;
static int items_per_group;
static int ngroups;
static int x_item[MAX_ITEMS];
static char *x_score[MAX_ITEMS];
static char *x_group[MAX_ITEMS];
static double x_f[MAX_ITEMS];
static int x_fi[MAX_ITEMS];
static int x_depth[MAX_ITEMS];
static char *x_subject[MAX_ITEMS];
static char *x_subjectsrt[MAX_ITEMS];
static char *x_from[MAX_ITEMS];
static char *x_date[MAX_ITEMS];
static char *x_ref[MAX_ITEMS];
static char *x_lines[MAX_ITEMS];
static char *x_bytes[MAX_ITEMS];
static char *x_mid[MAX_ITEMS];
static int x_titems[MAX_ITEMS]; /* number of items in the current thread */
static int nitem;

/* used as a temporary store for all xover data.  needed for articles in thread calculation */
static int all_x_item[MAX_ITEMS];
static char *all_x_subject[MAX_ITEMS];
static char *all_x_subjectsrt[MAX_ITEMS];
static char *all_x_from[MAX_ITEMS];
static char *all_x_date[MAX_ITEMS];
static char *all_x_ref[MAX_ITEMS];
static char *all_x_lines[MAX_ITEMS];
static char *all_x_bytes[MAX_ITEMS];
static char *all_x_mid[MAX_ITEMS];
static int all_x_titems[MAX_ITEMS]; /* number of items in the current thread */
static int all_nitem;

void emsg_reset(void);
static int skip_content;
static char global_newsgroups[BFSZ];	
static char global_entire_from[BFSZ];	
static char global_subject[BFSZ];	
static char guser[BFSZ]; /* chrisp */
static char utag[BFSZ]; /* chrisp domain */
static char udom[BFSZ];
static char utage[BFSZ]; /* chrisp+domain (encoded) */
static int chan;
static char thisuser[BFSZ];
static char thispass[BFSZ];
static char group[BFSZ];
static char groupe[BFSZ];
static int gfrom,gto,gmax,group_from,group_to;
static int origfrom,origto;
static int dosort;
static char log_file[BFSZ];
static char post_log_file[BFSZ];
static int search_result = FALSE;
static int pre_disable=FALSE;
static char etext[10000];
static int format_html = FALSE;
#ifdef WIN32
#define environ _environ
#define putenv _putenv
#endif
#ifdef isunix
extern char **environ;
#endif

char *cookie_date(void);
static FILE *mystdin;
static int web_login;
static int rootonly=FALSE;
char main_type[BFSZ];

int bad_input(char *var)
{
	char *q = query_find(var);
	if (strchr(q,'<')!=NULL) return TRUE;
	if (strchr(q,'>')!=NULL) return TRUE;
	if (strchr(q,'"')!=NULL) return TRUE;
	if (strchr(q,'&')!=NULL) return TRUE;
	if (strchr(q,'%')!=NULL) return TRUE;
	if (strchr(q,'\'')!=NULL) return TRUE;
	if (strchr(q,';')!=NULL) return TRUE;
	if (strchr(q,'(')!=NULL) return TRUE;
	if (strchr(q,'+')!=NULL) return TRUE;

	
	return FALSE;
}

int main(int argc, char *argv[])
{
	char method[BFSZ];
	FILE *f;
	char cmd[BFSZ];
	int len;
	char *s;
	int nodump = FALSE;
#ifndef WIN32
	signal(SIGPIPE,SIG_IGN);
	signal(SIGFPE,SIG_IGN);
	signal(SIGHUP,SIG_IGN);
#ifdef SIGIOT 
	signal(SIGIOT,SIG_IGN);
#endif
#endif
	ncpy(main_type,"misc",100);
	mystdin = stdin;
	if (argc==2) if (strcmp(argv[1],"-test")==0) {
		char bf[100000];
		/* get env from data.env, and re-assign stdin to be data.in  */
		f = fopen("dump.env","rb");
		if (f==NULL) return FALSE;
		for (;!feof(f);) {
			fgets(bf,90000,f);
			s = strchr(bf,'\n'); if (s!=NULL) *s = 0;
#ifndef WIN32
			/* this is at least needed on linux but doesn't
			seem to matter on NT */
			s = strdup(bf);
			if (s==NULL) continue;
			putenv(s);
#else
			putenv(bf);
#endif
		}
		fclose(f);
		len = atoi(mygetenv("CONTENT_LENGTH"));
		f = fopen("dump.in","rb");
		if (f!=NULL) {
			if (len>0) mystdin = f;
		}	
		nodump = TRUE;
	}
	
	strcpy(log_file,"dnewsweb.log");
	db_setscript(mygetenv("SCRIPT_NAME"));
	lprintf(log_file,BFSZ-1,"%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));
	}
	ncpy(path_attach,path_script,BFSZ-1);
	if (strlen(ini_get(I_CGI_ATTACH))>0) {
		ncpy(path_attach,ini_get(I_CGI_ATTACH),100);
	}
	
	if (strlen(ini_get(I_LOG_FILE))>0) {
		strcpy(log_file,ini_get(I_LOG_FILE));
	}
	
	strcpy(post_log_file,ini_get(I_POST_LOG_FILE));
	
	if (strstr(ini_get(I_DEBUG),"true")!=NULL) dodebug = TRUE;
	if (!nodump) if (strstr(ini_get(I_DUMP),"true")!=NULL) {
		/* Dump environment variables and stdin to a file for debugging */	
		f = fopen("dump.env","wb");
		if (f!=NULL) {
			int i;
			for (i=0;environ[i]!=NULL;i++) {
				fprintf(f,"%s\n",environ[i]);
			}
			fclose(f);			
		}
		len = atoi(mygetenv("CONTENT_LENGTH"));
#ifdef FIX_STDOUT
		_setmode(_fileno(stdin),_O_BINARY);
#endif
		
		f = fopen("dump.in","wb");
		if (f!=NULL) {
			char bf[100000];
			int r;
			if (len>0) {
				r = fread(bf,1,len,stdin);
				fwrite(bf,1,r,f);
			}
			fclose(f);			
		}
		do_content_force();
		printf("Data dumped\n");
		exit(0);
	}
	if (strstr(ini_get(I_SORT),"true")!=NULL) dosort = TRUE;
	if (strcmp("true",ini_get(I_PRE_DISABLE))==0) pre_disable = TRUE;
	if (strcmp("true",ini_get(I_FORMAT_HTML))==0) {pre_disable = TRUE; format_html = TRUE;}
	if (!dodebug) emsg_reset();
	imsg("DNEWSWEB version %s\n",DNEWS_VERSION);
	imsg("DNEWSWEB, Startup, Looking for winsock dll\n");
	nntp_startup();
	nntp_alwaysblock();
	if (ip_banned(mygetenv("REMOTE_ADDR"))) {
		var_set("ip",mygetenv("REMOTE_ADDR"));
		tpl_show("banned.tpl");
		return TRUE;
	}
	ncpy(thisuser,mygetenv("REMOTE_USER"),BFSZ-1);
	imsg("thisuser (%s)\n",thisuser);
	var_set("remote_user",thisuser);
	web_login = ini_true(I_WEB_LOGIN);
	
	/*	con = atoi(ini_get(I_CONCURRENT));
	if (con>0) if (con>con_count(mygetenv("REMOTE_ADDR"))) {
	lprintf(bf,BFSZ-1,"Concurrent limit exceeded %d/%d ip %s",CON,con_count(mygetenv("REMOTE_ADDR")));
	var_set("error",bf);
	tpl_show("error.tpl");
	return TRUE;
	}
	*/
	if (strlen(ini_get(I_USER))>0) ncpy(thisuser,ini_get(I_USER),100);
	if (strlen(ini_get(I_PASS))>0) ncpy(thispass,ini_get(I_PASS),100);
	if (strlen(thisuser)==0) {
		if (web_login) imsg("web_login changed to false for this user as REMOTE_USER was not defined, you must configure your cgi to require logins for web_login to work\n");
		web_login = FALSE;
	}
	
	ncpy(thispass,mygetenv("AUTH_PASSWORD"),BFSZ-1); /* IIS apparently */
	if (strlen(thispass)==0) strcpy(thispass,decode_basic(mygetenv("HTTP_AUTHORIZATION")));
	if (strlen(ini_get(I_PASS))>0) ncpy(thispass,ini_get(I_PASS),100);
	/*imsg("web_login (%s) (%s)\n",thisuser,thispass);*/
	if (strlen(thispass)>0) imsg("web_login got password user=%s ***\n",thisuser);
	ncpy(method,mygetenv("REQUEST_METHOD"),BFSZ-1); strlwr(method);
	
	/*	con_add(mygetenv("REMOTE_ADDR"));*/
	
	query_get(); /* Get the xxx=yyy... parameters  */

	if (bad_input("utag")) goto bad_response;
	if (bad_input("group")) goto bad_response;
	if (bad_input("cmd")) goto bad_response;
	if (bad_input("item")) goto bad_response;
	if (bad_input("wild")) goto bad_response;
	if (bad_input("from")) goto bad_response;
	if (bad_input("to")) goto bad_response;
	if (bad_input("key")) goto bad_response;
	if (bad_input("item")) goto bad_response;
	if (bad_input("search_groups")) goto bad_response;

	if (strcmp(method,"post")==0) {
		imsg("Processing POST\n");
		if (ini_true(I_POST_NOCACHE)) {
			printf("Pragma: no-cache\n");
			printf("Cache-Control: no-cache\n");
			/*printf("Expires: Tue, 31 Dec 1996 23:59:59 GMT\n");*/
		}
		form_get(); /* get any form fields */
		
		if (!done_content) {
			if (docache) printf("Pragma: cache\n");
			done_content = TRUE;
			if (strcmp(form_find("cmd"),"login")==0) {
				if (strlen(form_find("user"))>0) {
					printf("Set-Cookie: dnews_user=%s; expires=%s\n",form_find("user"),cookie_date());
				}
			}
			ncpy(cmd,form_find("cmd"),200);
			ncpy(main_type,cmd,100);
			if (strcmp(cmd,"login")==0) done_content = FALSE;
			if (strcmp(cmd,"logout")==0) done_content = FALSE;
			if (done_content)  printf("Content-type: %s\n\n",content_type());
		}
		chan = nntp_open(ini_get(I_NEWSHOST));
		if (chan<0) { goto failed;}
		nntp_setblock(chan,TRUE); /* Set to blocking IO */
		
		ncpy(cmd,form_find("cmd"),200);
		get_utag(either_find("utag"));
		imsg("Post cmd (%s)\n",cmd);
		if (strcmp(cmd,"post")==0) r_post();
		else if (strcmp(cmd,"login")==0)  r_login(FALSE);
		else if (strcmp(cmd,"logout")==0)  r_logout();
		else if (strcmp(cmd,"config")==0)  r_config();
	}
	else
	{
		int i;
		static int dolatest;
		ncpy(ltemplate,"latest.tpl",BFSZ-10);
		for(i=0;i<argc;i++) {
			strlwr(argv[i]);
			if (strncmp(argv[i],"-latest",7)==0) {
				if (++i<argc) {
					if (strstr(argv[i],"..")==NULL) 
						if (strchr(query_find("ltemplate"),'\\')==NULL)
							if (strchr(argv[i],'/')==NULL)
								if (strstr(argv[i],".tpl")!=NULL)
									ncpy(ltemplate,argv[i],BFSZ-10);
				}
				dolatest = TRUE;break;
			}
		}
		if (dolatest) {
			/* use the script path they specify when running from the command prompt */
			if (strlen(ini_get(I_LATEST_PATH))>0) {
				ncpy(path_script,ini_get(I_LATEST_PATH),BFSZ-10);
			}
		};
		
		isquery = TRUE;

		imsg("Processing command (%.80s)\n",query_find("cmd"));
		get_utag(query_find("utag"));
		ncpy(cmd,query_find("cmd"),200);
		ncpy(main_type,cmd,100);
		if ((strcmp(cmd,"raw")!=0) && (strcmp(cmd,"article")!=0)&& (strcmp(cmd,"parts")!=0) && !ispressed("cmd_next") && !ispressed("cmd_thread_next") && !ispressed("cmd_thread_last") && !ispressed("cmd_last") && !dolatest) {
			skip_content = FALSE;
		} else {
			skip_content = TRUE;
		}
		if (!skip_content) {
			if (docache) printf("Pragma: cache\n");
			done_content = TRUE;
			if (strcmp(cmd,"logout")==0) done_content = FALSE;
			if (ispressed("cmd_logout")) done_content = FALSE;
			if (done_content) printf("Content-type: %s\n\n",content_type());
		}
imsg("skip_content %d\n",skip_content);
	
		if (strstrnc(cmd,"<script>")!=NULL) goto bad_input;
		if (strstrnc(utag,"<script>")!=NULL) goto bad_input;
		if (strstrnc(utag,query_find("group"))!=NULL) goto bad_input;

		chan = nntp_open(ini_get(I_NEWSHOST));
		if (chan<0) { goto failed;}
		nntp_setblock(chan,TRUE); /* Set to blocking IO */
		
		if ((strcmp(cmd,"latest")==0)||(dolatest)) {
			if (strlen(query_find("ltemplate"))!=0) 
				if (strstr(query_find("ltemplate"),"..")==NULL) {
					if (strchr(query_find("ltemplate"),'\\')==NULL)
						if (strchr(query_find("ltemplate"),'/')==NULL)
							if (strstr(query_find("ltemplate"),".tpl")!=NULL)
								ncpy(ltemplate,query_find("ltemplate"),BFSZ-10);
				}
				tpl_show(ltemplate);
				/*con_del(mygetenv("REMOTE_ADDR"));*/
				return 0;
		}
		else if (strcmp(cmd,"showini")==0) display_info(argv[0]);
		else if (strcmp(cmd,"raw")==0) r_raw();
		else if (strcmp(cmd,"xover")==0) r_xover();
		else if (strcmp(cmd,"xthread")==0) r_xthread();
		else if (strcmp(cmd,"list")==0) r_list();
		else if (strcmp(cmd,"xdebug")==0) r_xdebug();
		else if (strcmp(cmd,"listall")==0) r_listall();
		else if (strcmp(cmd,"article")==0) r_article();
		else if (strcmp(cmd,"parts")==0) r_parts();
		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_subscribe")==0) r_subscribe();
		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 if (strcmp(cmd,"login")==0) f_login();
		else if (strcmp(cmd,"showfile")==0) r_showfile();
		else if (strcmp(cmd,"r_login")==0) r_login(FALSE);
		else if (strcmp(cmd,"logout")==0) r_logout();
		else if (strcmp(cmd,"user")==0) r_user();
		else if (strcmp(cmd,"f_config")==0) f_config();
		else if (ispressed("cmd_cancel")) f_cancel();
		else if (ispressed("cmd_config")) f_config();
		else if (ispressed("cmd_user")) r_user();
		else if (ispressed("cmd_read")) r_xover();
		else if (ispressed("cmd_grpsearch")) r_grpsearch();
		else if (ispressed("cmd_logout")) r_logout();
		else if (ispressed("cmd_unsub")) r_unsub();
		else if (ispressed("cmd_latest")) cmd_latest();
		else if (ispressed("cmd_related")) cmd_related();
		else if (ispressed("cmd_up")) cmd_up();
		else if (ispressed("cmd_down")) cmd_down();
		else if (ispressed("cmd_search_up")) cmd_search_up();
		else if (ispressed("cmd_search_down")) cmd_search_down();
		else if (ispressed("cmd_tomail_add_thread")) cmd_tomail_add_thread();
		else if (ispressed("cmd_tomail_add")) cmd_tomail_add();
		else if (ispressed("cmd_tomail_del")) cmd_tomail_del();
		else if (ispressed("cmd_post")) f_post();
		else if (ispressed("cmd_find")) f_find();
		else if (ispressed("cmd_follow")) f_follow(TRUE);
		else if (ispressed("cmd_login")) f_login();
		else if (ispressed("cmd_next")) cmd_next();
		else if (ispressed("cmd_last")) cmd_last();
		else if (ispressed("cmd_thread_next")) cmd_thread_next();
		else if (ispressed("cmd_thread_last")) cmd_thread_last();
		else if (ispressed("cmd_catchup")) r_catchup();
		else if (ispressed("xdebug")) r_xdebug();
		else r_list();
        }
        if (strlen(etext)>0) {
			skip_content = FALSE;
			show_error();
        }
        imsg("Program finished ok, etextlen = %d\n",strlen(etext));
		if (ini_true(I_LOGUSE)) log_use(main_type,total_read);
		/*con_del(mygetenv("REMOTE_ADDR"));*/
        return 0;
failed:
        if ((strcmp(cmd,"article")!=0) && !ispressed("cmd_next") && !ispressed("cmd_last")) {
			if (docache) printf("Pragma: cache\n");
			done_content = TRUE;
			if (!done_content) printf("Content-type: %s\n\n",content_type());
        }
        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");
		if (ini_true(I_LOGUSE)) log_use(main_type,total_read);
		/*con_del(mygetenv("REMOTE_ADDR"));*/
        return 0;
bad_response:
bad_input:
		if (!done_content) printf("Content-type: %s\n\n",content_type());
        printf("Bad input fields\n");
		return 0;
}
int log_use(char *type, int total_read)
{
	FILE *f;
	f = fopen("dnewsweb.rec","a");
	if (f==NULL) return TRUE;
	if (strlen(type)==0) type = "misc";
	fprintf(f,"%d %d bytes. Function: %s  Process: %d\n",time(NULL),total_read,type,getpid());
	fclose(f);
	return TRUE;
}
char *decode_basic(char *rest)
{
	char *s;
	int n;
	char uinfo[BFSZ];
	static char user[BFSZ],pass[BFSZ];
	s = strstr(rest,"Basic");
	if (s==NULL) return "";
	if (strlen(s)>200) return "";
	if (strlen(s)<8) return "";
	n = decode_base64(s+6,uinfo);
	if (n<0) return "";
	if (n>BFSZ-1) return "";
	uinfo[n] = 0;
	strcpy(pass,"");
	s = strchr(uinfo,':');
	if (s!=NULL) *s = 0;
	ncpy(user,uinfo,200);
	if (s!=NULL) ncpy(pass,s+1,200);
	return pass;
}
void set_utag(char *user, char *dom)
{
	char bf[BFSZ];
	lprintf(bf,BFSZ-1,"%s %s",user,dom);
	if (ini_true(I_IGNORE_ADDRESS)) {
		srand(time(NULL));
		lprintf(ukey,100,"%d%d",abs(rand()%10000000),abs(rand()%10000));
		lprintf(bf,200,"%s %s:%s",user,dom,ukey);
		imsg("set utag (%s) (%s) (%s)\n",user,dom,bf);
	}
	user_read(bf);
	imsg("set utag (%s) (%s) (%s)\n",user,dom,bf);
	get_utag(bf);
}
void show_error(void)
{
	char bf[BFSZ];
	static int done;
	char fname[BFSZ];
	ncpy(bf,etext,200);
	
	imsg("DOING SHOW_ERROR content %d %d\n",done_content,skip_content);
	do_content_force();
	strcpy(fname,"error.tpl");
	if (strstr(etext,"423")!=NULL) strcpy(fname,"missing.tpl");
	if ((!lib_exists(file_tpl(fname))) || done) {
		do_content();
		printf("%s<br>Not found (%s)",etext,file_tpl(fname));
		return;
	}
	done=TRUE;
	do_content();
	
	var_set("error",etext);
	tpl_show(fname);        
}
int ini_true(int x)
{
	char bf[BFSZ];
	strcpy(bf,ini_get(x));
	strlwr(bf);
	if (strstr(bf,"true")!=NULL) return TRUE;
	return FALSE;
}
static char utag_user[BFSZ];
void get_utag(char *ut)
{
	char *s;
	char bf[BFSZ];
	
	if (ini_true(I_LOGIN_COOKIE)) {
		if (strlen(ut)==0) {
			ncpy(bf,get_cookie("dnews_utoken"),200);
			plustospace(bf); 
			ut = bf;
		}
	}
	if (strchr(ut,' ')==NULL) {
		s = strchr(ut,'+');
		if (s!=NULL) *s = ' ';
	}
	ncpy(utag,ut,200);
	ncpy(guser,ut,200);
	s = strchr(guser,':');
	if (s!=NULL) {
		*s = 0;
		ncpy(ukey,s+1,BFSZ-1);
	}
	ncpy(utag_user,guser,BFSZ-1);
	s = strchr(guser,' '); 
	if (s!=NULL) {
		*s = 0;
		ncpy(udom,s+1,BFSZ-1);
	} else {
		s = strchr(guser,'+'); 
		if (s!=NULL) {
			*s = 0;
			ncpy(udom,s+1,BFSZ-1);
		}
	}
	
	
	if (strlen(ini_get(I_POST_DOMAIN))>0) strcpy(udom,ini_get(I_POST_DOMAIN));
	if (strcmp(udom,"")==0) strcpy(udom,ini_get(I_NEWSHOST));
	ncpy(utage,utag,BFSZ-1);
	lib_encode(utage);
	imsg("utag got = (%.80s) \n",utag);
	
}
int ispressed(char *s)
{
	char bf[BFSZ];
	if (strlen(query_find(s))>0) {
		imsg("=========PRESSED BUTTON (%s)\n",s);       
		return TRUE;
	}
	lprintf(bf,BFSZ-1,"%s.x",s);
	if (strlen(query_find(bf))>0) {
		imsg("=========PRESSED BUTTON.x (%s)\n",bf);    
		return TRUE;
	}
	
	return FALSE;
}
/*
sxprintf(bf,"group rec.humor\015\012"); nntp_write_s(c,bf,strlen(bf)); 
sxprintf(bf,"article\015\012"); nntp_write_s(c,bf,strlen(bf)); 
sxprintf(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;
*/
int nntp_read_timeout(int c, char *bf, int sz)
{
	int i;
	int r;
	r = nntp_chanwait(c,60);
	if (r) {
		r = nntp_read(c,bf,sz);       
		total_read += r;
		if (ini_true(I_LOGUSE)) {
			if (total_read>1000000) {
				log_use(main_type,total_read);
				total_read = 0;
			}
		}
		return r;
	}
	skip_content = FALSE;
	do_content_force();
	emsg("Channel timedout while trying to read from news server, try later\n");
	exit(0);
	return -1;      
}

static String *instr;
int read_lines(void)
{
	int n;
	char bf[3000];
	char *s;
wantmore:
	n = nntp_read_timeout(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) %d",group,strlen(group));
		printf(" via this DNEWSWEB gateway (%s) %d\n",bf,strlen(bf));
		return FALSE;
	}
	return TRUE;
}
int group_allow_silent(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)) {
		return FALSE;
	}
	return TRUE;
}

int read_pop(int chan)
{
	int n;
	char bf[3000];
	char *s;
	strcpy(nntp_status,"(no data)");
	n = nntp_read_timeout(chan,bf,200);
	if (n<0) return FALSE;
	bf[n] = 0;
	s = strchr(bf,'\n'); if (s!=NULL) *s = 0;
	s = strchr(bf,'\r'); if (s!=NULL) *s = 0;
	imsg("read_pop (%s) \n",bf);
	ncpy(nntp_status,bf,BFSZ-1);
	if (nntp_status[0]=='+') return TRUE;
	return FALSE;
}
int read_status(void)
{
	char *s,*s2;
	int slen;
	if (instr==NULL) instr = str_new();
	get_line_clear();
	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_linex(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;
}
static int gupto;
void get_line_clear(void)
{
	gupto = 0;
	if (instr==NULL) return;
	str_copysn(instr,"");
}
static int nowrap;
char *get_line(String *instr);
char *get_line_nowrap(String *instr)
{
	char *s;
	nowrap = TRUE;
	s = get_line(instr);
	nowrap = FALSE;
	return s;
}
static int unwraphead = FALSE;
char *get_line_head(String *instr)
{
	char *s;
	unwraphead = TRUE;
	s = get_line(instr);
	unwraphead = FALSE;
	return s;
}

char *get_line(String *instr)
{
	static char bf[BFSZ];
	char *s;
	int noskip = FALSE;
	char was;
	char *s2;
	int slen;
	if (instr==NULL) return NULL;
	s = str_s(instr);
	if (s==NULL) goto fail;
	s+=gupto;
	if (*s=='\r') {s++; gupto++;}
	if (*s=='\n') {s++; gupto++;}
again:
	for (s2=s;*s2!=0;s2++) if ((*s2=='\n') || (*s2=='\r')) break;
	if (*s2==0) goto fail;
	if (unwraphead) {
		if ((*s!='\n') && (*s!='\r')) 
			if ( (s2[2]==' ') || (s2[2]=='\t') ) {
				s2[0] = ' ';
				s2[1] = ' ';
				goto again;
			}
	}
	
	if (((s2-s)>500) && (!nowrap)) {
		/* Long line, wrap it on the last space if possible*/
		s2 = strchr(s+500,' ');
		if (s2==NULL) {s2 = s+500; noskip = TRUE;}
		if (s2>(s+599)) {s2 = s+500; noskip = TRUE;}
		was = *s2;
		*s2 = 0;
		ncpy(bf,s,BFSZ-1);
		*s2 = was;
		gupto += s2-s+1;
		if (noskip) gupto--;
	} else {
		*s2 = 0;
		ncpy(bf,s,BFSZ-1);
		gupto += s2-s+1;
	}
	/*strcat(bf,"\r");*/
	if (gupto==str_len(instr)) str_copysn(instr,"");
	if(*bf=='.') return bf+1;
	return bf;
fail:
	str_copysn(instr,"");
	return NULL;
}
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)>2) str_setlen(instr,str_len(instr)-3);
	return (str_len(instr)==0);
}
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) { 
		lprintf(bf,BFSZ-1,"%s?cmd=r_search&key=%s&from=%d&to=%d&utag=%s",path_script,lib_encode(key),from-page_size(),from,utage);
		var_set("b_prev",bf);
		var_set("to_up",int_str(from));
		var_set("from_up",int_str(from-page_size()));
	}
	if (to<total) { 
		lprintf(bf,BFSZ-1,"%s?cmd=r_search&key=%s&from=%d&to=%d&utag=%s",path_script,lib_encode(key),to,to+page_size(),utage);
		var_set("b_next",bf);
		var_set("to_down",int_str(to+page_size()));
		var_set("from_down",int_str(to));
		
	} 
	lprintf(bf,200,"%d",from);
	var_set("from",bf);
	lprintf(bf,200,"%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-page_size(),from,from-page_size(),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+page_size(),to,to+page_size());
	} 
	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 reverse_xover(void) {
	char *s;
	String *temp;
	
	temp=str_new();
	if (temp==NULL) return;
	s = get_line_nowrap(instr);
	for (;s!=NULL;) {
		str_insertsn(temp,"\n");	
		str_insertsn(temp,s);
		s = get_line_nowrap(instr);
	}
	str_clear(instr);
	str_copy(instr,temp);
	gupto=0;
	str_free(temp);
}
void show_xover(void)
{ 
	char *s;
	s = get_line_nowrap(instr);
	for (;s!=NULL;) {
		dmsg("Show sxline %s\n",s);
		show_xline(s);
		s = get_line_nowrap(instr);
	}
}
void show_search(void)
{ 
	char *s;
	
	s = get_line_nowrap(instr);
	for (;s!=NULL;) {
		show_xline(s); /* decode xover information */
		s = get_line_nowrap(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&utag=%s\">",x_score[i],path_script,towww(bf),s,utage);
		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_nowrap(instr);
	for (;s!=NULL;) {
		show_dline(s);
		s = get_line_nowrap(instr);
	}
}
void netwin_advert(void)
{
	printf("<hr> <FONT SIZE=-2>\n");
	printf("<a href=\"http://netwinsite.com/ref.htm\"> ");
	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];
	char bf2[BFSZ];
	ncpy(bf,line,BFSZ-1);
	strlwr(bf);
	if (strncmp(bf,field,strlen(field))==0) {
		ncpy(bf2,line+strlen(field)+1,BFSZ-80);
		ncpy(val,head_decode(bf2),BFSZ-80);
	}
	return TRUE;
}
#define MAXIMG 500000
void show_article_part(int wanted);

void do_content_part(char *s)
{
	imsg("do content part (%s)\n",s);
	if (strstr(s,".gif")!=NULL)  printf("Content-type: image/GIF\n");
	else if (strstr(s,".zip")!=NULL)  printf("Content-type: application/x-zip-compressed\n");
	else if (strstr(s,".exe")!=NULL)  printf("Content-type: application/octet-stream\n");
	else if (strstr(s,".doc")!=NULL)  printf("Content-type: application/octet-stream\n");
	else if (strstr(s,".xls")!=NULL)  printf("Content-type: application/octet-stream\n");
	else if (strstr(s,".com")!=NULL)  printf("Content-type: application/octet-stream\n");
	else if (strstr(s,".txt")!=NULL)  printf("Content-type: text/plain\n");
	else if (strstr(s,".ini")!=NULL)  printf("Content-type: text/plain\n");
	else if (strstr(s,".jpg")!=NULL)  printf("Content-type: image/JPEG\n");
	/*else if (strstr(s,".avi")!=NULL)  printf("Content-type: audio/avi\n");
	else if (strstr(s,".mpg")!=NULL)  printf("Content-type: video/mpeg\n");
	else if (strstr(s,".mpeg")!=NULL)  printf("Content-type: video/mpeg\n");
	*/
	else if (strstr(s,".htm")!=NULL)  printf("Content-type: text/html\n");
	else if (strstr(s,".html")!=NULL)  printf("Content-type: text/html\n");
	else if (strstr(s,".jpeg")!=NULL)  printf("Content-type: image/JPEG\n");
	else {
		printf("Content-type: application/octet-stream\n");    
		/* printf("Content-type: image/JPEG\n");*/
	}
}

void show_article_part(int wanted)
{ 
	int uucount,ycount;
	char *s;
	char *s2;
	int gothead;       
	int n;
	char subject[BFSZ];
	char fname[BFSZ];
	int isuu = FALSE;
	int isyenc = FALSE;
	int encfound = FALSE;
	char from[BFSZ];
	char tmp[BFSZ];
	char date[BFSZ];
	char boundary[BFSZ];
	char content[BFSZ];
	char newsgroups[BFSZ];
	char authenticated_user[BFSZ];
	char *out;
	int nout;
	int findimage = FALSE;
	int item = atoi(query_find("item"));
	int related = item;
	int got=0;
	int r;
	int ismsg=FALSE;
	int isvcard = FALSE;
	if (b_item>=0) item = b_item;
	if (wanted==200) {findimage = TRUE; gothead = TRUE;}
	if (wanted==99) wanted = 0;
	
	if (str_len(instr)>MAXIMG) out = mymalloc(str_len(instr));
	else 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;
	*/
	
	
	uucount=0; ycount=0;
	if (!gothead) s = get_line_head(instr);                    
	else 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;
		if (wanted==0) head_get("content-type:",content,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;
			head_get("content-type:",content,s);
		}
		/* head_get("subject:",subject,s); */
		if (!gothead) s = get_line_head(instr);
		else s = get_line(instr);
	}
	nout = 0;
	if (findimage) if (strlen(content)>0) wanted = 0;
	/*
	Content-Type: image/jpeg;
	name="BusScene.jpg"
	Content-Transfer-Encoding: base64
	Content-Disposition: attachment;
	filename="BusScene.jpg"	
	*/
	
	if (wanted==0) {
		if (strlen(content)==0) strcpy(content,"image/jpeg; name=\"test.jpg\"");
		if (!done_content) {
			imsg("Content-type: %s\n",content);
			printf("Content-type: %s\n",content);
			done_content = TRUE;
		}
		gothead = TRUE;
	}
	for (;s!=NULL;) {
		s2 = strchr(s,'\015') ;
		if (s2!=NULL) *s2 = 0;
		s2 = strchr(s,'\012') ;
		if (s2!=NULL) *s2 = 0;
		if(!encfound) {
			if (!isuu) {
				if (strncmp(s,"begin",5)==0) uucount++;
				/* brian - checks uucount!= 0 to ensure there is uuencoding used */
				/* also if wanted (mime part) == 100 then there is no mime so
				ignore the check to ensure we are in the right mime part */
				/* brian - why is got set to wanted + 1 below ?????? */
				if (((wanted==100)||(got==wanted)||(got==wanted+1))
					&&(uucount!=0)&&(uucount==atoi(query_find("uucount"))))
				{ 
					isuu = TRUE;
					strlwr(s);
					do_content_part(s); /* RL */
					done_content = TRUE;
				}
			}
			
			if (!isyenc) {
				if (strncmp(s,"=ybegin",7)==0) {
					ycount++;
					isyenc = TRUE;
					strlwr(s);
					s2=strstr(s," name="); if(!s2) break;
					ncpy(fname,s2+6,BFSZ-1);

					do_content_part(fname); /* RL */
					done_content = TRUE;

				}
			}
			encfound=isuu||isyenc;
		} else { 
			if (isuu) {
				if (strncmp(s,"end",3)==0) break;
				if (strcmp(s,"`")==0) break;
				if (s[0]!=0) {
					/*imsg("************* decode_uu(s='%s',out'%d'+nout'%d'='%d' '%s') about to die?\n",s,out,nout,out+nout,out+nout); */
					n = decode_uu(s,out+nout);
					nout += n;
				}
			} else if(isyenc) {
				if (strncmp(s,"=yend",5)==0) break;
				if (strncmp(s,"=ypart ",7)!=0) {
					n = ydecode(s,strlen(s),out+nout);
					nout += n;
				}
			}
		}
		
		if (!isuu) {
			if (!gothead) {
				boundary_add(s);
				if (strstr(s,"message/rfc822")!=NULL) ismsg = TRUE;
				if (strlen(s)==0) {
					if (!ismsg) gothead = TRUE;
					ismsg = FALSE;
				}
			}
			if (findimage) if (!gothead) {
				ncpy(tmp,s,BFSZ-1);
				strlwr(tmp);
				if ( (strstr(tmp,"jpeg")!=NULL) ||
					(strstr(tmp,"jpg")!=NULL) ||
					(strstr(tmp,"image")!=NULL) ||
					(strstr(tmp,"gif")!=NULL) )
				{
					wanted = got;
					goto showit;
				}
				if (strlen(s)==0) gothead = TRUE;
			}
			if ((strstr(s,boundary)!=NULL) || (boundary_check(s))) {
				gothead = FALSE; got++;
				uucount = 0; 
			} else if (got==wanted) {
				
showit:         
			if (strlen(s)==0) gothead = TRUE;
			else {
				if (!gothead) {
				/*                              {char *zz;
				zz = strchr(s,';');
				if (zz!=NULL) *zz = 0;
				}
					*/
					imsg("Attach Head(%s)\n",s);
					
					ncpy(tmp,s,BFSZ-1);
					strlwr(tmp);
					if (strstr(tmp,"message/rfc822")!=NULL) ismsg = TRUE;
					if (strstr(tmp,"text/x-vcard")!=NULL) isvcard = TRUE;
					if (strstr(tmp,"x-uue")!=NULL) got = wanted+1;
					printf("%s\n",s);
				} else {
					if (isvcard) {
						strcat(out+nout,s);
						nout += strlen(s);
						strcat(out+nout,"\n");
						nout += 1;
					} 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; 
		}
		if (!gothead) s = get_line_head(instr);
		else s = get_line(instr);
        }
        printf("Content-length: %d\n",nout); 
		printf("\n");
		r = fwrite(out,1,nout,stdout);
		/*	imsg("Wrote %d/%d bytes with fwrite %s\n",r,nout,strerror(errno));*/
		if (fflush(stdout)!=0) {
			imsg("Flush failed %s\n",strerror(errno));
		}
		return;
}
void do_content_force(void)
{
	skip_content = FALSE;
	do_content();
}
void do_content(void)
{
	if (skip_content) return;
	if (done_content) return;
	printf("Content-type: %s\n\n",content_type());
	imsg("do_content called and shown %s\n", content_type());
	done_content = TRUE;
}
int lib_msec(void)
{
	return 1;
}
static char msgid[BFSZ];
static  char subject[BFSZ];
static  char from[BFSZ];
static  char h_ref[BFSZ];
/* static char array to hold name of authenticated user who posted message*/
static char authenticated_user[BFSZ];
static char contenttype[BFSZ];
static char mimeversion[BFSZ];
static char mimeboundary[BFSZ];
void show_article1(void)
{ 
	char *boundary;
	char *boundary2;
	char *s;
	char date[BFSZ];
	char bf[4000];
	char newsgroups[BFSZ];
	
	strcpy(subject,"");
	strcpy(from,"");
	strcpy(date,"");
	strcpy(msgid,"");
	strcpy(newsgroups,"");
	strcpy(contenttype,"");
	strcpy(mimeversion,"");
	strcpy(mimeboundary,"");
	
	s = get_line(instr);
	for (;s!=NULL;) {
		if (strcmp(s,"")==0) break; /*bazil*/
		head_get("content-type:",contenttype,s);
		head_get("mime-version:",mimeversion,s);
		/* check for mime boundary and remember it
		so we can remove it when quoting the message */
		boundary = strstr(s,"boundary=\"");
		if (boundary!=NULL) {
			boundary+=10;
			boundary2=strchr(boundary,'\"');
			if (boundary2!=NULL) {
				if ((boundary2-boundary)<500) ncpy(mimeboundary,boundary,boundary2-boundary);
				else ncpy(mimeboundary,boundary,500);
			};
		};
		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);
		
		/* copy newsgroups to global static as will be needed in f_cancel */
		ncpy(global_newsgroups, newsgroups,BFSZ-10);
		ncpy(global_subject, subject,BFSZ-10);
		
		/* get authenticated username of original poster from header*/
		head_get("x-authenticated-user:",authenticated_user,s);
		
		s = get_line(instr);
	}
	imsg("from is (%s)\n",from);
	ncpy(global_entire_from, from, BFSZ-10);	/* copy from to global for f_cancel*/
	
	if (strlen(subject)==0) strcpy(subject,"(No subject)");
	var_set("h_subject",value_encode(subject));
	var_set("h_date",date);
	var_set("h_date_numeric",h_date_numeric(date)); /*brian123*/
	var_set("h_message-id",value_encode(msgid));
	var_set("h_from",value_encode(from));
	var_set("h_from_trim",value_encode(h_from_trim(from)));
	var_set("h_from_hide",value_encode(h_from_hide(from)));
	var_set("h_from_email",value_encode(h_from_email(from)));
	
	lprintf(bf,BFSZ-1,"%s %s",h_ref,msgid);
	var_set("h_references",eoltospace(value_encode(bf)));
	var_set("h_message-id",msgid);
	var_set("h_newsgroups",newsgroups);
	ncpy(bf,newsgroups,60);
	strcpy(bf+58,"...");
	var_set("h_newsgroups_trim",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>"); 
}

/* f_cancel cancels an article if originally posted by the current user.

  At this stage, the function does the following:
  
	checks user authentication
	connects to server and retrieves article
*/
void f_cancel(void)
{
	char bf[BFSZ];
	char bf2[BFSZ];
	
	char response[BFSZ];	
	
	char *p;
    int item;
	
	
	imsg("f_cancel: called by %.200s\n", guser);
	
	
    if (!islogin()) {
		imsg("Sorry user isn't logged in\n");
        lprintf(bf,200,"Sorry, you must login before posts can be cancelled");
        var_set("login_error",bf);
        set_id();
        tpl_show("login.tpl");
        return;
    }
	
	
	item = atoi(query_find("item"));
	if (item==0) item = atoi(query_find("xrelated"));
	ncpy(group,query_find("group"),BFSZ-1);
	
	
	if (!group_allow(group)) {
		zmsg("Cannot read that group"); 
		return;
	}
	
	
	if (!open_status()) {
		zmsg("News host not happy %s",tohtml(nntp_status)); 
		return;
	}
	
	
	if (!do_authent()) {
		zmsg("News host authentication failed: %s",tohtml(nntp_status)); 
		return;
	}
	
	
	if (ini_true(I_POST_LOGIN)) {
		if (group_readonly(group)) { /* brian456*/
			imsg("read only access to newsgroup %s showing login.tpl\n",group);
			tpl_show("login.tpl");
			return;  /* **** exit cancel function **** */
		};
	}
	
	
	lprintf(bf,BFSZ-1,"group %s\015\012",group);
	nntp_write_s(chan,bf,strlen(bf)); 
	
	
	if (!read_status()) {
		zmsg("News host group: %s",tohtml(nntp_status)); 
		return;		/* **** exit cancel function **** */
	}
	
	
	lprintf(bf,BFSZ-1,"article %d\015\012",item);
	nntp_write_s(chan,bf,strlen(bf)); 
	
    
	if (!read_status()) {
		zmsg("News host article failed: %s",tohtml(nntp_status)); 
		return;		/* **** exit cancel function **** */
	}
	
	
	read_xover();
	
	
	
	show_article1();
	
	
	
	if (strcmp(guser, authenticated_user)) {
		
		zmsg("User %s cannot delete article posted by another user.\n", guser);
		return;		/* **** exit cancel function **** */
	}
	
	/* If execution gets this far, then assume it is safe to cancel the post.
	
	  Format of a typical cancel message as sent to NNTP server:
	  
		From: honman@unitek.uucp (Man Wong)
		Sender: honman@unitek.uucp (Man Wong)
		Reply-To: honman@unitek.UUCP (Hon-Man Wong)
		Newsgroups: net.unix-wizards
		Subject: cmsg cancel <messagid>
		Control: cancel <messageid>
		
		  Cancelling message <> by dnewsewb.
		  .
		  
	*/
	
	
	
	tcp_printf(chan,"post");
	
	
	
	nntp_read(chan, response, BFSZ);
	
	tcp_printf(chan,"From: %s", global_entire_from);
	tcp_printf(chan,"Sender: %s", global_entire_from);
	tcp_printf(chan,"Reply-To: %s", global_entire_from);
	tcp_printf(chan,"Newsgroups: %s", global_newsgroups);
	
	tcp_printf(chan,"Subject: cmsg cancel %s", msgid);
	tcp_printf(chan,"Control: cancel %s", msgid);
	
	
	tcp_printf(chan,"");
	tcp_printf(chan,"Cancelling message %s by dnewsweb",msgid);
	tcp_printf(chan,".");
	
	nntp_read(chan, response, BFSZ);
	
	lprintf(bf,BFSZ-1,"%d",item);
	var_set_log("item",bf,"one");
	var_set("messageid", msgid);
	var_set("from", global_entire_from);
	var_set("group", value_encode(global_newsgroups));
	var_set("groupe",towww(global_newsgroups));
	var_set("subject", global_subject);
	var_set("response", response);
	if (strncmp(response,"240",3)==0) var_set("cancelled","yes");
	tpl_show("cancel.tpl");
    tcp_printf(chan,"quit"); 
    read_status(); /* Wait for the channel to close */
}

void f_config(void)
{
	char bf[BFSZ];
	tpl_init();
	
	if (!islogin()) {
		zmsg("Sorry, you are not logged in\n");
		return;
	}
	
	user_read(utag);
	var_set("conf_name",user_get("name"));
	var_set("conf_reply",user_get("reply"));
	ncpy(bf,user_get("sort"),200); strlwr(bf);
	if (strcmp(bf,"subject")==0) var_set("conf_sort_subject","selected");
	if (strcmp(bf,"thread")==0) var_set("conf_sort_thread","selected");
	if (strcmp(bf,"date")==0) var_set("conf_sort_date","selected");
	var_set("conf_sort",user_get("sort"));
	var_set("conf_sig",cr_decode(user_get("sig")));
	var_set("conf_window",user_get("window"));
	tpl_show("config.tpl");
}

void r_config(void)
{
	int x;
	char bf[BFSZ];
	user_read(utag);
	if (!islogin()) {
		zmsg("Sorry, you are not logged in\n");
		return;
	}
	user_set("name",form_find("conf_name"));
	user_set("reply",form_find("conf_reply"));
	user_set("sort",form_find("conf_sort"));
	user_set("sig",cr_encode(form_find("conf_sig")));
	x=atoi(form_find("conf_window"));
	if (x<=0) x=atoi(ini_get(I_WINDOW_DAYS));
	sprintf(bf,"%d",x);
	user_set("window",bf);
	
	user_write(utag);
	r_user();
}

void r_login(int nooutput)
{
	char user[BFSZ],pass[BFSZ],pop[BFSZ];
	char etxt[BFSZ];
	char bf[BFSZ];
	char ue[BFSZ];
	char *s;
	int id = atoi(form_find("loginid"));
	imsg("r_login(%s)\n",thisuser);
	
	if (!open_status()) return ;
	
	ncpy(user,either_find("user"),BFSZ-1);
	ncpy(pass,either_find("pass"),BFSZ-1);
	ncpy(pop,either_find("pop"),BFSZ-1);
	if (strlen(pass)==0) if (strlen(thisuser)>0) {
		imsg("web login (%s) \n",thisuser);
		ncpy(user,thisuser,200);
		goto isok;
	}
	if (!pop_check(user,pass,pop,etxt)) {
		do_content();
		var_set("login_error",etxt);
		set_id();
		tpl_show("login.tpl");
		return;         
	}
isok:
	set_utag(user,pop); /* this now calls user_read as it needs the new tag to read it ! */
	ncpy(ue,utag,100);
	lib_encode(ue);
	if (!skip_content) if (!done_content) {
		printf("Set-Cookie: dnews_utoken=%s; expires=%s\n",ue,cookie_date_plus(60*60*12));
		imsg("Set-Cookie: dnews_utoken=%s; expires=%s\n",ue,cookie_date_plus(60*60*12));
		done_content = TRUE;
		printf("Content-type: %s\n\n",content_type());
	}
	/*	user_read(utag);*/
	
	imsg("login id's (%s) (%d)\n",user_get("loginid"),id);
	if (!web_login) if (!ini_true(I_IGNORE_ID)) if (atoi(user_get("loginid"))==id) {
		if (!islogin()) {
			lprintf(bf,BFSZ-1,"Sorry, login form already used");
			var_set("login_error",bf);
			set_id();
			tpl_show("login.tpl");
			return;
		}
	}
	imsg("User logged in ok (%s)\n",user);
	/* Record that user is logged in, and store */
	if (ini_true(I_USER_CLEAR)) {
		imsg("Clearing existing user info\n");
		xuser_clear();
	}
	srand( (unsigned)time( NULL ) );
	lprintf(bf,30,"%d",rand());
	user_set("cookie",bf);
	lprintf(bf,BFSZ-1,"%d",id);
	user_set("loginid",bf);
	user_set("ukey",ukey);
	user_set("remote_addr",mygetenv("REMOTE_ADDR"));
	lprintf(bf,BFSZ-1,"%d",lib_gmtime());
	user_set("login_time",bf);
	
	if (ini_true(I_AUTH_NNTP)) {
		user_set("nntp_pass",pass);
	}
	imsg("user write\n");
	
	
	
	if (ngroups==0) {
		s = strtok(ini_get(I_AUTO_SUBSCRIBE),", ");
		for (;s!=NULL;) {
			group_add(s);
			s = strtok(NULL," ,");
		}
	}
	imsg("r_login calling user_write\n");
	user_write(utag);
	imsg("r_user()\n");
	if (!nooutput) {
		if (strlen(form_find("group"))>0) {
			cmd_latest();
		} else {
			r_user();
		}
	}
}

void r_unsub(void)
{
	user_read(utag);
	group_del(query_find("unsub"));
	user_write(utag);
	r_user();
}
void r_subscribe(void)
{
	user_read(utag);
	group_add(query_find("group"));
	user_write(utag);
	r_user();
}
void r_grpsearch(void)
{
	tpl_init();
	if (!open_status()) {zmsg("News host not responding1: %s",tohtml(nntp_status)); return;}
	if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
	
	/* Show user.tpl , fetch list of groups, and items for each group */
	var_set("wild",query_find("wild"));
	if (strlen(query_find("wild"))<3) {zmsg("Please give at least 3 characters to search for"); return;}
	if (ini_true(I_USE_XACTIVE))
		tcp_printf(chan,"LIST XACTIVE *%s*",query_find("wild"));
	else
		tcp_printf(chan,"LIST ACTIVE *%s*",query_find("wild"));
	if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
	if (read_xover()) var_set("no_match","bazil");
	tpl_show("grpsrch.tpl");
}
int open_status(void)
{
	static int done;
	if (done) return TRUE;
	done = TRUE;
	return read_status();
}
void r_user(void)
{
	tpl_init();
	user_read(utag);
	if (!open_status()) {zmsg("nntp open failed"); return;}
	if (!islogin()) {
		imsg("user not logged, showing top form instead\n");
		r_list();
		/*              do_login("Sorry, you are not logged in");*/
		return;
	}
	
	
	/* Show user.tpl , fetch list of groups, and items for each group */
	if (!open_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
	if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
	
	group_fetch();
	
	
	tpl_show("user.tpl");
}
typedef struct GINFO {
	char *name;
	int lasttime; int upto; int lastupto; 
	int curupto; int total;
} Ginfo;
#define MAX_SUB 1000
static Ginfo groups[MAX_SUB];
static char tpl_groups[15000];
static char *uinfo[100];
static char *uinfo_val[100];
static int nuinfo;
void group_add(char *s)
{
	int i;
	if (ngroups>=MAX_SUB-100) return;
	imsg("Add group, currently %d\n",ngroups);
	for (i=0; i<ngroups; i++) {
		if (strcmp(groups[i].name,s)==0) return;
	}
	groups[ngroups++].name = strdup(s);
}
void group_del(char *s)
{
	int i;
	imsg("Remove group (%s), currnetly %d\n",s,ngroups);
	for (i=0; i<ngroups; i++) {
		if (strcmp(groups[i].name,s)==0) {
			for (;i<(ngroups-1); i++) {
				groups[i] = groups[i+1];
			}
			ngroups--;                      
		}
	}
	imsg("Could not find that group(%s)\n",s);
}

void tpl_show_gsearch(void)
{
	char bf[BFSZ];
	char *s;
	int xend;
	int i;
	int n,from;
	int np;
	char *p[30];
	
	s = get_line(instr);
	/*test.new 3 3 y nitems */
	for (;s!=NULL;) {
		np = token_split(s,p," ",20);
		var_set("name",p[0]);
		if (!group_allow_silent(p[0])) goto nextone;
		var_set("name_ref",towww(p[0]));
		if (ini_true(I_USE_XACTIVE)) {
			if (np>4) var_set("total",p[4]);
		} else {
			if (np<3) goto nextone;
			xend = atoi(p[1]);
			xend -= atoi(p[2]) - 1;
			var_set("total",int_str(xend));
		}
		ncpy(bf,tpl_groups,BFSZ-1);
		s = tpl_replace_vars(bf);
		printf("%s\n",s);
nextone:        s = get_line(instr);
	}
}

void tpl_show_groups(void)
{
	char bf[100000];
	char *s;
	int i;
	int n,from;
	imsg("tpl_show_groups, %d groups\n",ngroups);
	for (i=0; i<ngroups; i++) {
		imsg("show_groups %d/%d %s\n",i,ngroups,groups[i].name);
		var_set("name",groups[i].name);
		var_set("name_ref",towww(groups[i].name));
		n = groups[i].curupto - groups[i].lastupto;
		imsg("New items %d, %d - %d\n",n,groups[i].curupto,groups[i].lastupto);
		if (n>groups[i].total) n = groups[i].total;
		if (n<0) n = 0;
		lprintf(bf,100000-1,"%d",n);
		var_set("new",bf);
		if (n==0) {
			var_set("isnew","false");
		} else {
			var_set("isnew","true");
		}
		from = groups[i].lastupto;
		if (from==0) from = groups[i].curupto-20;
		lprintf(bf,100000-1,"%d",from);
		var_set("from",bf);
		lprintf(bf,100000-1,"%d",groups[i].total);
		var_set("total",bf);
		ncpy(bf,tpl_groups,BFSZ-1);
		s = tpl_replace_vars(bf);
		printf("%s\n",s);
	}
	imsg("show_groups finished\n");
}

void tpl_show_group_list(void)
{
	char bf[BFSZ];
	char *s, *line;
	int i;
	int n,from;
	
	char *name,*groups;
	int lastupto,curupto,total;
	String *group_instr;
	int group_gupto;
	
	if (!open_status()) {zmsg("Could not read opening message - %s",tohtml(nntp_status)); return;}
	
	groups=ini_get(I_GROUP_LIST);
	imsg("Showing groups %s\n",groups);
	if (groups[0]=='\0') 
		tcp_printf(chan,"LIST");
	else
		tcp_printf(chan,"LIST ACTIVE %s",groups);
	if (!read_status()) {zmsg("Could not list groups - %s",tohtml(nntp_status)); return;}
	read_xover();
	group_instr=instr;
	instr = str_new();
	
	while((line=get_line_nowrap(group_instr))!=NULL && (line[0]!='.' || line[1]!='\r')) {
		imsg("Processing [%s]\n",line);
		name=strtok(line," \n\t");
		lastupto=atoi(strtok(NULL," \n\t"));
		curupto=atoi(strtok(NULL," \n\t"));
		
		group_gupto=gupto; /* backup gupto since get_line uses the global variable*/
		tcp_printf(chan,"group %s",name);
		/*imsg("sent group %s\n",name);*/
		if (!read_status()) {zmsg("Could not read group %s - %s",name, nntp_status); return;}
		/*imsg("status=%s\n",nntp_status);*/
		strtok(nntp_status," \n\t");
		total=atoi(strtok(NULL," \n\t"));
		gupto=group_gupto; /* restore gupto*/
		
		/*imsg("Name %s last %d cur %d\n",name,lastupto,curupto);*/
		
		var_set("name",name);
		var_set("name_ref",towww(name));
		n = curupto - lastupto;
		imsg("New items %d, %d - %d\n",n,curupto,lastupto);
		if (n>total) n = total;
		if (n<0) n = 0;
		lprintf(bf,BFSZ-1,"%d",n);
		var_set("new",bf);
		if (n==0) {
			var_set("isnew","false");
		} else {
			var_set("isnew","true");
		}
		from = lastupto;
		if (from==0) from = curupto-20;
		lprintf(bf,BFSZ-1,"%d",from);
		var_set("from",bf);
		lprintf(bf,BFSZ-1,"%d",total);
		var_set("total",bf);
		ncpy(bf,tpl_groups,BFSZ-1);
		s = tpl_replace_vars(bf);
		printf("%s\n",s);
	}
}

char *user_file(char *xuser)
{
	char user[BFSZ];
	char hash[BFSZ];
	char *s;
	char work[BFSZ];
	static char bf[BFSZ];
	ncpy(user,xuser,400);
	strlwr(user);
	for (s=user;*s!=0;s++) {
		if (xisspace(*s)) *s = '_';
		if (*s=='.') *s = '_';
		if (!isalnum(*s)) *s = '_';
	}
	if (ini_true(I_USE_HOME)) {
		ncpy(work,mygetenv("HOME"),BFSZ-1);
		lprintf(bf,BFSZ-1,"%s/%s.dat",work,user);
		return bf;
	}
	
	ncpy(hash,user,2);
	strcpy(work,ini_get(I_WORKAREA));
	if (strlen(work)==0) strcpy(work,ini_get(I_TEMPLATES));
	
	lprintf(bf,BFSZ-1,"%s/%s/%s.dat",work,hash,user);
	return bf;
}
char *ini_work(char *fname)
{
	char work[BFSZ];
	static char bf[BFSZ];
	strcpy(work,ini_get(I_WORKAREA));
	if (strlen(work)==0) strcpy(work,ini_get(I_TEMPLATES));
	
	lprintf(bf,BFSZ-1,"%s/%s",work,fname);
	return bf;
}
int xuser_clear(void)
{
	nuinfo = 0;
	return TRUE;
}
int user_write(char *xuser)
{
	FILE *f;
	Ginfo *g;
	int i;
	char user[BFSZ];
	char *s;
	
	ncpy(user,xuser,200);
	s = strchr(user,':'); 
	if (s!=NULL) *s = 0;
	
	imsg("user_write(%.80s)\n",user);
	f = fopen(user_file(user),"w");
	if (f==NULL) {
		lib_createdir(user_file(user));
		f = fopen(user_file(user),"w");
	}
	if (f==NULL) {
		do_content_force();
		emsg("Could not create (%s) (%s) \n",user_file(user),strerror(errno));
		return FALSE;
	}
	for (i=0; i<nuinfo; i++) {
		if (i>90) break;
		fprintf(f,"%s %s\n",uinfo[i],uinfo_val[i]);
	}
	for (i=0; i<ngroups; i++) {
		if (i>900) break;
		g = &groups[i];
		if (g->lastupto > g->upto) g->lastupto = g->upto;
		if (g->lasttime == 2) {
			g->lastupto = g->upto;
			g->lasttime = lib_gmtime();
		}
		fprintf(f,"group %s %d %d %d\n",
			g->name,g->lasttime,g->upto,g->lastupto);
	}
	fclose(f);
	return TRUE;
}
int user_read(char *xuser)
{
	FILE *f;
	static int done;
	char bf[BFSZ];
	char *s;
	Ginfo *g;
	char user[BFSZ];
	
	if (!done) imsg("user_read(%s)\n",xuser);
	ncpy(user,xuser,200);
	s = strchr(user,':'); 
	if (s!=NULL) *s = 0;
	
	
	if (strlen(user)==0) return FALSE;
	if (done) return TRUE;
	imsg("user(%s)_read_doing(%s)\n",user,user_file(user));
	done = TRUE;
	f = fopen(user_file(user),"r");
	if (f==NULL) {
		imsg("Could not read user (%s) file (%s) %s\n",user,user_file(user),strerror(errno));
		return FALSE;
	}
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-10,f)==NULL) break;
		s = strtok(bf," "); if (s==NULL) continue;
		uinfo[nuinfo] = strdup(s);
		if (strcmp(uinfo[nuinfo],"group")==0) {
			g = &groups[ngroups];
			s = strtok(NULL," \n"); if (s==NULL) continue;
			g->name = strdup(s);
			imsg("name = %s\n",s);
			s = strtok(NULL," \n"); if (s==NULL) continue;
			g->lasttime = atoi(s);
			imsg("lasttime = %s\n",s);
			s = strtok(NULL," \n"); if (s==NULL) continue;
			g->upto = atoi(s);
			/* imsg("Read upto %d\n",g->upto);*/
			s = strtok(NULL," \n"); if (s==NULL) continue;
			g->lastupto = atoi(s);
			
			if (g->lasttime == 1) {
				g->curupto = g->upto;
				g->lasttime = lib_gmtime();
				
			}
			
			ngroups++;
			if (ngroups+10>MAX_SUB) break;
		} else {
			s = strtok(NULL,"\n");
			if (s==NULL) s = "";
			/*imsg("user_read (%s) (%s)\n",uinfo[nuinfo],s);*/
			uinfo_val[nuinfo++] = strdup(s);
			if (nuinfo>90) break;
		}
	}
	fclose(f);
	
	var_set("conf_name",user_get("name"));
	var_set("conf_reply",user_get("reply"));
	
	return TRUE;
}
void user_set(char *var, char *val)
{
	int i;
	for (i=0; i<nuinfo; i++) {
		if (strcmp(uinfo[i],var)==0) {
			uinfo_val[i] = strdup(val);
			return;
		}
	}
	uinfo[nuinfo] = strdup(var);
	uinfo_val[nuinfo++] = strdup(val);      
}
char *user_get(char *s)
{
	int i;
	for (i=0; i<nuinfo; i++) {
		if (strcmp(uinfo[i],s)==0) return uinfo_val[i];
	}
	return "";
}
void group_fetchone(int g);
void group_fetch(void)
{
	int i;
	char bf[10000];
	char *s;
	for (i=0; i<ngroups; i++) {
		group_fetchone(i);
	}
	user_write(utag); 
	
	strcpy(bf,"");
	s = bf;
	for (i=0; i<ngroups; i++) {
		lprintf(s,BFSZ-1,"<option value=\"%s\">%s</option>\n",groups[i].name,groups[i].name);
		s += strlen(s);
		if ((s-bf)>9000) break;
	}
	var_set("unsub_list",bf);
}
void group_fetchone(int g)
{       /*  211 1 2 2 test.1 selected */                            
	char *s;
	imsg("Fetching group %s\n",groups[g].name);
	tcp_printf(chan,"GROUP %s",groups[g].name);
	if (!read_status()) { zmsg("No such group (%s) %s",groups[g].name,nntp_status); return;	}
	imsg("Status %s\n",nntp_status);
	s = strtok(nntp_status," \t\n"); if (s==NULL) return;
	s = strtok(NULL," \t\n"); if (s==NULL) return;
	groups[g].total = atoi(s);
	s = strtok(NULL," \t\n"); if (s==NULL) return;
	s = strtok(NULL," \t\n"); if (s==NULL) return;
	groups[g].curupto = atoi(s);                    
	groups[g].upto = atoi(s);                       
	/* imsg("age %d\n",lib_gmtime() - groups[g].lasttime);*/
	
}
int tcp_printf(int chan, char * arg_list,...)
{
	va_list arg_ptr;
	char *format;
	char output[5000];
	va_start(arg_ptr, arg_list);
	format = arg_list;
	vsnprintf(output, 4990, format, arg_ptr); output[4990]=0;
	strcat(output,"\r\n");
	nntp_write_s(chan,output,strlen(output));
	return TRUE;
}
int nntp_check(char *user, char *pass, char *etxt)
{
	if (!open_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return FALSE;}
	tcp_printf(chan,"AUTHINFO USER %s\015\012",user);
	if (!read_status()) { 
		ncpy(etxt,nntp_status,100);
		imsg("Login user failed %s\n",etxt);
		return FALSE;
	}
	tcp_printf(chan,"AUTHINFO PASS %s\015\012",pass);
	if (!read_status()) { 
		ncpy(etxt,nntp_status,100);
		imsg("Login pass failed %s\n",etxt);
		return FALSE;
	}
	return TRUE;
}
int pop_check(char *user, char *pass, char *pop, char *etxt)
{
	char xpop[BFSZ];
	int p=-1;
	int r=FALSE;
	imsg("pop_check (%s)\n",user);
	ncpy(xpop,pop,BFSZ-1);
	if (!ini_true(I_POP_AUTHENT)) {
		if (strlen(xpop)>0) strcpy(xpop,"");
	}
	/*      if (isdigit(pop)) { strcpy(etxt,"POP Host must be given by name not ip number");        goto done; }*/
	strcat(xpop,":110");
	if (strlen(ini_get(I_POP_HOST))>0) strcpy(xpop,ini_get(I_POP_HOST));
	imsg("pop_check1 (%s)\n",user);
	if (strlen(user)==0) { strcpy(etxt,"Please enter a user name");         goto done; }
	imsg("pop_check2 (%s)\n",user);
	if (strlen(pass)==0) { strcpy(etxt,"Please enter your password");   goto done; }
	imsg("pop_check3 (%s) (%s)\n",user,xpop);
	if (strlen(xpop)<5) 
		return nntp_check(user,pass,etxt);
	
	imsg("pop_check4 (%s)\n",user);
	if (strchr(xpop,':')==NULL) strcat(xpop,":110");
	imsg("Open %s\n",xpop);
	p = nntp_open(xpop);
	if (p<0) { strcpy(etxt,"Could not connect to pop host, please try again later");        goto done; }
	nntp_setblock(p,TRUE); /* Set to blocking IO */
	imsg("read pop\n");
	if (!read_pop(p)) { lprintf(etxt,BFSZ-1,"Login to POP server failed %s",nntp_status);  goto done; }
	imsg("read pop1 %s\n",nntp_status);
	if (!tcp_printf(p,"USER %s",user)) { strcpy(etxt,"Error writing to POP server, please try later");      goto done; }
	imsg("read pop2 %s\n",nntp_status);
	if (!read_pop(p)) { lprintf(etxt,BFSZ-1,"Login to POP server failed %s",nntp_status);  goto done; }
	if (!tcp_printf(p,"PASS %s",pass)) { strcpy(etxt,"Error writing to POP server, please try later");      goto done; }
	imsg("read pop3 %s\n",nntp_status);
	if (!read_pop(p)) { lprintf(etxt,BFSZ-1,"Login to POP server failed %s",nntp_status);  goto done; }
	r = TRUE;
done:
	if (p>=0) {
		tcp_printf(p,"QUIT");
		nntp_close(p);
	}
	return r;       
}
void set_id(void)
{
	char bf[BFSZ];
	lprintf(bf,BFSZ-1,"%d",lib_gmtime());
	var_set("loginid",bf);
}
void f_login(void)
{
	char bf[BFSZ];
	
	if (web_login) {
		ncpy(thisuser,mygetenv("REMOTE_USER"),BFSZ-1);
		imsg("f_login (%s) calling r_login \n",thisuser);
		r_login(FALSE);
		return;
	}
	
	tpl_init();
	var_set("togroup",either_find("togroup"));
	set_id();
	tpl_show("login.tpl");
}
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 r_showfile(void)
{
	char fname[BFSZ];
	tpl_init();
	ncpy(fname,query_find("file"),100);
	if (strstr(fname,"/")!=NULL) return;
	if (strstr(fname,"\\")!=NULL) return;
	if (strstr(fname,"..")!=NULL) return;
	if (strstr(fname,"~")!=NULL) return;
	if (strstr(fname,".tpl")==NULL) return;
	
	tpl_show(fname);
}
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 cmd_search_up(void)
{
	page_from = atoi(query_find("from_up"));
	page_to = atoi(query_find("to_up"));
	r_search();
}
void cmd_search_down(void)
{
	page_from = atoi(query_find("from_down"));
	page_to = atoi(query_find("to_down"));
	r_search();
}
int form_date(char *s)
{
	int x = atoi(s);
	if (strlen(s)==0) return 0;
	if (x < -9998) return 0;
	return (int) time(NULL)  + (x * 3600*24);
}
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;
	char op[BFSZ];
	int i;
	char *kw[100];
	int nkw;
	int matches[100];
	int xsince,xbefore;
	char gwild[BFSZ];
	char stype[BFSZ];
	int search_match;
	if ((page_from!=0) || (page_to!=0)) {
		from = page_from; to = page_to;
	}
	if (from<0) {to -= from; from = 0;}
	
	ncpy(key,query_find("key"),400);
	imsg("r_search %d-%d (%s)\n",from,to,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 (!open_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
	if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
	
	/* New form fields in dnewsweb/dnews 5.3
	search_groups
	search_since
	search_before
	search_match
	search_sort (date,score,group)
	*/
	var_set("search_groups",query_find("search_groups"));
	var_set("search_since",query_find("search_since"));
	var_set("search_before",query_find("search_before"));
	var_set("search_match",query_find("search_match"));
	var_set("search_sort",query_find("search_sort"));
	
	ncpy(gwild,query_find("search_groups"),BFSZ-10);
	ncpy(stype,query_find("search_sort"),BFSZ-10);
	xsince = form_date(query_find("search_since"));
	xbefore = form_date(query_find("search_before"));
	search_match = FALSE;
	if (strlen(query_find("search_match"))>0) search_match = TRUE;
	
	strcpy(op,"");
	if (strlen(gwild)>0) {sprintf(bf,"groups=%.200s ",gwild); strcat(op,bf);}
	if (strlen(stype)>0) {sprintf(bf,"sort=%.200s ",stype); strcat(op,bf);}
	if (xsince!=0) {sprintf(bf,"since=%d ",xsince); strcat(op,bf);}
	if (xbefore!=0) {sprintf(bf,"before=%d ",xbefore); strcat(op,bf);}
	if (search_match) {strcat(op,"match=and ");}
	lprintf(bf,BFSZ-1,"xdsearch %d-%d %s %s\015\012",from,to,xkey,op); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding: %s",tohtml(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");
	}
	
	if (read_xover()) var_set("no_match","bazil");
	show_xover();
	tpl_init();
	var_set("key",query_find("key"));
	lprintf(bf,BFSZ-1,"%d",total);
	var_set("total_matches",bf);
	strcpy(bf,"");
	for (i=0; i<nkw; i++) {
		if (i!=0) strcat(bf,", ");
		lprintf(bf+strlen(bf),BFSZ-1-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");
	*/
	lprintf(bf,BFSZ-1,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
	read_status(); /* Wait for the channel to close */
} 
int do_authent(void)
{
	char bf[BFSZ];
	char dom[BFSZ];
	char user[BFSZ];
	char pass[BFSZ];
	char fromip[BFSZ];
	char etext[BFSZ];
	ncpy(dom,mygetenv("REMOTE_ADDR"),BFSZ-1);
	ncpy(user,mygetenv("REMOTE_USER"),BFSZ-1);
	if (strlen(utag)>0) {
		if (islogin()) {
			ncpy(user,guser,400);
		}
	}
	if (strlen(thispass)>0) if (web_login) {
		if (strlen(user)>0) if (ini_true(I_WEB_LOGIN_NOCHECK)) return TRUE;
		ncpy(pass,thispass,400);
		if (nntp_check(user,pass,etext)) return TRUE;	
		return FALSE;
	}
	if (ini_true(I_AUTH_NNTP)) {
		ncpy(pass,user_get("nntp_pass"),400);
		if (nntp_check(user,pass,etext)) return TRUE;	
		return FALSE;
	}
	
	strcpy(pass,ini_get(I_TELLNEWS_PASS));
	if (strlen(pass)==0) return TRUE;
	
	strcpy(fromip,ini_get(I_FROM_IP));
	if (strlen(fromip)>0) ncpy(dom,fromip,400);
	
	if (strlen(user)==0) {
		strcpy(user,"(none)");
		if (strlen(ini_get(I_DEFAULT_USER))>0) {
			strcpy(user,ini_get(I_DEFAULT_USER));
		}
	}
	if (strlen(dom)==0) strcpy(dom,"(none)");
	imsg("Authenticating Domain (%s) User (%s) \n",dom,user);
	lprintf(bf,BFSZ-1,"xdauthent %s %s %s\015\012",dom,user,pass); nntp_write_s(chan,bf,strlen(bf)); 
	imsg("xdauthent %s %s %s\n",dom,user,pass); 
	return read_status(); 
}

int do_latest_authent(void) {
	char dom[BFSZ];
	char user[BFSZ];
	char pass[BFSZ];
	char fromip[BFSZ];
	char bf[BFSZ];
	
	if (strlen(auth_username)==0) return FALSE;
	ncpy(user,auth_username,BFSZ-10);
	
	ncpy(pass,ini_get(I_TELLNEWS_PASS),BFSZ-10);
	if (strlen(pass)==0) return FALSE;
	
	ncpy(dom,ini_get(I_NEWSHOST),BFSZ-10);
	ncpy(fromip,ini_get(I_LATEST_IP),BFSZ-10);
	if (strlen(fromip)>0) ncpy(dom,fromip,BFSZ-10);
	
	if (strlen(dom)==0) ncpy(dom,"(none)",BFSZ-10);
	imsg("do_latest_authent: Authenticating Domain (%s) User (%s) \n",dom,user);
	lprintf(bf,BFSZ-1,"xdauthent %s %s %s\015\012",dom,user,pass); nntp_write_s(chan,bf,strlen(bf)); 
	imsg("do_latest_authent: xdauthent %s %s %s\n",dom,user,pass); 
	return read_status(); 
}

int group_readonly(char *group) {
    char bf[BFSZ];
    int ret=FALSE;
	imsg("group_readonly called with (%.200s)\n",group);
    lprintf(bf,BFSZ-1,"post\r\n"); nntp_write_s(chan,bf,strlen(bf)); 
    if (!read_status()) emsg("problem trying assess group postability\n");
    else {
        lprintf(bf,BFSZ-1,"Newsgroups: %s\r\n",group); nntp_write_s(chan,bf,strlen(bf));
        lprintf(bf,BFSZ-1,"From: can.i@post\r\n",group); nntp_write_s(chan,bf,strlen(bf));
        lprintf(bf,BFSZ-1,"Subject: testing postability\r\n.\r\n",group); nntp_write_s(chan,bf,strlen(bf));
        read_status();
        ret=(atoi(nntp_status)==480);
    }
    if (instr!=NULL) {
		str_free(instr); /*brian789*/
		instr=NULL;
    }
    return ret;
}

void f_follow(int follow)
{
	char bf[BFSZ];
	char bf2[BFSZ];
	char *p;
	int item;
	
	
	if (ini_true(I_POST_REQUIRE_LOGIN)) {
		if (!islogin()) {
			if (ini_true(I_POST_LOGIN_POST)) {
				var_set("require_login","true");
			} else {
				imsg("Sorry user isn't logged in\n");
				lprintf(bf,BFSZ-1,"Sorry, you must login before posts can be accepted");
				var_set("login_error",bf);
				set_id();
				tpl_show("login.tpl");
				return;
			}
		}
	}
	
	
	
	if (follow) {
		item = atoi(query_find("item"));
		if (item==0) item = atoi(query_find("xrelated"));
		ncpy(group,query_find("group"),BFSZ-1);
		if (!group_allow(group)) {zmsg("Cannot read that group"); return;}
		if (!open_status()) {zmsg("News host not happy %s",tohtml(nntp_status)); return;}
		if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
		if (ini_true(I_POST_LOGIN)) {
			if (group_readonly(group)) { /* brian456*/
				imsg("read only access to newsgroup %s showing login.tpl\n",group);
				tpl_show("login.tpl");
				return;
			};
		}
		lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
		if (!read_status()) {zmsg("News host group: %s",tohtml(nntp_status)); return;}
		lprintf(bf,BFSZ-1,"article %d\015\012",item); nntp_write_s(chan,bf,strlen(bf)); 
		if (!read_status()) {zmsg("News host article failed: %s",tohtml(nntp_status)); return;}
		read_xover();
	} else if (ini_true(I_POST_LOGIN)) {
		ncpy(group,query_find("group"),BFSZ-1);
		if (!group_allow(group)) {zmsg("Cannot read that group"); return;}
		/* brian456 - why? */
		/*if (!open_status()) {zmsg("News host not happy %s",nntp_status); return;}*/
		/* if (!do_authent()) {zmsg("News host authentication failed: %s",nntp_status); return;}*/
		if (group_readonly(group)) {
			imsg("read only access to newsgroup %s showing login.tpl\n",group);
			tpl_show("login.tpl");
			return;
		};
	}
	
	ncpy(group,query_find("group"),BFSZ-1);
	if (!group_allow(group)) return;
	
	if (follow) {
		show_article1();
	}
	
	if (!follow) strcpy(subject,"");
	if (follow) {
		ncpy(bf,subject,400);
		strlwr(bf);
		if (strstr(bf,"re:")==NULL) {
			ncpy(bf,subject,400);
			lprintf(subject,BFSZ-1,"Re: %s",bf);   
		}
		if (ini_true(I_NON_ASCII)) ncpy(subject,rfc1522s(subject),BFSZ-10);
		var_set("h_subject",value_encode(subject));
		/* get the actual newsgroups header and set crosspost variable true if it contains a comma */
		ncpy(bf2,var_find("h_newsgroups"),400);
		if (strchr(bf2,',')!=NULL) var_set("crosspost","crosspost");
	}
	user_read(utag);
	if (ini_true(I_NON_ASCII))
	{
		var_set("from_name",rfc1522s(user_get("name")));
		var_set("from",rfc1522s(user_get("reply")));
	}
	else {
		var_set("from_name",user_get("name"));
		var_set("from",user_get("reply"));
	}
	if (strlen(user_get("reply"))==0) {
		if (!ini_true(I_FROM_FORCE)) {
			strcpy(bf,real_from(""));
			var_set("from",bf);
		}
	}
	if (ini_true(I_FROM_WEB_LOGIN)) {
		lprintf(bf,BFSZ-1,"%s@%s",mygetenv("REMOTE_USER"),ini_get(I_POST_DOMAIN));
		var_set("from",bf);
		lprintf(bf,BFSZ-1,"%s",mygetenv("REMOTE_USER"));
		var_set("from_name",bf);
	}
	if (follow) tpl_show("follow.tpl");
	else tpl_show("post.tpl");
	if (follow) {
		lprintf(bf,BFSZ-1,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
		read_status(); /* Wait for the channel to close */
	}
}

/*
int thread_count(char *rmsgid) {
String *backup;
int count=0;
char bf[BFSZ];
char *s;
int gupto_backup;

  gupto_backup=gupto;
  
	backup=str_new();
	str_copy(backup,instr);
	str_clear(instr);
	lprintf(bf,BFSZ-1,"xdfind 1- %s\015\012",lib_encode(rmsgid)); nntp_write_s(chan,bf,strlen(bf));
	dmsg("counting related items (%s)\n",bf);
	if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return 1;}
	read_xover();
	s = get_line_nowrap(instr);
	while (s!=NULL) {
	count++;
	s = get_line_nowrap(instr);
	}
	str_clear(instr);
	str_copy(instr,backup);
	str_clear(backup);
	str_free(backup);
	gupto=gupto_backup;
	return count;
	};
	*/
	
	int estimate_thread_counts() {
		int i;
		int j;
		
		for (i=0;i<all_nitem;i++) {
			for (j=0;j<nitem;j++) {
				if (strcmp(all_x_mid[i],x_mid[j])==0) {x_titems[j]++; break;}
				if (strstr(all_x_ref[i],x_mid[j])!=NULL) {x_titems[j]++; break;}
				if (!ini_true(I_THREAD_NO_SUBJECT))
					if (strcmp(all_x_subjectsrt[i],x_subjectsrt[j])==0) {x_titems[j]++; break;}
			}
		}
		return TRUE;
	};
	
	void show_xline(char *s)
	{
        /* item subject from date messageid ref byte lines */
        char item[BFSZ];
        char subject[BFSZ];
        char frotesm[BFSZ];
        char date[BFSZ];
        char msgid[BFSZ];
        char ref[BFSZ];
        char bytes[BFSZ];
        char lines[BFSZ];
        char score[BFSZ];
        char group[BFSZ];
		int i;
		int titems;
		int max;
		static int lastxover=FALSE;
		
        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);
		if (ini_true(I_NON_ASCII)) ncpy(subject,rfc1522s(subject),BFSZ-10);
        s = xstrtok(NULL,"\t\015\012"); if (s==NULL) goto done;
        ncpy(from,s,BFSZ-1);
		if (ini_true(I_NON_ASCII)) ncpy(from,rfc1522s(from),BFSZ-10);
        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);
		/*imsg("xover got %s %s\n",item,date);*/
        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:;
	 titems=0;
	 if (rootonly) xover_add_all(item,subject,from,lines,date,ref,msgid,bytes,titems);
	 if (rootonly) {
		 if (strlen(ref)>0) {
			 /*imsg("skipping last xover line as not a thread root\n");*/
			 return;
		 }
		 /*titems=thread_count(msgid);*/
		 if (nitem>=PAGESIZE) {
			 if (!lastxover) {
				 int fwd=FALSE;
				 if (strcmp(query_find("forward"),"TRUE")==0) fwd = TRUE;
				 if (fwd) gto=x_item[nitem-1];
				 else gfrom=x_item[nitem-1];
				 lastxover=TRUE;
			 }
			 /*imsg("got all the items we require, ignoring this part of the xover data\n");*/
			 return;
		 }
	 }
	 /*imsg("xover_add [%d] %d %s\n",nitem,atoi(item),subject);*/
	 xover_add(item,subject,from,lines,date,ref,msgid,bytes,titems);
	 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, char *bytes, int titems)
	{
        if (strcmp(item,".")==0) return;
        if (nitem>1800) return;
		if ((!ini_true(I_THREAD_NO_SUBJECT))&&rootonly) {
			int i;
			for(i=0;i<nitem;i++) {
				if (strcmp(lib_nore(subject),x_subjectsrt[i])==0) return;
			}
		}
        x_item[nitem] = atoi(item);
        x_subject[nitem] = strdup(head_decode(subject));
        x_subjectsrt[nitem] = strdup(lib_nore(subject));
        x_from[nitem] = strdup(head_decode(from));
        x_date[nitem] = strdup(date);
        x_lines[nitem] = strdup(lines);
        x_bytes[nitem] = strdup(bytes);
        x_ref[nitem] = strdup(ref);
        x_mid[nitem] = strdup(mid);
		x_titems[nitem] = titems;
        nitem++;
	}
	void xover_add_all(char *item, char *subject, char *from, char *lines, char *date, char *ref, char *mid, char *bytes, int titems)
	{
        if (strcmp(item,".")==0) return;
        if (all_nitem>1800) return;
        all_x_item[all_nitem] = atoi(item);
        all_x_subject[all_nitem] = strdup(head_decode(subject));
        all_x_subjectsrt[all_nitem] = strdup(lib_nore(subject));
        all_x_from[all_nitem] = strdup(head_decode(from));
        all_x_date[all_nitem] = strdup(date);
        all_x_lines[all_nitem] = strdup(lines);
        all_x_bytes[all_nitem] = strdup(bytes);
        all_x_ref[all_nitem] = strdup(ref);
        all_x_mid[all_nitem] = strdup(mid);
		all_x_titems[all_nitem] = titems;
        all_nitem++;
	}
	
	int the_cmp(const void *elem1, const void *elem2  )
	{
		int *a = (int *) elem1;
		int *b = (int *) elem2;
		return x_item[*a] - x_item[*b];
	}
	
	
	
	int sort_the_xover(void)
	{
		int *p;
		char *s;
		char **x;
		int *xi;
		int i;
		p = mymalloc(sizeof(int)*nitem+1);
		x = mymalloc(sizeof(char *)*nitem+1);
		xi = mymalloc(sizeof(int)*nitem+1);
		
		
		for (i=0; i<nitem; i++) p[i] = i;
		if (nitem>1) qsort(p,nitem,sizeof(int),the_cmp);
		
		for (i=0; i<nitem; i++) xi[i] = x_item[p[i]];
		for (i=0; i<nitem; i++) x_item[i] = xi[i];
		for (i=0; i<nitem; i++) x[i] = x_subject[p[i]];
		for (i=0; i<nitem; i++) x_subject[i] = x[i];
		for (i=0; i<nitem; i++) x[i] = x_subjectsrt[p[i]];
		for (i=0; i<nitem; i++) x_subjectsrt[i] = x[i];
		for (i=0; i<nitem; i++) x[i] = x_from[p[i]];
		for (i=0; i<nitem; i++) x_from[i] = x[i];
		for (i=0; i<nitem; i++) x[i] = x_date[p[i]];
		for (i=0; i<nitem; i++) x_date[i] = x[i];
		for (i=0; i<nitem; i++) x[i] = x_lines[p[i]];
		for (i=0; i<nitem; i++) x_lines[i] = x[i];
		for (i=0; i<nitem; i++) x[i] = x_bytes[p[i]];
		for (i=0; i<nitem; i++) x_bytes[i] = x[i];
		for (i=0; i<nitem; i++) x[i] = x_ref[p[i]];
		for (i=0; i<nitem; i++) x_ref[i] = x[i];
		for (i=0; i<nitem; i++) x[i] = x_mid[p[i]];
		for (i=0; i<nitem; i++) x_mid[i] = x[i];
		if (rootonly) {
			for (i=0; i<nitem; i++) xi[i] = x_titems[p[i]];
			for (i=0; i<nitem; i++) x_titems[i] = xi[i];
		}
		myfree(xi);
		myfree(x);
		myfree(p);
		return TRUE;
	}
	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[MAX_ITEMS]; 
        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 (!open_status()) {zmsg("News host not responding1: %s",tohtml(nntp_status)); return;}
        if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
        lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
        if (!read_status()) {zmsg("News host not responding2: %s",tohtml(nntp_status)); return;}
        lprintf(bf,BFSZ-1,"xdfind 1- %s\015\012",lib_encode(find)); nntp_write_s(chan,bf,strlen(bf)); 
        if (!read_status()) {zmsg("News host not responding3: %s",tohtml(nntp_status)); return;}
        if (read_xover()) var_set("no_match","bazil");
		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");
			
		*/
        lprintf(bf,BFSZ-1,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
        read_status(); /* Wait for the channel to close */
	}
	static int isdown,goingup;
	void cmd_up(void)
	{
		isdown = FALSE;
		goingup = TRUE;
        page_from = atoi(query_find("from_up"));
        if (page_from!=0) if (page_from<0) page_from=1;
        r_xover();
	}
	void cmd_down(void)
	{
		isdown = TRUE;
        page_from = atoi(query_find("from_down"));
        if (page_from!=0) if (page_from<0) page_from=1;
        r_xover();
	}
	void cmd_latest(void)
	{
		no_up_button = TRUE;
        page_from = -1;
        r_xover();
	}
	static int xrelated;
	static int viewitems=0;
	void cmd_related(void)
	{
		if (strlen(query_find("viewitems"))>0) viewitems=TRUE;
        xrelated = atoi(query_find("xrelated"));
        r_xover();
	}
	int require_login(void)
	{
        if (ini_true(I_REQUIRE_LOGIN) || web_login) {
			if (!islogin()) {
				r_login(TRUE);
				if (!islogin()) {
					do_login("Sorry, you are not logged in");
					return TRUE;
				}
			}
        }
        return FALSE;
	}
	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;
		int group_n;
        char *s;
		int p_gto,p_gfrom;
		int upto;
		int maxto = 0;
        int nloop = 0;
        int fwd = FALSE;
        int related;
		int minfrom;
		int xover_chunk;
		
		ncpy(main_type,"xover",100);
		
		dmsg("called r_xover");
		
        if (require_login()) return;
        if (page_from!=0) from = page_from;
        if (from<0) from = 0;
        related = atoi(query_find("related"));
        if (xrelated>0) related = xrelated;
        if (strcmp(query_find("forward"),"TRUE")==0) fwd = TRUE;
        
        ncpy(group,either_find("group"),BFSZ-1);
        if (!group_allow(group)) return;
		
		if (!ini_true(I_SUBSCRIBE_MANUAL)) {
			if (strlen(utag)>0) {
				user_read(utag);
				group_add(query_find("group"));
				user_write(utag);
			}
		}
		
        if (max==0) max = PAGESIZE;
        if (max>1000) max = 1000;
        gmax = max;
		if (rootonly) {
			xover_chunk=atoi(ini_get(I_XOVER_CHUNK));
			if (xover_chunk==0) xover_chunk=max;
			gmax=xover_chunk;
		}
		
		dmsg("Wait for hello\n");
        if (!open_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
        if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
		
		lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
        if (!read_status()) { 
			imsg("%s",nntp_status);
			if (atoi(nntp_status)==411) {
				zmsg("News host not responding: %s",tohtml(nntp_status)); return;
			}
			lprintf(bf,BFSZ-1,"Authenticaton required to read news group");
			if (strlen(ini_get(I_TELLNEWS_PASS))==0) {
				lprintf(bf,BFSZ-1,"To read protected news groups, define tellnews_pass in dnewsweb.ini to match (work)/tellnews.pass on dnews");               
			}
			var_set("login_error",bf);
			set_id();
			tpl_show("login.tpl");                          
			return;
        }
        imsg("%s",nntp_status);
        if (related>0) {
			imsg("Searching for related items %d\n",related);
			lprintf(bf,BFSZ-1,"xover %d\015\012",related); nntp_write_s(chan,bf,strlen(bf)); 
			if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
			read_xover();
			
			/* find first message-id in the references */
			s = get_line(instr);
			if (s==NULL) {zmsg("News host not responding thread: %s",tohtml(nntp_status)); return;}
			s = xstrtok(s,"\t");
			if (s!=NULL) s = xstrtok(NULL,"\t");	/* skip nntp response code*/
			if (s!=NULL) {				/* get subject*/
				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");	/*/ skip from*/
			if (s!=NULL) s = xstrtok(NULL,"\t");	/*/ skip date*/
			if (s!=NULL) s = xstrtok(NULL,"\t");	/*/ skip message-id*/
			if (s!=NULL) ncpy(rmsgid,s,BFSZ-1);	/*/ get the message-id of the root thread*/
			
			str_clear(instr);
			
			
			lprintf(bf,BFSZ-1,"xhdr references %d\015\012",related); nntp_write_s(chan,bf,strlen(bf)); 
			if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
			read_xover();
			s = get_line(instr);
			if (s==NULL) return; /* No data*/
			ncpy(bf,s,BFSZ-200);
			imsg("xhdr response %.90s\n",bf);
			s = strtok(bf," \r\n");
			if (s!=NULL) s = strtok(NULL," \r\n");	/* skip nntp response code*/
			if (s!=NULL) if (s[0]=='<') ncpy(rmsgid,s,BFSZ-10);
			imsg("Original mid (%s)\n",rmsgid);
			imsg("xhdr full stop %.90s\n",nntp_status);
			str_clear(instr);
			if (ini_true(I_THREAD_NO_SUBJECT)) {
				if ((strlen(var_find("rangeto"))>0)&&(strlen(var_find("rangefrom"))>0))
					lprintf(bf,BFSZ-1,"xdfind %d-%d %s\015\012",atoi(query_find("rangefrom")),atoi(query_find("rangeto")),lib_encode(rmsgid));
				else 
					lprintf(bf,BFSZ-1,"xdfind 1- %s\015\012",lib_encode(rmsgid));
			}
			else {
				if ((strlen(query_find("rangeto"))>0)&&(strlen(query_find("rangefrom"))>0))
					lprintf(bf,BFSZ-1,"xdfind %d-%d %s %s\015\012",atoi(query_find("rangefrom")),atoi(query_find("rangeto")),lib_encode(rsubject),lib_encode(rmsgid));
				else
					lprintf(bf,BFSZ-1,"xdfind 1- %s %s\015\012",lib_encode(rsubject),lib_encode(rmsgid));
			}
			
			nntp_write_s(chan,bf,strlen(bf)); 
			imsg("Searching for related items (%s) \n",bf);
			if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
        } else {
			group_n = atoi(get_word(nntp_status,1));
			group_from = xfrom = atoi(get_word(nntp_status,2));
			var_set("this_from",int_str(group_from));
			imsg("group command, group_from = %d\n",group_from);
			group_to = xto = atoi(get_word(nntp_status,3));
			var_set("this_to",int_str(group_to));
			minfrom = group_to;
			if (strlen(query_find("from"))==0)  if (from==0) {
				if (rootonly) from=xto;
				else from = xto-max;
			}
			if (from<xfrom) from = xfrom;
			if (rootonly) {
				if (fwd) {
					to=from+(xover_chunk-1);
				}
				else {
					to=from;
					from=to-(xover_chunk-1);
				}
				if (from<xfrom) from=xfrom;
				if (to>xto) to=xto;
			}
			else to = from+max;
			gfrom = from; gto = to;
			upto = gto;
			if (rootonly) {
				if (fwd) origfrom=gfrom;
				else origto=gto;
			}
again:
			imsg("xfrom=%d xto =%d max=%d to=%d  %d-%d\n",xfrom,xto,max,to,gfrom,gto);
			imsg("xover %d-%d\n",gfrom,gto); 
			lprintf(bf,BFSZ-1,"xover %d-%d\015\012",gfrom,gto); nntp_write_s(chan,bf,strlen(bf)); 
			if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
        }
		if (!ini_true(I_PAGE_EXACT)) {
			if (maxto<gto) maxto = gto;
			if (minfrom>gfrom) minfrom = gfrom;
			var_set("group_to",int_str(maxto));
			var_set("group_from",int_str(minfrom));
			var_set("group_shown",int_str(maxto-minfrom+1));
		}
        read_xover();
		if (rootonly) {
			if (!fwd) reverse_xover();
		} else {
			if (ini_true(I_PAGE_EXACT)) {
				if (!fwd) reverse_xover();
			}
		}
        show_xover();
        lastfrom = gfrom;
		imsg("nitems got %d, gfrom=%d group_to=%d related=%d max=%d fwd=%d\n",nitem,gfrom,group_to,related,max,fwd);
		if (ini_true(I_PAGE_EXACT)) {
			if (nitem>max) {
				imsg("waset gfrom %d gto %d nitem %d\n",gfrom,gto,nitem);
				nitem = max;
			}
		}
		if (ini_true(I_PAGE_EXACT)) {
			if (maxto<x_item[nitem-1]) maxto = x_item[nitem-1];
			if (maxto<x_item[0]) maxto = x_item[0];
			if (minfrom>x_item[nitem-1]) minfrom = x_item[nitem-1];
			if (minfrom>x_item[0]) minfrom = x_item[0];
			var_set("group_to",int_str(maxto));
			var_set("group_from",int_str(minfrom));
			var_set("group_shown",int_str(maxto-minfrom+1));
		}
		
        if (related==0) if (nitem<max) {
			if (fwd) {
                gfrom = gto;
				if (rootonly) {
					gfrom=gto+1;
					gto+=xover_chunk-1;
				} else gto += max;
				upto = gto;
                nloop++;
				imsg("fwd nitems got %d, gfrom=%d group_to=%d\n");
				
                if (nloop<100) {
					if (gfrom<=group_to) goto again;
				}
			} else {
				if (goingup) {
					p_gfrom = gfrom;
					p_gto = gto;
					gfrom = gto;
					if (rootonly) {
						gfrom=gto+1;
						gto+=xover_chunk-1;
					}
					else gto += max;
					upto = gto;
					nloop++;
					imsg("fwd nitems got %d, gfrom=%d group_to=%d\n");
					if (nloop<100) {
						if (gfrom<=group_to) goto again;
						else {
							no_up_button = TRUE;
							/*					nloop = 200;
							gfrom = p_gfrom;
							gto = p_gto;
							gto = gfrom-1;
							gfrom -= max;
							goto again;*/
						}
					}
				} else {
					gto = gfrom-1;
					if (rootonly) {
						if (gfrom>group_from) gfrom -=xover_chunk-1;
					} else {
						if (gfrom>group_from) gfrom -=max;
					}
					if (gfrom<group_from) gfrom = group_from;
					imsg("back, gfrom %d, lastfrom %d\n",gfrom,lastfrom);
					if (gfrom!=lastfrom) {
						goto again;
					}
				}
			}
        }
		if (rootonly) {
			if (fwd) origto=gto;
			else origfrom=gfrom;
			origfrom=gfrom;
			estimate_thread_counts();
			var_set("rangefrom",int_str(origfrom));
			var_set("rangeto",int_str(origto));
		}
		/*imsg("brian: origfrom=%d, origto=%d\n",origfrom,origto);
		imsg("brian: gfrom=%d, gto=%d\n",gfrom,gto);*/
		
		sort_the_xover();
		gto = upto;
        if (related>0) lprintf(bf,BFSZ-1,"Related items in %s",group);
        else lprintf(bf,BFSZ-1,"Items in %s",group);
		
        var_set("group_n",int_str(group_n));
		gto = maxto;
		gfrom = minfrom;
        tpl_set_buttons();
		if (viewitems) tpl_show("thread.tpl");
        else if (rootonly) tpl_show("threads.tpl");
		else tpl_show("items.tpl");
        lprintf(bf,BFSZ-1,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
        read_status(); /* Wait for the channel to close */
}

void r_xthread(void) {
	rootonly=TRUE;
	r_xover();
}

void r_listall(void)  /* from=nnn   max=nnn*/
{
	char bf[BFSZ];
	ncpy(group,query_find("group"),BFSZ-1);
	
	if (!open_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
	if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
	lprintf(bf,BFSZ-1,"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 or possibly you need to give your web server access to dnews with no password restrictions<br>%s<br>",nntp_status);
		return;
	}
	read_xover();
	tpl_init();
	tpl_show("list.tpl");
	lprintf(bf,BFSZ-1,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
	read_status(); /* Wait for the channel to close */
} 
static int isbackward;
static int isforward;
void cmd_next(void)
{
	isbackward = FALSE;
	isforward = TRUE;
	b_item = atoi(query_find("xrelated"))+1;
	r_article();
}
void cmd_last(void)
{
	isbackward = TRUE;
	b_item = atoi(query_find("xrelated"))-1;
	r_article();
}

/* brian's new next/previous in thread stuff */
void cmd_thread_both(int isup) ;
void cmd_thread_next(void) 
{
	cmd_thread_both(TRUE);
}
void cmd_thread_last(void) 
{
	cmd_thread_both(FALSE);
}
void cmd_thread_both(int isup) 
{
	char bf[BFSZ];
	char find[BFSZ];
	int np;
	char *p[1000];
	char *s;
	char rsubject[BFSZ];
	char rmsgid[BFSZ];
	int n;
	
	b_item = atoi(query_find("xrelated"));
	
	ncpy(find,query_find("key"),BFSZ-1);
	ncpy(group,query_find("group"),BFSZ-1);
	if (!group_allow(group)) return;
	
	if (!open_status()) {zmsg("News host not responding1: %s",tohtml(nntp_status)); return;}
	if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
	lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding2: %s",tohtml(nntp_status)); return;}
	lprintf(bf,BFSZ-1,"xover %d\015\012",b_item); nntp_write_s(chan,bf,strlen(bf));         
	
	if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
	read_xover();
	
	/* find first message-id in the references */
	s = get_line(instr);
	if (s==NULL) {
		if (isup) var_set("msg","Sorry, no next item in that thread");
		else var_set("msg","Sorry, no previous item in that thread");
        tpl_init_item();
		tpl_show("item_nomore.tpl");
		return;
	}
	if (s!=NULL) s = xstrtok(s,"\t");
	if (s!=NULL) s = xstrtok(NULL,"\t");	/* skip nntp response code*/
	if (s!=NULL) {		/* get subject*/
		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");	/*/ skip from*/
	if (s!=NULL) s = xstrtok(NULL,"\t");	/*/ skip date*/
	if (s!=NULL) s = xstrtok(NULL,"\t");	/*/ skip message-id*/
	if (s!=NULL) ncpy(rmsgid,s,BFSZ-1);	/*/ get the message-id of the root thread*/
	
	str_clear(instr);
	
	lprintf(bf,BFSZ-1,"xhdr references %d\015\012",b_item); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
	read_xover();
	s = get_line(instr);
	if (s!=NULL) {
		ncpy(bf,s,BFSZ-200);
		s = strtok(bf," \r\n");
		if (s!=NULL) s = strtok(NULL," \r\n");	/* skip nntp response code*/
		if (s!=NULL) if (s[0]=='<') ncpy(rmsgid,s,BFSZ-10);
	}
	str_clear(instr);
	
	if (isup) {
		lprintf(bf,BFSZ-1,"xdfind %d- %s %s\015\012",b_item+1,lib_encode(rsubject),lib_encode(rmsgid)); 
	} else {
		lprintf(bf,BFSZ-1,"xdfind 1-%d %s %s\015\012",b_item-1,lib_encode(rsubject),lib_encode(rmsgid)); 
	}
	if (ini_true(I_NEXTLAST_REF)) {
		if (isup) {
			lprintf(bf,BFSZ-1,"xdfind %d- %s\015\012",b_item+1,lib_encode(rmsgid)); 
		} else {
			lprintf(bf,BFSZ-1,"xdfind 1-%d %s\015\012",b_item-1,lib_encode(rmsgid)); 
		}
	}
	nntp_write_s(chan,bf,strlen(bf)); 
	imsg("Thread Searching for related items (%s) \n",bf);
	if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
	if (read_xover()) {
		lprintf(bf,BFSZ-1,"%d",b_item);
		var_set("xreated",bf);
		if (isup) var_set("msg","Sorry, no next item in that thread");
		else var_set("msg","Sorry, no previous item in that thread");
		tpl_init_item();
		tpl_show("item_nomore.tpl");
		return;
	}
	s = get_line_nowrap(instr); 
	if (s==NULL) { zmsg("No xover data"); return;}
	for (n = -1;s!=NULL;) {
		n = atoi(s);
		if (n<0) break;
		if (isup) {
			if (n>b_item) break;
		} else { ;
		}
		s = get_line_nowrap(instr);
	}
	if (n<0) {
		zmsg("Sorry, no next item in that thread");
		return;
	}
	imsg("Found item %d\n",n);
	b_item = n;
	r_article();
}




void r_raw(void)
{
    char bf[BFSZ];
    int item = atoi(query_find("item"));
    if (require_login()) return;
	
    if (b_item>=0) item = b_item;
    ncpy(group,query_find("group"),BFSZ-1);
    imsg("r_raw() group=%s item=%d \n",group,item);
    if (!group_allow(group)) return;
    if (!open_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
    if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
    lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
    if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
    var_set_log("item",int_str(item),"two");
    lprintf(bf,BFSZ-1,"article %d\015\012",item); nntp_write_s(chan,bf,strlen(bf)); 
    if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
    read_xover();
    show_raw();
    lprintf(bf,BFSZ-1,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
    read_status(); /* Wait for the channel to close */
}

void nobble_script(void) {
	/* code to nobble <script> </script> <applet> </applet> and <form> </form> tags */
	char *s;
	static String *tempstr;
	int inscript = FALSE;
	int inapp = FALSE;
	int inform = FALSE;
	int ascript,ajava,aform;
	
	ascript = ini_true(I_ALLOW_SCRIPT);
	ajava = ini_true(I_ALLOW_JAVA);
	aform = ini_true(I_ALLOW_FORM);
	
	imsg("nobble_script: disabled javascript=%d applets=%d forms=%d\n",!ascript,!ajava,!aform); 
	if (ascript&&ajava&&aform) return;
	
	s=str_s(instr);
	if (s==NULL) return;
	tempstr = str_new();
	str_copysn(tempstr,"");
	for(;*s!=0;s++) {
		if (inscript||inapp||inform) {
			/* look for end tag */
			if (*s!='<') {
				str_adds(tempstr,s,1);
				continue;
			};
			if (inscript) {
				if(strncmpnc(s,"</script>",9)==0) {
					str_addsn(tempstr,"</script>\n <!--- --->\n");
					inscript=FALSE;
					s+=9;
					continue;
				};
			};
			if (inapp) {
				if(strncmpnc(s,"</applet>",9)==0) {
					str_addsn(tempstr,"</applet>\n <!--- --->\n");
					inapp=FALSE;
					s+=9;
					continue;
				};
			};
			if (inform) {
				if(strncmpnc(s,"</form>",7)==0) {
					str_addsn(tempstr,"</form>\n <!--- --->\n");
					inform=FALSE;
					s+=7;
					continue;
				};
			};
			str_adds(tempstr,s,1);
			continue;
		} else {
			/* look for start tag */
			if (*s!='<') {
				str_adds(tempstr,s,1);
				continue;
			}
			else {
				/* check for possible <script <app or <form */
				if (!ascript) if (strncmpnc(s,"<script",7)==0) {
					str_addsn(tempstr,"\nscript tag removed\n <!--- ");
					inscript=TRUE;
					continue;
				}
				if (!ajava) if(strncmpnc(s,"<app",4)==0) {
					str_addsn(tempstr,"\napp tag removed\n <!--- ");
					inapp=TRUE;
					continue;
				}
				if (!aform) if(strncmpnc(s,"<form",5)==0) {
					str_addsn(tempstr,"\nform tag removed\n <!--- ");
					inform=TRUE;
					continue;
				}
				str_adds(tempstr,s,1);
				continue;
			}
		}
	};
	/*imsg("nobble script: input: '%s'\n",str_s(instr));*/
	str_copy(instr,tempstr);
	/*imsg("nobble script: output: '%s'\n",str_s(instr));*/
}

void r_article(void)  /* from=nnn   max=nnn*/
{
	char *s;
	char bf[BFSZ];
	int first,last;
	int ner;
	int item = atoi(query_find("item"));
	if (viewitems) item=atoi(var_find("item"));
	if (require_login()) return;
	
	if (b_item>=0) item = b_item;
	ncpy(group,query_find("group"),BFSZ-1);
	if (viewitems) ncpy(group,var_find("group"),BFSZ-10);
	imsg("r_article() group=%s item=%d \n",group,item);
	if (!group_allow(group)) return;
	
	if (!open_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
	if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
	lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {
		if (atoi(nntp_status)==480) {f_login(); return;}	
		zmsg("News host not responding to group: %s",tohtml(nntp_status)); return;
		
	}
	/* status number first last name*/
	last = atoi(get_word(nntp_status,3));
	first = atoi(get_word(nntp_status,2));
	
	imsg("Group first=%d last=%d item %d\n",first,last,item);
	if (item<last) {
		lprintf(bf,BFSZ-1,"%s?cmd=article&group=%s&item=%d&utag=%s",path_script,towww(group),item+1,utage);
		var_set("item_next",bf);
	}
	if (item>first) {
		lprintf(bf,BFSZ-1,"%s?cmd=article&group=%s&item=%d&utag=%s",path_script,towww(group),item-1,utage);
		var_set("item_last",bf);
	}       
	var_set_log("item",int_str(item),"three");
	
	lprintf(bf,BFSZ-1,"article %d\015\012",item); nntp_write_s(chan,bf,strlen(bf)); 
	read_status();
	ner = atoi(nntp_status);
	if (ner==423) {
		if (isbackward) {
			lprintf(bf,BFSZ-1,"last\015\012",item); nntp_write_s(chan,bf,strlen(bf)); 
		} else if (isforward) {
			lprintf(bf,BFSZ-1,"next\015\012",item); nntp_write_s(chan,bf,strlen(bf)); 
		} 
		if (!read_status()) {
			zmsg("News host not responding: %s",tohtml(nntp_status)); 
			return;
		}
		s = strchr(nntp_status,' ');
		if (s!=NULL) item = atoi(s+1);
		imsg("Found next valid item is really %d\n",item);
		lprintf(bf,BFSZ-1,"article\015\012",item); nntp_write_s(chan,bf,strlen(bf)); 
		read_status();
		ner = atoi(nntp_status);
		
		if (item<last) {
			lprintf(bf,BFSZ-1,"%s?cmd=article&group=%s&item=%d&utag=%s",path_script,towww(group),item+1,utage);
			var_set("item_next",bf);
		}
		if (item>first) {
			lprintf(bf,BFSZ-1,"%s?cmd=article&group=%s&item=%d&utag=%s",path_script,towww(group),item-1,utage);
			var_set("item_last",bf);
		}       
		var_set_log("item",int_str(item),"four");	
		real_item = item;
		/*xrelated = item;*/
		if (b_item>=0) b_item = item;
	}
	if (ner>299) {
		zmsg("News host not responding: %s",tohtml(nntp_status)); 
		return;
	}
	read_xover();
	nobble_script();
	show_article();
	if (!viewitems) {
		lprintf(bf,BFSZ-1,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
		read_status(); /* Wait for the channel to close */
	}
}
/*

  From: Bruce the Duck <removed@due.to.spam.com>
  Newsgroups: alt.binaries.sounds.mp3.beatles
  Subject: As requested - Rubber Soul - 05 - Think For Yourself.mp3 (5/7)
  Date: Fri, 22 Oct 1999 13:23:11 -0400
  Message-ID: <f50QOIX3AoNEkXWM0UM4FDBMjNEx@4ax.com>
  X-Newsreader: Forte Agent 1.6/32.525
  MIME-Version: 1.0
  Content-Type: message/partial; id="<epwQODI1v4zyMKtdRRFsHKwjA3Wj@4ax.com>"; number=5; total=7
  NNTP-Posting-Host: 12.21.199.102
  X-Trace: 22 Oct 1999 13:24:52 -0400, 12.21.199.102
  Organization: OneNet Communications News Hub
  Lines: 6415
  
	
	  0wcrZl8Lm71gpW+KvPNpTJirkXqg7nWTFXIYeOo+QpgLEQRz8wZY9/lOiMkVi0ouK8lHUJLp90zl
	  /g+T9Cr6CzxIxXof/+vgzJuuYQa1nyiWZi7SsUX0wlqgctMnD5x5rgi93ZyyCIJTclwxsAUlzPL
	  SjIbe9X3qS3kr87qOXXpGFMZXbMzMDYaHk1MqSC0EE7qT/CF3l2wQGNtHuemIpNrElo2zjl5JMIP
	  
		
		  If mime, then find this in first message.
		  Content-Transfer-Encoding: base64
		  And this in each part header:
		  Content-Type: message/partial; id="<15sQOM0Q6K94PP=MeT=yIjtOJW0L@4ax.com>"; number=1; total=8
		  
			*/
			
			void r_parts(void)
			{
				char *out;
				int nout;
				int r;
				int sz=0;
				char tname[BFSZ];
				char bf[BFSZ];
				char bf2[BFSZ];
				char xbf[BFSZ];
				char xsubj[BFSZ];
				char fname[BFSZ];
				char subj_fname[BFSZ];
				char content[BFSZ];
				int parti;
				int xparts[5000]; 
				int nfound=0;
				FILE *f;
				int item = atoi(query_find("item"));
				char subj[BFSZ];
				char *s,*tmp;
				int n;
				int isbase64 = FALSE;
				int isyenc = FALSE;
				int max;
				int i;
				int chunk;
				int xinit;
				int gotdata = FALSE;
				int upto;
				int res;
				int partmax = atoi(query_find("partmax"));
				if (partmax>500) {
					emsg("Too many parts %d",partmax);
					return;
				}
				/*	ncpy(subj,query_find("subj"),BFSZ-10);*/
				tmp=(char *)decode_url(query_find("subj"));
				if(!tmp) return;
				ncpy(subj,tmp,BFSZ-10);
				
				ncpy(main_type,"parts",100);
				
				
				if (require_login()) return;
				
				s=query_find("ycount");
				if(*s) {
					isyenc = TRUE;
					ncpy(subj,(char *)decode_url(query_find("subj")),BFSZ-10);
				}
				
				if(s=query_find("fname")) ncpy(fname,s,BFSZ-10);
				
				if (b_item>=0) item = b_item;
				ncpy(group,query_find("group"),BFSZ-1);
				imsg("r_parts() group=%s item=%d partmax=%d, subj=(%s) \n",group,item,partmax,subj);
				if (!group_allow(group)) return;
				
				if (!open_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
				if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
				lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
				if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
				
				/*	if (cached_item(tname,content)) goto showit;*/ 
				
				/* status number first last name*/
				ncpy(bf2,subj+1,400);
				lprintf(bf,BFSZ-1,"xdfind 1- %s\015\012",/*lib_encode*/(bf2)); nntp_write_s(chan,bf,strlen(bf)); 
				if (!read_status()) {zmsg("News host not responding xdfind: %s",tohtml(nntp_status)); return;}
				if (read_xover()) var_set("no_match","bazil");
				
				for (i=0; i<=partmax; i++) xparts[i] = -1;
				
				s = get_line_nowrap(instr);
				for (;s!=NULL;) {
					/* */
					n = atoi(s);
					s = strtok(s,"\t");
					if (s!=NULL) {
						s = strtok(NULL,"\t");
						if (s!=NULL) {
							imsg("Found item %d subj %s\n",n,s);
							if (isyenc) {
								memcpy(xsubj,s,BFSZ);
								res=parts_subject_yenc(s,xsubj,&parti,&max);
							} else res=parts_subject(s,xsubj,subj_fname,&parti,&max);
							if (res) {
								if ((strcmpnc(xsubj,subj)!=0) || (max!=partmax)) {
									imsg("Wrong item part (%s) (%s) or max %d..%d\n",xsubj,subj,max,partmax);
								} else {
									if (xparts[parti]<0) nfound++;
									xparts[parti] = n;
								}
							} else {
								imsg("Invalid subject\n");
							}
						}
					}
					s = get_line_nowrap(instr);
				}
				lprintf(content,BFSZ-1,"application/octet-stream; name=\"%s\"",fname);
				
				if (nfound<partmax) {
					do_content_force();
					emsg("Only found %d of %d parts, sorry \n",nfound,partmax); 
					return;
				}
				
				lprintf(bf,BFSZ-1,"parts_%d.tmp",getpid());
				strcpy(tname,ini_work(bf));
				f = fopen(tname,"w");
				if (f==NULL) {
					do_content_force();
					emsg("Cannot write temp file %s %s \n",tname,strerror(errno)); 
					return;
				}
				xinit = FALSE;
				for (parti=1; parti<=partmax; parti++) {
					str_clear(instr);
					lprintf(bf,BFSZ-1,"body %d\015\012",xparts[parti]); nntp_write_s(chan,bf,strlen(bf)); 
					imsg("ask for body %d\n",xparts[parti]);
					if (!read_status()) {zmsg("News host not responding body: %s",tohtml(nntp_status)); goto failed;}
					if (read_xover()) var_set("no_match","bazil");
					s = get_line_nowrap(instr);
					for (;s!=NULL;) {
						if (!xinit) {
							if (strncmpnc(s,"Content-Type: ",14)==0) {
								ncpy(content,s+14,200);
							}
							if (strcmpnc(s,"Content-Transfer-Encoding: base64")==0) {
								isbase64 = TRUE;
								imsg("This is a base64 message\n");
							}
							if (parti>1) if (isbase64) if (gotdata) if ((strlen(s)==60)||(strlen(s)==76)) {
								imsg("Might be data again as it's the right length\n");
								xinit = TRUE;
							}
							if (parti>1) if (isbase64) if (gotdata) if ((strlen(s)>60)) {
								if (strchr(s,' ')==NULL) {
									imsg("Might be data again as it's long and has no spaces \n");
									xinit = TRUE;
								}
							}
							if (!gotdata) if (isbase64) if (strlen(s)==0) {
								xinit = TRUE;
								imsg("Found start of base64 data\n");
							}
						}
						if (strcmp(s,"end")==0) {
							imsg("Found end\n");
							xinit = FALSE;
						}
						if (strcmp(s,"`")==0) {
							imsg("Found reverse quote\n");
							xinit = FALSE;
						}
						if (xinit) if (strlen(s)>0) if(!isyenc) {								
							fprintf(f,"%s\n",s);
							gotdata = TRUE;
						} 
						if (gotdata) if (*s==0) {
							imsg("Found blank line\n");
							/*xinit = FALSE;*/
						}
						if (strncmp(s,"begin ",6)==0) {
							xinit = TRUE;
							if(atoi(tmp=s+7)) if(tmp=strstr(tmp," ")) { /* Fix fname w/ spaces */
								ncpy(fname,tmp+1,BFSZ-10);
							}
							lprintf(content,BFSZ-1,"application/octet-stream; name=\"%s\"",fname);	
						}
						
						if (isyenc) {
							res=0;
							if(strncmp(s,"=ybegin ",8)==0) res=1;
							if(strncmp(s,"=ypart ",7)==0) res=1;
							if(strncmp(s,"=ydata ",7)==0) res=1;
							if(strncmp(s,"=yend",5)==0) res=1;
							if(!res) {
								fprintf(f,"%s\n",s);
							} else {
								imsg("Skipping yEnc header information\n");
								*s='\0';
							}
						}
						
						sz += strlen(s);
						s = get_line_nowrap(instr);
					}
				}
				fclose(f);
#ifdef FIX_STDOUT
				_setmode(_fileno(stdout),_O_BINARY);
#endif
				
				imsg("Image size encoded is %d bytes (%s)\n",sz,tname);
				out = mymalloc(sz+5000);
				nout = 0;
				
				f = fopen(tname,"rb");
				if (f==NULL) {
					do_content_force();
					emsg("Cannot read temp file %s %s \n",tname,strerror(errno)); 
					return;
				}
				for (;!feof(f);) {
					if (fgets(bf,BFSZ-1,f)==NULL) break;
					s = strchr(bf,'\n'); if (s!=NULL) *s = 0;      
					if (isbase64) {
						n = decode_base64(bf,out+nout);
					} else if (isyenc) {
						n = ydecode(bf,strlen(bf),out+nout);
					} else {
						n = decode_uu(bf,out+nout);
					}
					if (n<0) {
						imsg("Odd n=%d\n",n);
						continue;
					}
					nout += n;
				}
				
				fclose(f);
				imsg("Now sending data to screen nout=%d\n",nout);
				done_content = TRUE;
				
				printf("Content-Type: %s\n",content);
				imsg("Content-Type: %s\n",content);
				printf("Content-Disposition: attachment; filename=\"%s\"\n",fname);
				printf("Content-length: %d\n",nout); 
				printf("\n");
				for (upto=0;nout>0;) {
					chunk = nout;
					if (chunk>4000) chunk = 4000;
					nout -= chunk;
					r = fwrite(out+upto,1,chunk,stdout);
					if (r!=chunk) {
						imsg("Wrote %d/%d bytes with fwrite %s\n",r,chunk,strerror(errno));
						break;
					}
					upto += chunk;
				}
				if (fflush(stdout)!=0) {
					imsg("Flush failed %s\n",strerror(errno));
				}
				imsg("file left %s\n",tname);
failed:;
	   remove(tname);	
}
char *post_template(char *xfname);

char *fix_crlf(char *s)
{
	int len;
	int wrap;
	char *bf;
	char *out;
	
	bf = malloc(strlen(s)*2+100);
	
	for (out=bf;*s!=0;s++) {
		if ((s[0]=='\r') && (s[1]!='\n')) {
			*out++ = '\r';
			*out++ = '\n';
		} else if ((s[0]!='\r') && (s[1]=='\n')) {
			*out++ = '\r';
			*out++ = '\n';
		} else {
			*out++ = *s;            
		}
	}
	*out++ = 0;
	return bf;
}
/* chris to here */
void do_addbr(char *msg, char *out)
{
	int len;
	char *s;
	
	for (s=msg; (*s!=0); s++) {
		if (*s=='\n') {
			*out++ = '<';
			*out++ = 'b';
			*out++ = 'r';
			*out++ = '>';
		}
		*out++ = *s;
	}
	*out++ = 0;
}


int JLLgetindentonline(char *indent, char *readpos){
	int len = 0;
    int found = FALSE;
    while (found == FALSE){
        found = TRUE;
        if (readpos[len] == '\r' || readpos[len] == '\n' || readpos[len] == 0) 
		{*(indent + len) = 0; return len;}
        
        if (strncmp((readpos + len),"> ",2)==0)
		{ found = FALSE;
		*(indent + len) = *(readpos + len);
		*(indent + len+1) = *(readpos + len+1);len += 2;}
        else if (strncmp((readpos + len),">",1)==0)
		{ found = FALSE;
		*(indent + len) = *(readpos + len);len += 1;}
		if (len>195) break;
    }
    *(indent + len) = 0;
    return len;
}
int JLLgetindentlen(char *readpos){
	int len = 0;
    int found = FALSE;
    while (found == FALSE){
        found = TRUE;
        if (readpos[len] == '\r' || readpos[len] == '\n' || readpos[len] == 0) 
		{ return len;}
        
        if (strncmp((readpos + len),"> ",2)==0)
		{ found = FALSE;
		len += 2;}
        else if (strncmp((readpos + len),">",1)==0)
		{ found = FALSE;
		len += 1;}
		if (len>195) break;
    }
    return len;
}

void JLLoutput(void (*outputfn)(char *), char *thisline){
	int sp;
	sp = strlen(thisline);
	if (thisline[sp] == ' ')
        thisline[sp] = 0;
	if (thisline[sp-1] == ' ')
        thisline[sp-1] = 0;
	outputfn(thisline);
}

int JLLprintword(char *readpos, char *indent, char *thisline, char *nextword, int wrap, int wordsfoundonline, void (*outputfn)(char *)){
	int thisline_len, nextword_len;
	nextword_len = strlen(nextword);
	if (nextword_len == 0) return wordsfoundonline;
	thisline_len = strlen(thisline);
	if (strlen(nextword) == 0) return wordsfoundonline;
    if (nextword_len + thisline_len > wrap || nextword_len + thisline_len > 380){
        if (thisline_len > JLLgetindentlen(thisline) ){
            JLLoutput(outputfn,thisline);
            wordsfoundonline = 0;
			strcpy(thisline, indent);
        }
        strcat(thisline, nextword);
		wordsfoundonline++;
        strcat(thisline," ");
		return wordsfoundonline;
		/*thisline_len = strlen(thisline);
        if (thisline_len  > wrap || thisline_len > 380){
		JLLoutput(outputfn,thisline);
		wordsfoundonline = 0;
		strcpy(thisline, indent);
        }
        return wordsfoundonline;*/
    }else{
        strcat(thisline, nextword);
        strcat(thisline," ");
        return wordsfoundonline+1;
    }
}

int JLLgetnextword(char *nextword, char *readpos){
    char *nwstart;
    nwstart = nextword;
    if (*readpos == 0)
        return 0;
	while(*readpos == ' ' || *readpos == '\t')
		readpos++;
    if (*readpos == '\n' || *readpos == '\r' || *readpos == 0)
        return 0;
    while(*readpos != ' ' && *readpos != '\t' && *readpos != '\n'
		&& *readpos != '\r' && *readpos != 0   ){
        *nextword = *readpos;
        nextword++;
        readpos++;
		if (nextword-nwstart >196) break;
    }
	*nextword = 0;
    return strlen(nwstart);
}

char *JLLfindbeginningofline(char *readpos){
    if (*readpos == 0)
        return readpos;
    while (*readpos == ' ' || *readpos == '\t')
        readpos++;
    if (*readpos == 0)
        return readpos;
    if (*readpos == '\n' || *readpos == '\r')
        readpos++;
    if (*readpos == '\n' || *readpos == '\r')
        readpos++;
    if (*readpos == 0)
        return readpos;
	return readpos;
}



int JLLmovereadposontonextword(char *readpos){
    int len = 0;
    if (*(readpos+len) == 0)
        return len;
	while(*(readpos+len) == ' ' || *(readpos+len) == '\t')
		len++;
    if (*(readpos+len) == '\n' || *(readpos+len) == '\r' || *(readpos+len) == 0)
        return len;
    while(*(readpos+len) != ' ' && *(readpos+len) != '\t' && *(readpos+len) != '\n'
		&& *(readpos+len) != '\r' && *(readpos+len) != 0   ){
        len++;
    }
	if (len >= 198) return 197;
    return len;
}

int JLLgetlenofnextline(char *readpos){
    int len=0;
    while (*readpos != '\r' && *readpos != '\n' && *readpos != 0)
	{readpos++;len++;}
    return len;
}

void JLLformat_wrap_output(void (*outputfn)(char *), char *readpos, int wrap){
    char nextword[200];
	char indent[200];
    char thisline[800];
	
    int indentlen=0;
    int oldindentlen;
    int wordlen;
    int wordsfoundonline=0;
    int addon;
    int doneline;
	
    if (wrap > 370) wrap = 370;
	if (wrap < 5) wrap = 5;
	
    oldindentlen = indentlen;
    indentlen = JLLgetindentonline(indent, readpos);
    readpos += indentlen;
    strcpy(thisline,indent);
    for(;;){
        wordlen = JLLgetnextword(nextword, readpos);
        addon = JLLmovereadposontonextword(readpos);
		readpos += addon;
        if (wordlen != 0)
        {   wordsfoundonline = JLLprintword(readpos, indent, thisline, nextword, wrap, wordsfoundonline,outputfn);
		}else{
            readpos = JLLfindbeginningofline(readpos);
			
            oldindentlen = indentlen;
            indentlen = JLLgetindentonline(indent, readpos);
			readpos += indentlen;
			
            if (*readpos == 0){
				JLLoutput(outputfn,thisline);
				return;
            }else if (oldindentlen != indentlen ){
                JLLoutput(outputfn, thisline);
				strcpy(thisline,indent);
			}
			else if (JLLgetlenofnextline(readpos) == 0 || wordsfoundonline == 0){
				JLLoutput(outputfn,thisline);
				strcpy(thisline,indent);
			}
            wordsfoundonline = 0;
		}
    }
}

void JLLnntp_write_s(char *s){
    nntp_write_s(JLLchan,s,strlen(s));
    nntp_write_s(JLLchan,"\n",1);
}
void JLLcomment_write_s(char *s){
    printf("> %s\n",s);
}

void JLLtext_encode(char *s){
	int noencode=FALSE;
	static char bf[2410];
	int i;
	char *out=bf;
	for (i=0; i<2400 && *s!=0;i++,s++) {
		if (noencode) {
			if (*s==27) {
				if (strncmp(s+1,"(B",2)==0) noencode = FALSE;
			}
			*out++ = *s;
			continue;
		}
		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 if (*s=='\r' || *s=='\n') { *s = 0;}
                else if (*s==27) {
					if (strncmp(s+1,"$B",2)==0) noencode = TRUE;
					*out++ = *s;
                }
                else *out++ = *s;
	}
	*out++ = 0;
    printf("%s\n",bf);
}



void do_wrap(char *msg)
{
	int len;
	int wrap;
	char *s;
	
	wrap = atoi(ini_get(I_WRAP_POST));
	dmsg("wrap at %d\n",wrap);
	if (wrap==0) return;
	wrap += 2;
	for (len=0,s=msg; (*s!=0); len++,s++) {
	/*              if ((s[0]=='\r') && (s[1]=='\n')) {
	s[0] = ' ';
	}
	if ((s[0]=='\r') && (s[1]!='\n')) {
	s[0] = '\n';
	}
		*/
		if (len>wrap) if (xisspace(*s)) {
			*s = '\n';
			len = 0;
		}
		if ((*s=='\r') || (*s=='\n')) len = 0;
	}
}
char *fix_email(char *s)
{
	static char bf[BFSZ];
	static char bf2[BFSZ];
	char *orig=s;
	ncpy(bf,s,200);
	s = strtok(s,"<> \t\n");
	for (;s!=NULL;) {
		if (strchr(s,'@')!=NULL) {
			lprintf(bf2,BFSZ-1,"<%s>",s);
			return bf2;
		}
		s = strtok(NULL,"<> \t\n");
	}
	return orig;
}
char *eoltospace(char *s)
{
	static char bf[4000];
	ncpy(bf,s,3000);
	for (s=bf;*s!=0;s++) {
		if (*s=='\n') *s = ' ';
		if (*s=='\r') *s = ' ';
	}
	return bf;
}
char *stripeol(char *s)
{
	char *orig=s;
	
	for (;*s!=0;s++) {
		if (*s=='\r') *s = 0;
		if (*s=='\n') *s = 0;
	}
	return orig;
}
char *real_from(char *xin)
{
	static char bf[BFSZ];
	char *s = udom;
	
	if (ini_true(I_FROM_REMOTE_USER)) {
		ncpy(bf,mygetenv("REMOTE_USER"),BFSZ-1);
		return bf;
	}
	if (strlen(ini_get(I_POST_DOMAIN))>0) s = ini_get(I_POST_DOMAIN);
	/* if the username already has a domain don't append the login ip */
	if (strchr(guser,'@')==NULL)
		lprintf(bf,BFSZ-1,"<%s@%s>",guser,s);
	else
		lprintf(bf,BFSZ-1,"%.500s",guser);
	if (strlen(guser)==0) return xin;
	return bf;      
}
int islogin(void)
{
	int tout;
	user_read(utag);
	if (ini_true(I_LOGIN_UTAG)) return TRUE; 
	if (ini_true(I_IGNORE_ADDRESS)) {
		if (strlen(ukey)>0) 
			if (strcmp(user_get("ukey"),ukey)==0) 
				goto xxx;
			if (web_login) {
				r_login(TRUE);
				return TRUE;
			}
			if (strlen(ukey)!=0) {
				zmsg("User login failed, ukey invalid, please login again (%s) \n",ukey);			
				imsg("ukey invalid, please login again (%s) != (%s) \n",user_get("ukey"),ukey);			
				return FALSE;
			}
	}
	if (web_login) {
		r_login(TRUE);
		return TRUE;
	}
	
	if (strcmp(user_get("remote_addr"),mygetenv("REMOTE_ADDR"))==0) {
xxx:            tout = atoi(ini_get(I_LOGIN_TIMEOUT));
                if (tout>0) {
					if (atoi(user_get("login_time"))+tout*60<lib_gmtime()) {
						zmsg("User login timed out %d < %d",atoi(user_get("login_time"))+tout*60,lib_gmtime());
						return FALSE;
					}
                }
				
                return TRUE;    
	} else {
		if (strlen(user_get("remote_addr"))==0) {
			imsg("User not currently logged in\n");
		} else {
			zmsg("User not connecting from correct ip address prev=(%s) != cur=(%s) , you could use IGNORE_ADDRESS true\n",user_get("remote_addr"),mygetenv("REMOTE_ADDR"));
		}
	}
	return FALSE;
}
void do_login(char *bf)
{
	var_set("login_error",bf);
	set_id();
	do_content();
	tpl_show("login.tpl");
}
void do_addbr(char *msg, char *out);

void add_custom_header(void) {
/*
parses the dnewsweb.ini 'add_header' setting and adds a customised header to the
post if it is in the corect format.

  dnewsweb.ini format: add_header X-ComVC: user-id ,comvc
  And in post.tpl with an equivalent form input...
  <input type=text name="comvc" value="12345">
  
    Would append the following header to the post...
    X-ComVC: user-id 12345
    */
	char custom_header[BFSZ];
	char custom_variables[BFSZ];
	char bf[BFSZ];
	char *p,*q;
	int added_one;
    ncpy(custom_header,ini_get(I_ADD_HEADER),400);
	p = strchr(custom_header,',');
	if (p==NULL) {
		wmsg("add_custom_header: malformed add_header line in dnewsweb.ini - correct syntax is 'header_add X-Custom-Header-Name: ,variable[,variable] (%.200s)'\n",ini_get(I_ADD_HEADER));
	} else {
		*p=0;
        if ((q=strchr(custom_header,':'))==NULL) {
			wmsg("add_custom_header: add_header header line must contain a ':' '%.200s'\n",custom_header);
        }
        else {
			if (strchr(q+1,':')!=NULL) {
				wmsg("add_custom_header: add_header header line can't contain more than one colon character ':' '%.200s'\n",custom_header);
			}
			else {
				if (q[1]!=' ') {
					wmsg("add_custom_header: add_header header colon must be followed by space '%.200s'\n",custom_header);
				}
				else {
                    ncpy(custom_variables,p+1,BFSZ-10);
                    q = custom_variables;
                    added_one=FALSE;
                    for(;;) {
                        p=strchr(q,',');
                        if (p!=NULL) *p=0;
                        if (strlen(form_find(q))>0) {
                            ncat(custom_header,stripeol(form_find(q)),400);
                            added_one++;
                        }
                        if (p==NULL) break;
                        q = p+1;
                    }
                    if (added_one) {
                        if (custom_header[0]!='X')
                            lprintf(bf,BFSZ-1,"X-%.400s\015\012",custom_header);
                        else
                            lprintf(bf,BFSZ-1,"%.400s\015\012",custom_header);
                        imsg("add_custom_header: adding header '%.200s'\n",bf);
                        nntp_write_s(chan,bf,strlen(bf)); 
                    }
                    else {
                        imsg("add_custom_header: not adding custom header - no form variables set matching (%.200s)\n",custom_variables); 
                    }
                }
            }
        }
	}
}

void r_post(void)  /* from=nnn   max=nnn*/
{
	char *s2=0,*period;
	char uinfo[BFSZ];
    char ruser[BFSZ];
    char xxfrom[BFSZ],xxreply[BFSZ];
    char bf[BFSZ];
    char *xout;
    char *s;
    char attach[BFSZ];
    int ismime=FALSE;
    int isuuencoded=FALSE;
    char xsubj[BFSZ];
    char xfile[BFSZ];
    FILE *f;
    FILE *fout;
    char boundary[BFSZ];
    char newsgroups[BFSZ];
    char xfrom[BFSZ],xname[BFSZ];
    int lp;
    int addmid = FALSE;
    int sk = chan;
    char *p;
	char *oldp;
    char newsgroup[BFSZ];
    struct tm *newtime;
    time_t aclock;
	
	if (ini_true(I_POST_REQUIRE_LOGIN)) {
		if (!islogin()) {
			imsg("require_login, attempting to login now\n");
			r_login(TRUE);
		}
	}
	
	user_read(utag);
	imsg("User data read\n");
	
	if (ini_true(I_POST_REQUIRE_LOGIN)||ini_true(I_POST_REQUIRE_LOGIN_AFTER)) {
		if (!islogin()) {
			if (ini_true(I_POST_LOGIN_POST)) {
				var_set("require_login","true");
			} else {
				imsg("Sorry user isn't logged in\n");
				lprintf(bf,BFSZ-1,"Sorry, you must login before posts can be accepted");
				var_set("login_error",bf);
				set_id();
				tpl_show("login.tpl");
				return;
			}
		}
	}
	
    imsg("Length of attach field %d\n",strlen(attach));
    ncpy(attach,form_find("attach"),200);
    if (strlen(attach)>0) {
		if (lib_length_file(attach)>3) {
			imsg("attach file (%s) %d bytes\n",attach,lib_length_file(attach));
			if(ini_true(I_UUENCODE_ATTACH)) isuuencoded = TRUE;
			else ismime = TRUE;
		}
    }
	
	
    
    if (strcmp("true",ini_get(I_ADD_MID))==0) addmid=TRUE;
    imsg("mid %d, (%s) \n",addmid,ini_get(I_ADD_MID));
	
    ncpy(ruser,mygetenv("REMOTE_ADDR"),BFSZ-1);
	
    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();
	
    imsg("mid2 \n");
	if (!ini_true(I_FROM_OPTIONAL)) if (!ini_true(I_FROM_REMOTE_USER)) {
		if (strlen(form_find("from"))==0) {
nofrom:         var_set("missing","from");
                tpl_show("required.tpl");
                return;
		}
	}
    if (strlen(form_find("subject"))==0) {
		var_set("missing","subject");
		tpl_show("required.tpl");
		return;
    }
	
    imsg("mid3 \n");
    ncpy(bf,form_find("required"),400);
    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,",");
    }
    ncpy(bf,form_find("optional"),400);
    s = strtok(bf,",");
    for (;s!=NULL;) {
		if (strlen(form_find(s))!=0) {
			var_set(s,form_find(s));
		}
		s = strtok(NULL,",");
    }
	
    imsg("mid5 \n");
	
    ncpy(group,form_find("group"),BFSZ-1);
    stripeol(group);
	/* make sure all cross-posted newsgroups exist */
	ncpy(newsgroup,group,BFSZ-10);
	dmsg("r_post newsgroups (%.200s)\n",newsgroup);
    oldp=newsgroup;
	/* can't use strtok because strtok replies on a static buffer and
	   thus calling it recursively in the group_allow and group_match
	   functions will break it :-) So munge it with strchr and some pointers*/
	for(;;) {
		p = strchr(newsgroup,',');
		if (p!=NULL) *p=0;
		
		dmsg("checking if posting allowed to newsgroup %.200s\n",oldp);
		if (!group_allow(oldp)) {zmsg("Sorry, posting not allowed to group %.200s\n",oldp); return;}
		if (strlen(ini_get(I_POST_GROUPS))>0) if (!group_match(ini_get(I_POST_GROUPS),oldp)) {
			printf("Sorry, posting is not permitted to (%s) \n<br>",oldp);
			return;
		}
		if (!open_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
		if (!do_authent()) {zmsg("News host authentication failed: %s",nntp_status) ; return;}
        lprintf(bf,BFSZ-1,"group %s\015\012",oldp); nntp_write_s(chan,bf,strlen(bf));
        if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
		
		if (p==NULL) break;
		oldp = p+1;
    }
    lprintf(bf,BFSZ-1,"post\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
    if (!read_status()) {zmsg("Post status bad %s\n",tohtml(nntp_status)); return;}
	
    imsg("From %s\n",form_find("from"));   
    ncpy(xfrom,form_find("from"),200);
    ncpy(xname,form_find("from_name"),200);
	
	
	if (ini_true(I_FROM_ALLOW_EMAIL)) {
		ncpy(xname,mygetenv("REMOTE_USER"),BFSZ-1);
	}
	
	if (ini_true(I_FROM_REMOTE_USER)) {
		ncpy(xxfrom,mygetenv("REMOTE_USER"),BFSZ-1);
		ncpy(xxreply,mygetenv("REMOTE_USER"),BFSZ-1);
	} else {
		stripeol(xfrom);
		stripeol(xname);
		ncpy(xxfrom,real_from(xfrom),400);
		ncpy(xxreply,fix_email(xfrom),400);
		if (strlen(xname)>0) {
			lprintf(xxfrom,BFSZ-1,"\"%s\" %s",xname,fix_email(real_from(xfrom)));
			lprintf(xxreply,BFSZ-1,"\"%s\" %s",xname,fix_email(xfrom));
		}
		if (!ini_true(I_FROM_NOCHECK)) if (strchr(xxreply,'.')==NULL) {
			zmsg("Sorry, invalid email address given in FROM field\n");
			return;
		}
	}
	if (!ini_true(I_FROM_ALLOW_EMAIL)) {
		if (ini_true(I_FROM_WEB_LOGIN)) {
			lprintf(xxfrom,BFSZ-1,"<%s@%s>",mygetenv("REMOTE_USER"),ini_get(I_POST_DOMAIN));
			var_set("from",bf);
			ncpy(xxreply,xxfrom,BFSZ-1);
		}
	} 
	
	if (strcmp(xxreply,"<@.>")==0) ncpy(xxreply,xxfrom,200);
    lprintf(bf,BFSZ-1,"From: %s\015\012",xxreply);      nntp_write_s(chan,bf,strlen(bf)); 
    if (!ini_true(I_DISABLE_SENDER)) {
		lprintf(bf,BFSZ-1,"Sender: %s\015\012",xxfrom);      nntp_write_s(chan,bf,strlen(bf)); 
    }
    if (!ini_true(I_DISABLE_REPLYTO)) {
		lprintf(bf,BFSZ-1,"Reply-To: %s\015\012",xxreply);      nntp_write_s(chan,bf,strlen(bf)); 
    }    
    ncpy(xsubj,stripeol(form_find("subject")),500);
    ncpy(bf,xsubj,400);
    strlwr(bf);
    s = strtok(bf," \t"); if (s==NULL) s = bf;
    if (strncmp(s,"cmsg",3)==0)  {
		ncpy(bf,stripeol(form_find("subject")),BFSZ-10);
		strcpy(xsubj,"x-");
		strcat(xsubj,bf);
    }
    lprintf(bf,BFSZ-1,"Subject: %s\015\012",xsubj);   nntp_write_s(chan,bf,strlen(bf)); 
    ncpy(newsgroups,form_find("newsgroups"),BFSZ-1);
	stripeol(newsgroups);
    if (strlen(newsgroups)>0) {
		lprintf(bf,BFSZ-1,"Newsgroups: %s\015\012",newsgroups); 
    } else {
		lprintf(bf,BFSZ-1,"Newsgroups: %s\015\012",stripeol(form_find("group")));
    }
	if (strlen(form_find("crosspost"))>0) {
	/* for follow up messages if they don't select the reply all
		cross-posted newsgroups checkbox then reply only to the primary group */
		if (strlen(form_find("replyall"))==0) {
			lprintf(bf,BFSZ-1,"Newsgroups: %.400s\015\012",stripeol(form_find("primarygroup")));
			ncpy(group,stripeol(form_find("primarygroup")),400);
		}
	}
	nntp_write_s(chan,bf,strlen(bf)); 
	if (addmid) {
		lprintf(bf,BFSZ-1,"Message-ID: <%d@%s>\015\012",time(NULL),ini_get(I_NEWSHOST));     nntp_write_s(chan,bf,strlen(bf)); 
    }
	
    ncpy(uinfo,mygetenv("REMOTE_ADDR"),BFSZ-1); strcat(uinfo," ");
    strcat(uinfo,mygetenv("REMOTE_HOST")); strcat(uinfo," ");
    strcat(uinfo,mygetenv("REMOTE_IDENT")); strcat(uinfo," ");
    strcat(uinfo,mygetenv("REMOTE_USER")); strcat(uinfo," ");
    strcat(uinfo,utag); strcat(uinfo," ");
    if (!ini_true(I_POST_ANONYMOUS)) {
		lprintf(bf,BFSZ-1,"X-User-Info: %s\015\012",uinfo);     nntp_write_s(chan,bf,strlen(bf)); 
		lprintf(bf,BFSZ-1,"X-Forwarded-For: %s\015\012",s=mygetenv("X_FORWARDED_FOR"));
		if(*s=='\0')  lprintf(bf,BFSZ-1,"X-Forwarded-For: %s\015\012",s=mygetenv("HTTP_X_FORWARDED_FOR"));
		if(*s) nntp_write_s(chan,bf,strlen(bf));    
	}
	
    if (strlen(form_find("ref"))>0) {
		lprintf(bf,BFSZ-1,"References: %s\015\012",stripeol(form_find("ref")));     nntp_write_s(chan,bf,strlen(bf)); 
    }
    /* add a custom header to posts if add_header setting present in dnewsweb.ini */
    if (strlen(ini_get(I_ADD_HEADER))>0) add_custom_header();
    lprintf(boundary,BFSZ-1,"-=_%s%x",nntp_hostname(),time(NULL));
    if (ismime) {
		tcp_printf(sk,"MIME-Version: 1.0");
		tcp_printf(sk,"Content-Type: multipart/mixed;\r\n\tboundary=\"%s\"",boundary);
		tcp_printf(sk,"");
		tcp_printf(sk,"This is a multi-part message in MIME format.");
		tcp_printf(sk,"");
		tcp_printf(sk,"--%s",boundary);
		tcp_printf(sk,"Content-Type: text/plain");
		tcp_printf(sk,"Content-Transfer-Encoding: 7bit");
    }
    tcp_printf(sk,"");
    
    
    lprintf(bf,BFSZ-1,"\015\012");       nntp_write_s(chan,bf,strlen(bf)); 
	
    imsg("Message \n");   
    s = form_find("message");
	
	period=strstr(s,"\015\012."); /* RL - Fix posting "[\015]\012." */
	if(!period) period=strstr(s,"\012.");
	if(period) {
		s2=malloc(strlen(s)*2);
		ncpy(s2,s,strlen(s));
		period=strstr(s2,"\015\012.");
		if(!period) period=strstr(s2,"\012.");
		while(period) {
			s=period;
			while(*s!='.') s++;
			memmove(s+1,s,strlen(s)+1);
			*s='.';
			period=strstr(s,"\015\012.");
			if(!period) period=strstr(s,"\012.");
		}	
		s=s2;
	}
	
    imsg("message length %d\n",strlen(s));
    /*s = fix_crlf(s);*/
    
    
    JLLchan = chan;
    if (s!=NULL) 
		if (*ini_get(I_KEEP_FORMATING) == 't')
			JLLformat_wrap_output(JLLnntp_write_s,s,atoi(ini_get(I_WRAP_POST)));
		else
		{
			do_wrap(s);
			imsg("message warpeed (%d)\n",strlen(s));
			strcpy(xfile,form_find("template"));
			if (strlen(xfile)>0) {
				var_set("message",s);
				s = post_template(xfile);
			}
			imsg("message send (%d)\n",strlen(s));
			if (s!=NULL) nntp_write_s(chan,s,strlen(s));
		}
		
		free(s2);
		
		if (ismime) {
            tcp_printf(sk,"\r\n--%s",boundary);
            tcp_printf(sk,"Content-Type: %s; name=\"%s\"",attach_type(),attach_showname());
            tcp_printf(sk,"Content-Transfer-Encoding: base64");
            tcp_printf(sk,"Content-Disposition: attachment; filename=\"%s\"",attach_justname());
			
            tcp_printf(sk,"");
            f = fopen(attach_fname(),"rb");
            if (f==NULL) {zmsg("temp attach file problem %s %s\n",attach_fname(),strerror(errno)); goto failed;}
            fout = fopen(temp_fname(),"w");
            if (fout==NULL) {zmsg("temp file problem %s %s\n",temp_fname(),strerror(errno)); goto failed;}
            base64_encode(f,fout);
			
            f = fopen(temp_fname(),"r");
            if (f==NULL) {  goto fail2; }
            for (;!feof(f);) {
				if (fgets(bf,BFSZ-10,f)==NULL) break;
				s = strchr(bf,'\n');
				if (s!=NULL) *s = 0;
				tcp_printf(sk,"%s",bf);
            }
            fclose(f);
fail2:      remove(temp_fname());
            remove(attach_fname());
		} else if(isuuencoded) {
			tcp_printf(sk,"\r\nbegin 600 %s",attach_showname());
			f = fopen(attach_fname(),"rb");
			if (f==NULL) {zmsg("temp attach file problem (uuencode) %s %s\n",attach_fname(),strerror(errno)); goto failed;}
			fout = fopen(temp_fname(),"w");
			if (fout==NULL) {zmsg("temp file problem (uuencode) %s %s\n",temp_fname(),strerror(errno)); goto failed;}
			uuenc(f,fout);
			fclose(fout);
			
			f = fopen(temp_fname(),"r");
			if (f==NULL) {  goto fail2u; }
			while(fgets(bf,BFSZ-10,f)) {
				s = strchr(bf,'\n');
				if (s!=NULL) *s = 0;
				tcp_printf(sk,"%s",bf);
			}
			tcp_printf(sk,"end\n");
			
			fclose(f);
fail2u:	remove(temp_fname());
		remove(attach_fname());
		}
		
		s = cr_decode(user_get("sig"));
		if (strlen(s)>0) {
            if (ismime) {
				tcp_printf(sk,"\r\n--%s",boundary);
				tcp_printf(sk,"Content-Type: text/plain");
				tcp_printf(sk,"Content-Transfer-Encoding: 7bit");
            }
            tcp_printf(sk,"");
            nntp_write_s(chan,s,strlen(s));
		}
		
		if (ismime) {
            tcp_printf(sk,"\r\n--%s--",boundary);
		}
		
		lprintf(bf,BFSZ-1,"\015\012.\015\012");       nntp_write_s(chan,bf,strlen(bf)); 
		for (lp=0;nntp_isbusy(chan) && lp<10000;lp++) {
            nntp_write_now(chan);
		}
		
		
		imsg("readstatus (%d)\n",strlen(s));
		read_status(); /* Wait for the channel to close */
		imsg("readstatxxxus (%d)\n",strlen(s));
		
		
		var_set("post_status",nntp_status);
		if (atoi(nntp_status)==240) {
			var_set("ok","TRUE");
			if (*post_log_file) {
				time(&aclock);
				newtime=localtime(&aclock);
				post_log_do("%s User \"%s (%s)\", Subject \"%s\", Newsgroups \"%s\"",stripeol(asctime(newtime)),xxreply,mygetenv("REMOTE_USER"),xsubj,group);
			}
		}
		
		
		/* if the message is posted to two or more newsgroups then
		set the primary newsgroup so posted.tpl knows which newsgroup
		the lastest items button should reference  (the primary newsgroup
		is the newsgroup in which the post button was pressed */
		ncpy(newsgroup,group,BFSZ-10);
		if (strlen(newsgroup)>0) {
			if (strchr(newsgroup,',')!=NULL)
				var_set("crosspost","crosspost");
		}
		var_set("primarygroup",form_find("primarygroup"));
		var_set("allgroups",group);
		tpl_show("posted.tpl");
		lprintf(bf,BFSZ-1,"quit\015\012"); nntp_write_s(chan,bf,strlen(bf)); 
		read_status(); /* Wait for the channel to close */
		return;
failed:
		zmsg("Unable to read attachment file");
		return;
} 
char *temp_fname(void)
{
	static char fname[BFSZ];
	char *s;
	static char bf[BFSZ];
	if (strlen(fname)!=0) return fname;
	strcpy(fname,ini_work("teXXXXXX"));
	s = mktemp(fname);
	if (s==NULL) s = "attach2.tmp";
	ncpy(bf,s,BFSZ-1);
	strcpy(fname,bf);
	return fname;
}

void r_logout(void)
{
	int g;
	
	if (!skip_content) if (!done_content) {
		printf("Set-Cookie: dnews_utoken=%s; expires=%s\n","",cookie_date_plus(60*60*12));
		imsg("Logout. Set-Cookie: dnews_utoken=%s; expires=%s\n","",cookie_date_plus(60*60*12));
		done_content = TRUE;
		printf("Content-type: %s\n\n",content_type());
	}        
	user_read(utag);
	
	user_set("remote_addr","");
	user_set("ukey","");
	
	for (g=0; g<ngroups; g++) {
		groups[g].lasttime = 1;
	}
	
	user_write(utag);
	f_login();
}
void r_catchup(void)
{
	int g;
	user_read(utag);
	
	for (g=0; g<ngroups; g++) {
		groups[g].lasttime = 2;
	}
	
	user_write(utag);
	r_user();
}
void show_dline(char *s)
{
	char bf[BFSZ];
	char gname[BFSZ];
	char mod[BFSZ];
	char from[BFSZ];
	char to[BFSZ];
	char items[BFSZ];
	char href[BFSZ];
	char lcpy[BFSZ];
	char *s1;
	
	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,".");
		 var_set("grp_name",to);
		 var_set("grp_nitems",from);
		 if (group_match(ini_get(I_ALLOW),to)) {
			 s1=ini_get(I_GROUP_DIR_GROUPS);
			 if(*s1) {
				 ncpy(lcpy,s1,BFSZ-1);
				 sprintf(href,"%s?cmd=listall&group=%s&utag=%s",path_script,towww(to),utage);
				 var_set("grp_href",href);
				 tpl_replace_vars(lcpy);
				 printf(lcpy);
			 } else printf("<li><a href=\"%s?cmd=listall&group=%s&utag=%s\">%s.. %s groups</a> <br>",path_script,towww(to),utage,to,from);
		 }
	 } else {
		 var_set("grp_name",gname);
		 var_set("grp_nitems",items);
		 if (group_match(ini_get(I_ALLOW),gname)) {
			 s1=ini_get(I_GROUP_DIR_ITEMS);
			 if(*s1) {
				 ncpy(lcpy,s1,BFSZ-1);
				 sprintf(href,"%s?cmd=xover&group=%s&utag=%s",path_script,towww(gname),utage);
				 var_set("grp_href",href);
				 tpl_replace_vars(lcpy);
				 printf(lcpy);
			 } else printf("<li><a href=\"%s?cmd=xover&group=%s&utag=%s\">%s, %s items %s</a> <br>",path_script,towww(gname),utage,gname,items,mod,utage);
		 }
	 }
}

static char topname[BFSZ];
void r_list(void)
{
	imsg("r_list\n");
	tpl_init();
	imsg("r_list2\n");
	
	if (ini_true(I_REQUIRE_LOGIN) || web_login) {
		if (!islogin()) {
			f_login();
		} else {
			r_user();
		}
		return;
	}
	imsg("r_list3\n");
	if (!open_status()) {zmsg("Could not read opening message - %s",tohtml(nntp_status)); return;}
	
	imsg("r_list4 %s\n",topname);
	
	tpl_show(topname);
	imsg("r_list5\n");
	dmsg("top done\n");
}
/* ---------------------------------------- xxx.ini file processing -- */
void db_setscript(char *script)
{
	char *last;
	char *s;
	char bf[BFSZ];
	ncpy(path_script,script,BFSZ-1);
	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 xin[5000];
	char *last;
	FILE *f;
	int k;
	int i;
	char *s;
	
	db_setscript(mygetenv("SCRIPT_NAME"));
	lprintf(bf,BFSZ-1,"%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;
		lprintf(bf,BFSZ-1,"%s/%s.ini",xroot,rootname);
		imsg("Looking in (%s) for ini file\n",bf);
		f = fopen(bf,"r");
		strcpy(bf2,bf);
	}
	if (f==NULL) {
		lprintf(bf,BFSZ-1,"/etc/%s.ini",rootname);
		imsg("Last resort, looking in (%s) for ini file\n",bf);
		f = fopen(bf,"r");
	}
	if (f==NULL) {
		done_content = TRUE;
		if (docache) printf("Pragma: cache\n");
		printf("Content-type: %s\n\n",content_type());
		done_content = TRUE;
		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(xin,4000,f)!=NULL) {
			ncpy(bf,xin,BFSZ-20);
			s = strtok(bf," \t\n"); if (s==NULL) continue;
			if (*s==0 || *s=='#') continue;
			i = ini_find(s);
			if (i<0) { 
				do_content_force();
				printf("\r\n\r\n");
				emsg("Unknown ini file setting {%s} \n",s); continue; 
			}
			s = strtok(NULL,"\n"); if (s==NULL) continue;
			for (k=strlen(s)-1; k>=0; k--) {
				if ((s[k]!=' ') && (s[k]!='\t')) {
					s[k+1] = 0;
					break;
				}
			}
			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);
int 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);
	return TRUE;
}
int 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);
	return TRUE;
}
int imsg(const char * arg_list, ...)
{
	va_list arg_ptr;
	char *format;
	int len;
	if (!dodebug) return 0;
	va_start(arg_ptr, arg_list);
	format = (char *) 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);
	return TRUE;
}
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");
	/*      if (!done_content) {
	if (docache) printf("Pragma: cache\n");
	printf("Content-type: %s\n\n",content_type());
	done_content = TRUE;
	}
	*/
	if (strlen(output) + strlen(etext)<10000) {
		strcat(etext,output);
	}
	/*      printf("%s",output);*/
	emsg_do(D_INFO,output);
}
int 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);
	return TRUE;
}
static int domoredebug=FALSE;
int dmsg(char * arg_list, ...)
{
	va_list arg_ptr;
	char *format;
	int len;
	if (!dodebug) return 0;
	if (!domoredebug) return 0;
	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");
	if (domoredebug) emsg_do(D_DEBUG,output);
	return TRUE;
}
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)  {
		do_content_force();
		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 (xisspace(*s)) *s = '+';
	}
	return ss;
}
char *lib_encode_more(char *bf,char *s)
{
	char *out=bf;
	int i;
	if (s==NULL) return "";
	if (strlen(s)>600) return s;
	
	for (i=0; i<1000 && *s!=0;i++,s++) {
		if (*s=='&') { strcpy(out,"%26"); out += strlen(out); }
		else if (*s=='#') { strcpy(out,"%23"); out += strlen(out); }
		else if (*s==' ') { strcpy(out,"+"); out += strlen(out); }
		else if (*s=='+') { strcpy(out,"%2b"); out += strlen(out); }
		else if (*s=='|') { strcpy(out,"%7c"); out += strlen(out); }
		else if (*s=='%') { strcpy(out,"%25"); out += strlen(out); }
		else if (*s=='"') { strcpy(out,"%22"); out += strlen(out); }
		else *out++ = *s;
	}
	*out++ = 0;
	return bf;
}
char *value_encode(char *s)
{
	static char bf[18010];
	int noencode=FALSE;
	int i;
	int iniso;
	int addend=FALSE;
	char *out=bf;
	
	if (s==NULL) return "";
	for (i=0; i<2000 && *s!=0;i++,s++) {
		if (noencode) {
			if (*s==27) {
				if (strncmp(s+1,"(B",2)==0) noencode = FALSE;
			}
			*out++ = *s;
			continue;
		}
		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 if (*s=='"') { strcpy(out,"&#34;"); out += strlen(out); }
		else if (*s==27) {
			if (strncmp(s+1,"$B",2)==0) noencode = TRUE;
			*out++ = *s;
		} 
		else *out++ = *s;
	}
	*out++ = 0;
	return bf;
}
char *head_decode(char *s)
{
	return s;
	
	/*
	static char bf[10000];
	char *out,*s2;
	int iniso=FALSE;
	
	  for (out=bf;*s!=0;s++) {
	  if ((strncmp(s,"=?iso",5)==0) || (strncmpnc(s,"=?ISO",5)==0)) {
	  s2 = strchr(s+3,'?');
	  if (s2!=NULL) s = s2;
	  iniso = TRUE;
	  strcpy(out,"\033$B"); out+= strlen(out);
	  continue;
	  }               
	  if (iniso) if ((strncmp(s,"?=",2)==0)) {
	  s = s+1;
	  iniso = FALSE;
	  strcpy(out,"\033(B"); out+= strlen(out);
	  continue;
	  }
	  *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_url(char *protocol, char *instr, int maxlen);
char *ref_encode_mail(char *instr, int maxlen);
char *ref_encode(char *s)
{
	static char bf[15000];
	ncpy(bf,s,4000);
	if (strchr(s,27)!=NULL) return bf;
	s = ref_encode_mail(bf,4000);
	s = ref_encode_url("http:",s,4000);
	s = ref_encode_url("https:",s,4000);
	s = ref_encode_url("ftp:",s,4000);
	s = ref_encode_url("telnet:",s,4000);
	return s;
}
char *ref_encode_mail(char *instr, int maxlen)
{
	int len;
	char bf[20000];
	char bf2[20000];
	char *s,*s1,*s2;
	s = instr;
	if (ini_true(I_NO_MAILTO)) return 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 (xisspace(*s1)) {
			s1++;
			break;
		}
	}
	for (s2=s;*s2!=0;s2++) {
		if (xisspace(*s2)) break;
	}
	s2--;
	len = s2-s1+1;
	if (len>BFSZ-200) return instr;
	if (strlen(instr)*2>maxlen) return instr;
	ncpy(bf,s1,len);
	if (strstr(bf,"http")!=NULL) {
		s++;
		goto again;
	}
	lprintf(bf2,BFSZ-1,"<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_url(char *protocol,char *instr, int maxlen)
{
	char *s,*s1,*s2;
	char bf[20000];
	char bf2[20000];
	int len;
	s = instr;
again:
	s = strstr(s,protocol);
	if (s==NULL) return instr;
	if (s>instr) if (s[-1]<0) {
		s++;
		goto again;
	}
	s1 = s;
	for (s2=s;*s2!=0;s2++) {
		if (xisspace(*s2)) break;
	}
	s2--;
	
	len = s2-s1+1;
	if (len>BFSZ-200) return instr;
	if (strlen(instr)+500>maxlen) return instr;
	
	ncpy(bf,s1,s2-s1+1);
	lprintf(bf2,BFSZ-1,"<a%s%s href=\"%s\">%s</a>",(strlen(ini_get(I_LINK_ADD))>0)?" ":"",ini_get(I_LINK_ADD),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_base64text(char *s) {
	static char bf[2000];
	char *c,*d;
	int n = decode_base64(s,bf);
	bf[n] = 0;
	/* cleverly go thru and turn \r\n into just \n for decoded text messages so
	that messages encoded with both don't stuff up the formatting when we display
	the text */
	for(c=d=bf;*c!=0;c++,d++) {
		if (((c[0]=='\r')||(c[0]=='\n'))&&((c[1]=='\r')||(c[1]=='\n'))) {c++; *d='\n';}
		else *d=(*c=='\r')?'\n':*c; 
	};
	*d=0;
	return bf;
};
/* quoted printable decoder - stolen from ralph, but it seems to work :-) */
char hexbyte(char *s)
{
	char bf[3];
	int x;
	strncpy(bf,s,2);
	bf[2] = 0;
	sscanf(bf,"%x",&x);
	return (char) x;
}

static char qeol[2]="\n";
char *decode_quote_eol(void){
	return qeol;
}

char *decode_quote(char *s){
	static char bf[2000];
	int i;
	int truncated=FALSE;
	char *out=bf;
	for (i=0; i<2000 && *s!=0;i++,s++) {
		if (*s<0) {
			*out++ = *s++;
			*out++ = *s;
		} else if (*s=='=') {
			if (s[1]==0) { truncated=TRUE; continue;}
			if (s[1]=='\n' || s[1]=='\r') { if (s[2]==0) truncated=TRUE; continue;}
			*out++ = hexbyte(s+1);
			s+=2;
		} else *out++ = *s;
	}
	*out++ = 0;
	if (truncated) qeol[0]=0; else qeol[0]='\n';
	return bf;
}
/* Chris's testing? quoted printable decoder - broken!
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 *wrap_encode(char *s);
char *text_encode(char *s)
{
	int noencode=FALSE;
	static char bf[2010];
	int i;
	char *out=bf;
	for (i=0; i<2000 && *s!=0;i++,s++) {
		if (noencode) {
			if (*s==27) {
				if (strncmp(s+1,"(B",2)==0) noencode = FALSE;
			}
			*out++ = *s;
			continue;
		}
		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 if (*s==27) {
					if (strncmp(s+1,"$B",2)==0) noencode = TRUE;
					*out++ = *s;
                }
                else *out++ = *s;
	}
	*out++ = 0;
	return ref_encode(wrap_encode(bf));
}
char *wrap_encode(char *s)
{
	static char bf[4000];
	int i;
	char *out=bf;
	int len;
	int w = atoi(ini_get(I_WRAP_TEXT));
	if (w==0) return s;
	dmsg("Wrap text at %d\n",w);
	len = 0;
	for (i=0; (i<2000) && (*s!=0);i++,s++,len++) {
		if (*s=='\n') len = 0;
		if (*s=='\r') len = 0;
		if ((*s=='\n') || (s[1]==0)) if (ini_true(I_ADD_BR)) {
			strcpy(out,"<br>"); out+= strlen(out);
		}
		if (len>w) {
			if (xisspace(*s)) {
				if (ini_true(I_ADD_BR)) {
					strcpy(out,"<br>"); out+= strlen(out);
				}
				*out++ = '\n';
				len = 0;
				continue;
			}
		} 
		*out++ = *s;
	}
	*out++ = 0;
	return bf;
}
char *mygetenv(char *v)
{
	char *s;
#ifdef VMS
	char bf[2000];
	lprintf(bf,2000-1,"WWW_%s",v);
	v = bf;
#endif
	s = getenv(v);
	if (s==NULL) return "";
	return s;
}
char *query_find(char *s)
{
	int i;
	static char bf[200];
	if (strcmp(s,"item")==0) if (real_item>0) {
		lprintf(bf,200-1,"%d",real_item);
		return bf;
	}
	for (i=0; i<nquery; i++) if (strcmp(query_name[i],s)==0) return query_val[i];
	return "";
}

int log_hacker(void)
{
	time_t t;
	FILE *f;
	char *q;
	do_content_force();
	emsg("Hacker attempted to abuse this system, details being logged\n");
	printf("<html><h3>Fatal error occured, this appears to be a hacking attempt, your details are now being logged for the system administrator, we suggest you contact the adminstrator to explain your actions</h3></html>\n");
	f = fopen(ini_work("hacker.log"),"a");
	if (f==NULL) return TRUE;
	
	q =  mygetenv("QUERY_STRING");
	
	t = time(NULL);
	fprintf(f,"Possible hacking attempt at %s\n",ctime(&t));
	fprintf(f,"remote_addr %s\n",mygetenv("REMOTE_ADDR"));
	fprintf(f,"remote_host %s\n",mygetenv("REMOTE_HOST"));
	fprintf(f,"remote_ident %s\n",mygetenv("REMOTE_IDENT"));
	fprintf(f,"remote_user %s\n",mygetenv("REMOTE_USER"));
	if (q!=NULL) fprintf(f,"Hackers query string was %s\n",q);
	fprintf(f,"\n\n");
	fclose(f);
	
	return TRUE;
}
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;
	ncpy(bf,q,9000); cl = bf;
	for(x=0;cl[0] != '\0';) {
		getword(val,cl,'&',1999);
		plustospace(val);
		unescape_url(val);
		getword(name,val,'=',BFSZ-1);
		if (strlen(name)>400) continue;
		if (strlen(val)>400) {
			log_hacker();
			exit(0);
			continue;
		}
		if(*name=='/' && !(*cl)) {
			strcpy(val,name+1);
			strcpy(name,"fname");
		}
		query_name[x] = strdup(name);
		query_val[x] = strdup(val);
		x++;
		/*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 len) 
{
    int x = 0,y;
    len--;
	
    for(x=0;((line[x]) && (line[x] != stop));x++) {
        word[x] = line[x];
		if (x>=len) break;
    }
	
    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 */

char *attach_fname(void)
{
	static char fname[BFSZ];
	char *s;
	static char bf[BFSZ];
	if (strlen(fname)!=0) return fname;
	strcpy(fname,ini_work("atXXXXXX"));
	imsg("attach_fname_mask(%s)\n",fname);
	s = mktemp(fname);
	if (s==NULL) s = "attach.tmp";
	imsg("attach_fname_result(%s)\n",s);
	ncpy(bf,s,BFSZ-1);
	ncpy(fname,bf,BFSZ-1);
	return fname;
}
static char a_type[BFSZ],a_name[BFSZ];
char *attach_type(void)
{
	return a_type;
}
char *attach_showname(void)
{
	return a_name;
}
char *attach_justname(void)
{
	static char bf[BFSZ];
	char *last;
	char *s;
	static char tmp[BFSZ];
	ncpy(tmp,a_name,200);
	last = NULL;
	for (s=tmp;*s!=0;s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	if (last==NULL) return a_name;
	ncpy(bf,last+1,200);
	return bf;
	
}

void form_get_mime(char *content)
{       
	char bf[BFSZ];
	char bf2[BFSZ];
	char boundary[BFSZ];
	char bound_part[BFSZ];
	char bound_end[BFSZ];
	int cl;
	char name[BFSZ];
	char fname[BFSZ];
	char *body;
	char tmp[BFSZ];
	int inhead;
	int i,n;
	char lasteol = FALSE;
	FILE *attach;
	int body_len;
	char *s = mygetenv("CONTENT_LENGTH");
	cl = atoi(s);

	body_len = cl+66000;
	body = malloc(body_len);
	strcpy(body,"");
	strcpy(fname,"");
	attach = NULL;
	s = strstr(content,"boundary=");
	if (s==NULL) {
		/*do_content();*/
		imsg("Can't find boundary in mime contents %s",content);
		strcpy(boundary,"none");
		goto xxx;
	}
	s += 9;
	ncpy(boundary,get_quote(s),BFSZ-1);
	imsg("Got boundary (%s) \n",boundary);
xxx:    lprintf(bound_part,BFSZ-1,"--%s",boundary);
        lprintf(bound_end,BFSZ-1,"--%s--",boundary);
#ifdef FIX_STDOUT
        _setmode(_fileno(stdin),_O_BINARY);
#endif
        for (;(!feof(mystdin)) && (cl>0);) {
			n = 0;
			for (i=0; (i<400) && (!feof(mystdin)) && (cl>0); i++) {
				bf[i] = fgetc(mystdin);
				cl--;
				n = i+1;
				if (bf[i]=='\n') break;
                /*      if ((bf[i]=='\r') && (bf[i]!='\n')) break;*/
				
			}
			bf[n] = 0;
			if (strncmp(bf,bound_part,strlen(bound_part))==0) {
				imsg("Found Boundary %s",bf);
				inhead = TRUE;
				if (attach!=NULL) {
					fclose(attach);
					attach = NULL;
					lprintf(tmp,BFSZ-1,"%s\r\n",attach_fname()); 
					cgi_addvar(name,tmp);
					strcpy(fname,"");
				} else cgi_addvar(name,body);
			} else if (strncmp(bf,bound_end,strlen(bound_end))==0) {
				if (attach!=NULL) {
					fclose(attach);
					strcpy(fname,"");
					attach = NULL;
					lprintf(tmp,BFSZ-1,"%s\r\n",attach_fname()); 
					cgi_addvar(name,tmp);
					strcpy(fname,"");
				} else cgi_addvar(name,body);
				break;
			} else {
				if (inhead) {
					/*      Content-Disposition: form-data; name="reply" */
					imsg("Mime Headers %s",bf);
					if (strcmp(bf,"\r\n")==0) inhead = FALSE;
					if (xisspace(bf[0])) goto skip;
					ncpy(bf2,bf,BFSZ-1); strlwr(bf2);
					if (strstr(bf2,"content-type: ")!=NULL) {
						s = bf + 14;
						ncpy(a_type,s,BFSZ-1);
						s = strchr(a_type,';'); if (s!=NULL) *s = 0;
						s = strchr(a_type,'\r'); if (s!=NULL) *s = 0;
						s = strchr(a_type,'\n'); if (s!=NULL) *s = 0;
					}
					if (strstr(bf2,"content-disposition:")!=NULL) {
skip:                           s = strstr(bf,"name="); if (s==NULL) continue;
                                ncpy(name,get_quote(s+5),BFSZ-1);
                                s = strstr(bf,"filename="); if (s==NULL) continue;
                                ncpy(fname,get_quote(s+9),BFSZ-1);
                                ncpy(a_name,fname,BFSZ-1);
								if ((attach==NULL)&&(strlen(a_name)>0)) {
									attach = fopen(attach_fname(),"wb");
									if (attach==NULL) emsg("Cannot create (%s) (%s)\n",attach_fname(),strerror(errno));
									imsg("Writing to %s %p\n",attach_fname(),attach);
                                }
                                lasteol = FALSE;
					}
					strlwr(bf);
					
				} else {
					if (attach!=NULL) {
						if (lasteol) fwrite("\r\n",1,2,attach);
						lasteol = FALSE;
						if (n>2) if (strncmp(bf+n-2,"\r\n",2)==0) {
							n-=2;
							lasteol = TRUE;
						} 
						
						fwrite(bf,1,n,attach);
					} else if (strlen(name)>0) {
						if (strlen(body)>(body_len-400)) continue;
						strcat(body,bf);
					}
				}
			}
        }
        if (attach!=NULL) fclose(attach);
        attach = NULL;
        imsg("cl = %d\n",cl);
	free(body);         
}


void form_get(void)
{
	int x,cl;
	char content[BFSZ];
	char lwr_content[BFSZ];
	char *s = mygetenv("CONTENT_LENGTH");
	if (s==NULL) return;
	cl = atoi(s);
	
	ncpy(content,mygetenv("CONTENT_TYPE"),BFSZ-1);
	ncpy(lwr_content,content,BFSZ-10);
	imsg("Form content (%s)\n",content);
	strlwr(lwr_content);
	if (strstr(lwr_content,"multipart/form-data")!=NULL) {
		form_get_mime(content);
		return;
	}
	
	for(x=0;cl && (!feof(mystdin));x++) {
		form_val[x]= fmakeword(mystdin,'&',&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;
	return ;
	for (;*s!=0;s++) {
		if ((*s == '\r') && (s[1]!='\n')) *s = '\n';
		if (*s == '\r') 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 *either_find(char *s)
{
	int i;
	for (i=0; i<nform; i++) if (strcmp(form_name[i],s)==0) return form_val[i];
	return query_find(s);
}

char *form_find_i(char *s, int i)
{
	char bf[BFSZ];
	lprintf(bf,BFSZ-1,"%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;
	lprintf(fname,BFSZ-1,"%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;
}
char *file_tpl(char *fname)
{
	char bf[BFSZ];
	static char rslt[BFSZ];
	ncpy(bf,tplname,BFSZ-1);
	if (strlen(bf)>0) strcat(bf,"/");
	
	if (strlen(ini_get(I_TEMPLATES))>0) 
        lprintf(rslt,BFSZ-1,"%s/%s%s",ini_get(I_TEMPLATES),bf,fname);
	else  lprintf(rslt,BFSZ-1,"%s/%s%s",ini_get(I_HEADERS),bf,fname);
#ifdef VMS
	lprintf(rslt,BFSZ-1,"%s%s%s",ini_get(I_TEMPLATES),bf,fname);
#endif
	return rslt;
	
}

int tpl_show_file(char *xfname)
{
	FILE *f;
	char fname[BFSZ];
	char bf[66000];
	
	ncpy(fname,file_tpl(xfname),BFSZ-1);
	
	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;
		tpl_replace_vars(bf);
		printf("%s",bf);
	}       
	fclose(f);
	return TRUE;
}


char *towww(char *s)
{
	static char bf[BFSZ],*out;
	for (out=bf;*s!=0;s++) {
        if (xisspace(*s)) *s = '+';
        if (*s=='+') {sprintf(out,"%%%x",*s); out += strlen(out);}
        else 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 *tohtml(char *s)
{
	static char bf[BFSZ],*out;
	for (out=bf;*s!=0;s++) {
       	if (*s=='<') {sprintf(out,"&lt;",*s); out += strlen(out);}
       	else if (*s=='>') {sprintf(out,"&gt;",*s); out += strlen(out);}
        else *out++ = *s;
	}
	*out++ = 0;
	return bf;
}
char *post_template(char *xfname)
{
	FILE *f;
	char fname[BFSZ];
	char bf[90000];
	static char msg[90000];
	char *s;
	
	ncpy(fname,file_tpl(xfname),BFSZ-1);
	
	tpl_init();
	f = fopen(fname,"r");
	if (f==NULL) {
		printf("Cannot open template {%s}<br>\n",fname);
#ifndef NOSTRERROR
		printf("%s<br>",strerror(errno));       
#endif
		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 next_tpl[BFSZ];
	char bf[66000];
	char *s;
	imsg("tpl_show(%s)\n",xfname);
	do_content();
	
	ncpy(next_tpl,either_find("next_tpl"),200);
	if (strlen(next_tpl)>0) {
		if (strstr(next_tpl,"..")==NULL) 
			if (strstr(next_tpl,"/")==NULL) 
				if (strstr(next_tpl,"\\")==NULL) 
					if (strstr(next_tpl,".tpl")!=NULL) {
						xfname = next_tpl;
					}
	}
	imsg("tpl_show called init (%s)\n",xfname);
	
	tpl_init();
	
	strcpy(fname,file_tpl(xfname));
	
	dmsg("tpl_show called (%s)\n",fname);
	imsg("tpl_show called (%s)\n",fname);
	
	dmsg("tpl_show init done\n");
	f = fopen(fname,"r");
	if (f==NULL) {
		printf("Cannot open {%s} check your templates setting in dnewsweb.ini is correct and that this file is readable by the web server user code \n",fname);
#ifndef NOSTRERROR
		printf("%s<br>",strerror(errno));       
#endif
		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;
		/*imsg("tpl_show (%s)\n",bf);*/
		s = tpl_replace(bf);
		if (strlen(s)>0) printf("%s\n",s);
		
	}
	dmsg("tpl_show done\n");
}
static int tpl_mode;
static char tpl_latest[10000];
static char tpl_item[10000];
static char tpl_dir[10000];
static char* tpl_indent_on[20];
static char* tpl_indent_off[20];
char *tpl_indenton(int i)
{
	if (i<0) return "";
	if (i>9) return "";
	if (tpl_indent_on[i]!= NULL) return tpl_indent_on[i];
	return "";
}
char *tpl_indentoff(int i)
{
	if (i<0) return "";
	if (i>9) return "";
	if (tpl_indent_off[i]!= NULL) return tpl_indent_off[i];
	return "";
}
enum {T_NORM,T_ITEM,T_DIR,T_LIST,T_IF,T_GROUPS,T_GSEARCH,T_GROUP_LIST,T_LATEST};
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,M_LIB);
	tpl_indent_off[n] = mystrdup(s3,M_LIB);
	for (i=n+1; i<10; i++) {
		if (tpl_indent_on[i]==NULL) {
			tpl_indent_on[i] = "";
			tpl_indent_off[i] = "";
		}
	}
	return;
}
char *int_str(int i)
{
	static char bf[BFSZ];
	lprintf(bf,BFSZ-1,"%d",i);
	return bf;
}
void tpl_set_buttons(void)
{
	char bf[BFSZ];
	if (gfrom>group_from) { 
		lprintf(bf,BFSZ-1,"%s?cmd=xover&group=%s&from=%d&utag=%s",path_script,towww(group),gfrom-gmax,utage);
		var_set("b_prev",bf);
		var_set("from_down",int_str(gfrom-gmax));
	}
	imsg("tpl_set_buttons (%d<%d)  (%d>%d)\n",gto,group_to,gfrom,group_from);
	if (gto<group_to && !no_up_button) { 
		lprintf(bf,BFSZ-1,"%s?cmd=xover&group=%s&from=%d&forward=TRUE&utag=%s",path_script,towww(group),gto,utage);
		var_set("b_next",bf);
		var_set("from_up",int_str(gto));
		lprintf(bf,BFSZ-1,"%s?cmd=xover&group=%s&utag=%s",path_script,towww(group),utage);
		var_set("b_recent",bf);
	} 
	if (rootonly) {
		if (origfrom>group_from) {
			lprintf(bf,BFSZ-1,"%s?cmd=xthread&group=%s&from=%d&utag=%s",path_script,towww(group),origfrom,utage);
			var_set("b_tprev",bf);
		}
		if (origto<group_to) {
			lprintf(bf,BFSZ-1,"%s?cmd=xthread&group=%s&from=%d&forward=TRUE&utag=%s",path_script,towww(group),origto,utage);
			var_set("b_tnext",bf);
			lprintf(bf,BFSZ-1,"%s?cmd=xthread&group=%s&utag=%s",path_script,towww(group),utage);
			var_set("b_trecent",bf);
		}
	}
	if (strlen(utage)>0) {
		lprintf(bf,BFSZ-1,"%s?cmd=user&utag=%s",path_script,utage);
	} else {
		lprintf(bf,BFSZ-1,"%s?cmd=list&utag=%s",path_script,utage);
	}
	var_set("b_topgroups",bf);
	lprintf(bf,BFSZ-1,"%s?cmd=listall&utag=%s",path_script,utage);
	var_set("b_allgroups",bf);
	lprintf(bf,BFSZ-1,"%s?cmd=post&group=%s&utag=%s",path_script,towww(group),utage);
	var_set("b_post",bf);
	lprintf(bf,BFSZ-1,"%s?cmd=f_find&group=%s&utag=%s",path_script,towww(group),utage);
	var_set("b_search",bf);
}
void tpl_set_gl(void);

void tpl_init(void)
{
	char bf[BFSZ];
	
	char *ord = user_get("sort");
	
	
	tpl_mode = T_NORM;
	user_read(utag);
	if (strlen(ord)==0) ord = ini_get(I_SORT_ORDER);
	if (strlen(ord)==0) ord = "Thread";
	var_set("sort_order",ord);
	if (strlen(ini_get(I_POP_HOST))>0) var_set("pop_host",ini_get(I_POP_HOST));
	if (isquery) var_set("isquery","TRUE");
	if (strlen(utag)>0 || ini_true(I_LOGGEDIN)) {
		var_set("loggedin","true");
	}
	if (ini_true(I_POP_AUTHENT)) var_set("pop_authent","true");
	imsg("set utag = (%.80s) \n",utag);
	var_set("utag",utag);
	var_set("utage",utage);
	var_set("udom",udom);
	var_set("user",guser);
	if (ini_true(I_TOMAIL)) var_set("tomail","true");
	strcpy(tpl_item,"");
	if (strlen(utage)>0) {
		lprintf(bf,BFSZ-1,"%.200s?cmd=user&utag=%.100s",path_script,utage);
	} else {
		lprintf(bf,BFSZ-1,"%.200s?cmd=list&utag=%.100s",path_script,utage);
	}
	var_set("b_topgroups",bf);
	lprintf(bf,BFSZ-1,"%.200s?cmd=listall&utag=%.200s",path_script,utage);
	var_set("b_allgroups",bf);
	lprintf(bf,BFSZ-1,"%.200s?cmd=xover&utag=%.200s",path_script,utage);
	var_set("b_group",bf);
	lprintf(bf,BFSZ-1,"%s",path_script);
	var_set("b_action",bf);
	lprintf(bf,BFSZ-1," <FONT SIZE=-2> \n <a href=\"http://netwinsite.com/ref.htm\"> DNEWSWEB %s Copyright &copy; <I>NetWin Ltd</I>\n </a></FONT> ",DNEWS_VERSION,path_script);
	var_set("copyright",bf);
	
	ncpy(topname,query_find("top"),BFSZ-1);
	if (strlen(topname)==0) {
		ncpy(topname,form_find("top"),BFSZ-1);
	}
	if (strlen(topname)==0) strcpy(topname,"top.tpl");
	if (strstr(topname,".tpl")==NULL) strcpy(topname,"top.tpl");
	if ((strstr(topname,"..")!=NULL)
		|| (strstr(topname,"/")!=NULL)
		|| (strstr(topname,"\\")!=NULL)) strcpy(topname,"top.tpl");
	var_set("top",topname);
	
	ncpy(tplname,query_find("tpl"),BFSZ-1);
	if (strlen(tplname)==0) {
		ncpy(tplname,form_find("tpl"),BFSZ-1);
	}
	if (strlen(tplname)==0) strcpy(tplname,"");
	if ((strstr(tplname,"..")!=NULL)
		|| (strstr(tplname,"/")!=NULL)
		|| (strstr(tplname,"\\")!=NULL)) strcpy(tplname,"");
	var_set("tpl",tplname);
	ncpy(group,query_find("group"),BFSZ-200);
	var_set("group",value_encode(group));
	if (strlen(group)==0) {
		ncpy(group,form_find("group"),BFSZ-1);
		var_set("group",tohtml(group));
	}
	var_set("groupe",towww(group));
	lprintf(bf,BFSZ-1,"%.200s?cmd=r_subscribe&group=%.200s&utag=%.200s",path_script,towww(group),utage);
	var_set("b_subscribe",bf);
	lprintf(bf,BFSZ-1,"%.200s?cmd=f_find&group=%.200s&utag=%.200s",path_script,towww(group),utage);
	var_set("b_searchgrp",bf);
	var_set("action",path_script);
	if (strlen(ini_get(I_SEARCH))>0) {
		lprintf(bf,BFSZ-1,"%.200s?cmd=f_search&utag=%.200s",path_script,utage);
		var_set("b_search",bf);
	}
	lprintf(bf,BFSZ-1,"%.200s?cmd=post&group=%.200s&utag=%.200s",path_script,towww(group),utage);
	var_set("b_post",bf);
	/* nospamnews */
	if ((strcmp(either_find("cmd"),"post")==0) ||
		(strcmp(either_find("cmd"),"follow")==0) ||
		ispressed("cmd_follow") ||
		ispressed("cmd_post")) {
		if (strlen(utag)==0) {
			if (strlen(ini_get(I_POST_NOT_LOGGED_IN_ACTION))>0) {
				var_set("action",ini_get(I_POST_NOT_LOGGED_IN_ACTION));
			}
		}
	}
	tpl_set_gl();
	
}
/*
Add options like
%%if var_name
xxxx
%%endif
*/

int tpl_get_latest_groups(char *groups)
{
    char bf[BFSZ];
    char temp[BFSZ];
    char *s;
    int i;
    ncpy(bf,groups,BFSZ-10);
    s=strtok(bf,", ");
    if (s!=NULL) {
		lprintf(temp,BFSZ-1,"%.200s?cmd=xover&group=%.200s",path_script,towww(s));
		var_set("primary_group_ref",temp);
		var_set("primary_group",value_encode(s));
		/*printf("primary_group_ref (%s) primary_group (%s)\n",temp,value_encode(s));*/
    }
    for (;s!=NULL;)
    {
		group_add(s);
		s=strtok(NULL,", ");
    }
    return TRUE;
}

char *tpl_replace(char *bf)
{
	char *s,*s2;
	char var[BFSZ]; 
	char head[BFSZ];        
	char list[BFSZ];
	
	static int lvl;
	/*imsg("Calling replace with [%s]\n",bf);*/
	
	switch (tpl_mode) {
	case T_LATEST:
		if (strcmp(bf,"%%end_list%%")==0)
		{
			tpl_mode = T_NORM;
			tpl_show_latest();
		}
		else
			strcat(tpl_latest,bf);
		break;
	case T_IF:
		if ((strncmp(bf,"%%ifdef%%",9)==0) || (strncmp(bf,"%%ifgroup%%",9)==0)|| (strncmp(bf,"%%ifcgi%%",9)==0)) {
			lvl++;
		} else if (strcmp(bf,"%%endif%%")==0) {
			lvl--;
			if (lvl==0) tpl_mode = T_NORM;
		}
		if (lvl==1) if (strcmp(bf,"%%else%%")==0) tpl_mode = T_NORM;
		break;
	case T_NORM:
		if (strncmp(bf,"%%auth_username%%",17)==0) {
			s = bf+17;
			s2 = strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(auth_username,s,s2-s);
			return "";
		}
		else if (strncmp(bf,"%%body_lines%%",14)==0) {
			s = bf+14;
			s2= strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(var,s,s2-s);
			body_lines = atoi(var);
			return ""; 
		} else if (strncmp(bf,"%%items_per_group%%",19)==0) {
			s = bf+19;
			s2 = strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(var,s,s2-s);
			items_per_group = atoi(var);
			return "";
		} else if (strncmp(bf,"%%groups_to_show%%",18)==0) {
			s = bf+18;
			s2 = strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(var,s,s2-s);
			tpl_init();
			tpl_get_latest_groups(var);
			return "";
		} else if (strcmp(bf,"%%begin_list%%")==0) {
			tpl_mode = T_LATEST;
			strcpy(tpl_latest,"");
			return "";
		} else if (strcmp(bf,"%%begin_item%%")==0) {
			tpl_mode = T_ITEM;
			strcpy(tpl_item,"");
			return "";
		} else if (strcmp(bf,"%%begin_groups%%")==0) {
			tpl_mode = T_GROUPS;
			strcpy(tpl_groups,"");
			return "";
		} else if (strcmp(bf,"%%begin_group_list%%")==0) {
			tpl_mode = T_GROUP_LIST;
			strcpy(tpl_groups,"");
			return "";
		} else if (strcmp(bf,"%%begin_gsearch%%")==0) {
			tpl_mode = T_GSEARCH;
			strcpy(tpl_groups,"");
			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) || (strcmp(var_find(var),"false")==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) {
			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,"%%ifcgi%%",9)==0) {
			s = bf+9;
			s2 = strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(head,s,s2-s);
			s = s2+2;
			s2 = strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(list,s,s2-s);
			imsg("ifhead Test (%s) (%s) (%s)\n",list,head,mygetenv(head));
			if (!group_match(list,mygetenv(head))) {
				tpl_mode = T_IF;
				lvl = 1;
				imsg("ENV didn't match  {%s} {%s}\n",list,mygetenv(head));
			} else imsg("ENV matched {%s} {%s}\n",list,mygetenv(head));
		} else if (strncmp(bf,"%%include%%",11)==0) {
			s = bf+11;
			s2 = strstr(s,"%%");
			if (s2==NULL) return bf;
			ncpy(var,s,s2-s);
			tpl_show_file(var);
		} else if ((s=strstr(bf,"%%quote_body%%"))!=NULL) {
			s2=s+14;
			*s=0;
			printf(bf);
			tpl_quote_body();
			printf(s2);
			return "";
		} else if (strcmp(bf,"%%show_item_image%%")==0) {
			tpl_show_item_image();
			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_GROUPS:
			  if (strcmp(bf,"%%end_groups%%")==0) {
				  tpl_mode = T_NORM;
				  tpl_show_groups();
				  return "";
			  }
			  strcat(tpl_groups,bf);
			  break;          
          case T_GROUP_LIST:
			  if (strcmp(bf,"%%end_group_list%%")==0) {
				  tpl_mode = T_NORM;
				  tpl_show_group_list();
				  return "";
			  }
			  strcat(tpl_groups,bf);
			  break;          
          case T_GSEARCH:
			  if (strcmp(bf,"%%end_gsearch%%")==0) {
				  tpl_mode = T_NORM;
				  tpl_show_gsearch();
				  return "";
			  }
			  strcat(tpl_groups,bf);
			  break;          
          case T_DIR:
			  if (strcmp(bf,"%%end_dir%%")==0) {
				  tpl_mode = T_NORM;
				  tpl_show_list();
				  return "";
			  }
			  break;          
        }
        return "";
}
char *get_cookie(char *var)
{
	char *s;
	char *s2;
	static char orig[5001];
	s = mygetenv("HTTP_COOKIE");
	if (strlen(s)==0) s = mygetenv("COOKIE");
	if (strlen(s)==0) {
		imsg("No cookies defined\n");
		return "";
	}
	ncpy(orig,s,5000);
	imsg("Searching cookie (%s) (%s) \n",var,orig);
	s = strtok(orig,";");
	for (;s!=NULL;) {
		for (;*s==' ';) s++;
		s2 = strchr(s,'='); if (s2==NULL) break;
		*s2 = 0;
		if (strcmp(s,var)==0) return s2+1;
		s = strtok(NULL,";");
	}
	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];
	}
	if (strncmpnc(var,"e_",2)==0) {
		return mygetenv(var+2);
	}
	if (strncmpnc(var,"cookie_",7)==0) {
		return get_cookie(var+7);
	}
	return "";
}
void var_set(char *var, char *val)
{
	int i;
	char *s;
	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,M_LIB);
setit:
	var_values[i] = mystrdup(val,M_LIB);
	if (strlen(var_values[i]) > 60000) {
		s = var_values[i];
		s[60000] = 0;
	}
}
char *tpl_replace_vars(char *orig)
{
	char var[BFSZ];
	char *s=orig;
	char *s2,*v;
again:
	s = strstr(s,"%%");
	if (s==NULL) return orig;
	s += 2;
	s2 = strstr(s,"%%");
	if (s2==NULL) return orig;
	ncpy(var,s,s2-s);
	v=var_find(var);
	if(*v==0) { /* Roydon - special variables %%var=group.name%% */
		if(strncmp(var,"nitems:",7)==0) {
			v=tpl_get_nitems(var);
		} else if(strncmp(var,"lastxhdr:",9)==0) {
			v=tpl_get_lastxhdr(var);
		}
	}
	tpl_replace_var(s-2,(s2+2) - (s-2) ,v);
	s -= 2;
	goto again;
}
void tpl_replace_var(char *bf, int len, char *var)
{
	int nlen = strlen(var);    
	memmove(bf+nlen,bf+len,strlen(bf+len)+1);
	strncpy(bf,var,nlen);
}
int ref_find(char *ref)
{       
	int i;
	int j;
	char bf[BFSZ];
	int np;
	char *p[1000];
	ncpy(bf,ref,BFSZ-10);
	np = token_split(bf,p," ",800);
	
	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;
}

static int rev = 1;
/*
Subject sort
Primary number is item number of first matching subject
plus 1/n 
Sort by item number no sort.
primary number = n
*/
enum {SRT_THREAD,SRT_SUBJECT,SRT_DATE};
int srt_order(void)
{
	int o;
	char bf[BFSZ];
	char *ord = user_get("sort");
	user_read(utag);
	if (strlen(ord)==0) ord = ini_get(I_SORT_ORDER);
	ncpy(bf,ord,BFSZ-1); strlwr(bf);
	o = SRT_THREAD;
	if (strcmp(bf,"subject")==0) o = SRT_SUBJECT;
	if (strcmp(bf,"date")==0) o = SRT_DATE;
	return o;
}
char *do_spaces(int z)
{
	static char bf[5000];
	int i;
	if (strlen(ini_get(I_SPACES))==0) {
		memset(bf,' ',z);
		bf[z] = 0;
		return bf;
	}
	strcpy(bf,"");
	for (i=0; (i<z) && (strlen(bf)<3000); i++) {
		strcat(bf,ini_get(I_SPACES));
	}
	return bf;
	
}
int subj_cmp(char *s1, char *s2)
{
	char bf1[BFSZ]; 
	char bf2[BFSZ];
	ncpy(bf1,s1, BFSZ-1);
	ncpy(bf2,s2, BFSZ-1);
	s1 = bf1; s2 = bf2;
	if (strncmpnc(s1,"re: ",4)==0) s1+=4;
	if (strncmpnc(s2,"re: ",4)==0) s2+=4;
	if (strncmpnc(s1,"re:",3)==0) s1+=3;
	if (strncmpnc(s2,"re:",3)==0) s2+=3;
	if (strncmpnc(s1,s2,30)==0) return TRUE;
	return FALSE;
}

void tpl_show_latest(void)
{
    int i;
    char *s,*e;
    char bf[5000];
    char temp[BFSZ];
    char temp2[BFSZ];
    char item[BFSZ];
    int item2;
    int item3;
    char item4[BFSZ];
    
    if (!open_status()) {zmsg("tpl_show_latest1: News host not responding: %s",tohtml(nntp_status)); return;}
    if (strlen(auth_username)>0) {
	/* authenticate using the username defined in latest.tpl.
	e.g. %%auth_username%%fred%% issue xdauthent domain fred tellnews_pass
        */
        if (!do_latest_authent()) {
            zmsg("tpl_show_latest2: News host authentication failed: %s",tohtml(nntp_status));
            return;
        }
    }
    if (items_per_group==0) items_per_group = 1;
    for (i=0;i<ngroups;i++) {
		lprintf(temp,BFSZ-1,"group %s\015\012",groups[i].name);
		nntp_write_s(chan,temp,strlen(temp)); 
		if (!read_status()) {zmsg("tpl_show_latest3: News host not responding: %s",tohtml(nntp_status)); continue;}
		/* get last article number - fourth number in the group response */
		s=strchr(nntp_status,' '); s=strchr(++s,' '); s=strchr(++s,' ');
		e=strchr(++s,' '); if (e!=NULL) *e=0;
		ncpy(item,s,BFSZ-10);
		for(item2=0;item2<items_per_group;item2++) {
			item3 = atoi(item)-item2;
			if (item3<0) {
				imsg("tpl_show_latest: not displaying latest article (%s)(%d)(%d-%d)< %%items_per_group%% items in group\n",groups[i].name,item3,atoi(item),item2);
				break;
			}
			lprintf(temp2,BFSZ-1,"article %d\015\012",item3);
			nntp_write_s(chan,temp2,strlen(temp2)); 
			if (!read_status()) {zmsg("tpl_show_latest4: News host not responding: %s",tohtml(nntp_status)); continue;}
			read_xover();	
			tpl_set_latest_vars();
			
            /* set variables involving group/item numbers */
            lprintf(temp,BFSZ-1,"%s?cmd=xover&group=%s&utag=%s",path_script,towww(groups[i].name),utage);
            var_set("group_ref",temp);
			var_set("group",value_encode(groups[i].name));
			var_set("groupe",towww(groups[i].name));
			lprintf(item4,BFSZ-1,"%d",item3);
            var_set_log("item",value_encode(item4),"five");
            lprintf(temp,BFSZ-1,"%s?cmd=article&group=%s&item=%s&utag=%s",path_script,towww(groups[i].name),value_encode(item4),utage);
            var_set("item_ref",temp);
			
			ncpy(bf,tpl_latest,6000);
			s = tpl_replace_vars(bf);
			printf("%s",s);
		}
    }
}

char *show_kbytes(char *s)
{
	static char bf[BFSZ];
	int x = atoi(s);
	if (x>1000000) lprintf(bf,20,"%.1fM",(double) x/1000000.0);
	else if (x>10000) lprintf(bf,20,"%.0fk",(double) x/1000.0);
	else if (x>1000) lprintf(bf,20,"%.1fk",(double) x/1000.0);
	else lprintf(bf,20,"%.2fk",(double) x/1000.0);
	return bf;
}
void tpl_show_group(void)
{
	int p[9000]; 
	char bf[60000];
	char bf2[60000];
	char *s;
	int repeat;
	int nloop=0;
	char subj[BFSZ];
	int j;
	int i2;
	int z;
	int depth,d,newdepth;
	int i,ii;
	double px,py;   
	int ord = srt_order();
	int srtsubj=FALSE;
	
	if (strcmp("true",ini_get(I_SORT_SUBJ))==0) srtsubj = TRUE;
	
	rev = -1;
	if (strcmp("false",ini_get(I_SORT_REV))==0) rev = 1;
	if (nitem>=MAX_ITEMS) nitem = MAX_ITEMS-10;
	
	imsg("Show_group %d items\n",nitem);
	for (i=0; i<nitem; i++) p[i] = i;
	for (i=0; i<nitem; i++) x_f[i] = -9999;
	if (ord==SRT_SUBJECT) {
		for (i=0; i<nitem; i++) {
			x_f[i] = i*rev;
			if (srtsubj) {
				char *x;
				double z;
				double tot=0;
				x = x_subject[i];
				if (strncmpnc(x,"re: ",4)==0) x += 4;
				for (z=.001; *x!=0; x++,z*=10) tot += tolower(*x)/z;
				x_f[i] = tot;
			}
			//				} else {
			
			if (strlen(x_ref[i])>0) {
				x_depth[i] = ref_depth(x_ref[i]);
				/*imsg("depth[%d] = %d\n",i,x_depth[i]);*/
			} 
			for (j=0; j<i; j++) {
				if (subj_cmp(x_subject[j],x_subject[i])) {
					if (rev<0) {
						x_f[i] = x_f[j]+1.0-(1.0/(i+1));
					} else {
						x_f[i] = x_f[j]+(1.0/(i+1));
					}
					/*imsg("subject: x_f[%d] = %g\n",i,x_f[i]);*/
					break;
				}
			}
			//				}
		}
		goto dosort;
	}
	if (ord==SRT_DATE) {
		for (i=0; i<nitem; i++) {
			x_f[i] = rev*i;
		}
		if (ini_true(I_SORT_POST_DATE)) {
			for (i=0; i<nitem; i++) {
				x_f[i] = rev * date_tonum(x_date[i]);
			}
		}
		goto dosort;
	}
	
	imsg("Show_group_2 %d items\n",nitem);
	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] = rev*i;
		} else x_f[i] = rev*i;
		/* dmsg("[%d] points to %d\n",i,x_fi[i]);*/
	}
	imsg("Show_group_3 %d items\n",nitem);
	for (repeat=TRUE;repeat && (nloop<10);nloop++) {
		repeat = FALSE;
		for (i=0; i<nitem; i++) {
			if (x_f[i] == -9999) {
				py = x_depth[i];
				if (py<0) py = 0;
				if (py>7) py = 7;
				px = pow(40,py);
				if (px<=1) px = 1;
				if (x_f[x_fi[i]] == -9999) repeat = TRUE;
				else {
					if (px==0) px = 1;      
					x_f[i] = x_f[x_fi[i]] + ((double) i)/(double) px;
					if (x_f[i] > pow(40,8)) x_f[i] = pow(40,8);
				}
				/* 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]))*/
			} 
		}
	}
dosort:
	imsg("Show_group_5 %d items\n",nitem);
	if (!search_result) {
		if (nitem>2) qsort(p,nitem,sizeof(int),xover_cmp);
		printf("%s",tpl_indenton(1));
	}
	depth = 1;
	imsg("Show_group_6 %d items\n",nitem);
	for (i=0; i<nitem; i++) {
		ii = p[i];
		imsg("Loop %d %d \n",i,ii,x_item[ii]);
		
		if (i>0) i2 = p[i-1];
		if (search_result) goto skip;
		
		newdepth = ref_depth(x_ref[ii]);
		if (newdepth<2) {
			if (strncmpnc(x_subject[ii],"re:",3)==0) newdepth=2;
		}
		if (i==0) newdepth = 1;
		if (newdepth!=1) {
			if (rev) {
				if (((int) (x_f[ii]-0.99999999)) != ((int) (x_f[i2]-0.99999999))) {
					newdepth = 1;
				}
			} else {
				if (((int) x_f[ii]) != ((int) x_f[i2])) {
					newdepth = 1;
				}
			}
		}
		if (newdepth>9) newdepth = 9;
		for (d = depth+1; d <= newdepth; d++) {
			printf("%s",tpl_indenton(d));
		}
		for (d = depth; d > newdepth; d--) {
			printf("%s",tpl_indentoff(d));
		}
		depth = newdepth;
skip:   
		if (x_group[ii]!=NULL) if (strlen(x_group[ii])>0) {
			ncpy(bf2,x_group[ii],BFSZ-1);
			s = strchr(bf2,':');
			if (s!=NULL) {
				*s = 0; s++;
				strcpy(group,bf2);
				x_item[ii] = atoi(s);
				goto skip2;
			}
		}
skip2:                  
		var_set("h_group",value_encode(group));
		lprintf(bf,60000-1,"%s?cmd=article&group=%s&item=%d&utag=%s",path_script,towww(group),x_item[ii],utage);
		var_set("b_item",bf);
		
		var_set("item_n",pad_right(int_str(x_item[ii]),3));
		if (strlen(x_subject[ii])==0) var_set("h_subject",value_encode("(No subject)"));
		else var_set("h_subject",value_encode(x_subject[ii]));
		/*sprintf(bf,"%g %s",x_f[ii],value_encode(x_subject[ii]));
		var_set("h_subject",bf);
		*/
		if (depth>1) {
			z = (depth-1) * 1;
			if (z>10) z=10;
			memset(bf,'-',z);
			bf[z] = '>';
			bf[z+1] = 0;
			var_set("arrows",value_encode(bf));
		} else var_set("arrows","");
		if (depth>1) {
			z = (depth-1) * 1 + 1;
			if (ini_true(I_NO_DOUBLE_SPACE)) z--; 
			if (z>10) z=10;                        
			var_set("spaces",do_spaces(z));
		} else var_set("spaces","");
		
		var_set("h_from",value_encode(x_from[ii]));
		var_set("h_message-id",value_encode(x_mid[ii]));
		var_set("h_lines",x_lines[ii]);
		var_set("h_bytes",x_bytes[ii]);
		var_set("h_kbytes",show_kbytes(x_bytes[ii]));
		var_set("h_score",x_score[ii]);
		var_set("h_date",value_encode(x_date[ii]));
		var_set("h_date_trim",value_encode(h_date_trim(x_date[ii])));
		/* brian123*/
		var_set("h_date_numeric",value_encode(h_date_numeric(x_date[ii])));
		var_set("h_from_trim",value_encode(h_from_trim(x_from[ii])));
		var_set("h_from_email",value_encode(h_from_email(x_from[ii])));
		var_set("h_from_hide",value_encode(h_from_hide(x_from[ii])));
		ncpy(subj,x_subject[ii],BFSZ-1);
		if (strcmp(subj,"")==0) strcpy(subj,"(No subject)");
		var_set("h_subject_60",value_encode(h_trim(subj,60)));
		var_set("h_subject_40",value_encode(h_trim(subj,40)));
		var_set("h_subject_30",value_encode(h_trim(subj,30)));
		if (rootonly) {
			var_set("titems",int_str(x_titems[ii]));
			lprintf(bf,60000-1,"%s?cmd_related=Related+Items&viewitems=true&group=%s&xrelated=%d&rangefrom=%s&rangeto=%s&utag=%s",path_script,towww(group),x_item[ii],var_find("rangefrom"),var_find("rangeto"),utage);
			var_set("b_thread",bf);
		}
		
		if (atoi(x_bytes[ii]) >atoi(ini_get(I_ISBINARY_SIZE))) {
			var_set("isbinary",ini_get(I_ISBINARY_IMG));
			var_set("istext","");
		} else {
			var_set("istext",ini_get(I_ISTEXT_IMG));
			var_set("isbinary","");
		}
        strcpy(bf,tpl_item);
        if (!group_allow_silent(group)) continue;
		
		if (viewitems) {
			char *s2;
			int it;
			s2=strstr(bf,"%%show_item%%");
			if (s2!=NULL) {
				/*herenow*/
				*s2=0;
				s2+=13;
				ncpy(bf2,s2,59990);
				s = tpl_replace_vars(bf);
				printf("%s",s);
				it=x_item[ii];
				var_set("item",int_str(it));
				var_set("group",value_encode(group));
				var_set("groupe",towww(group));
				r_article();
				tpl_show_item();
				s = tpl_replace_vars(bf2);
				printf("%s\n",s);
			}
		} else {
			s = tpl_replace_vars(bf);
			printf("%s\n",s);
		}
    }
    imsg("Show_group done\n");
    if (search_result) return;
    for (d = depth; d > 1; d--) {
		printf("%s",tpl_indentoff(d));
    }
    printf("%s",tpl_indentoff(1));
}

char *h_from_email(char *orig)
{
	static char bf[BFSZ];
	char *s;
	ncpy(bf,orig,BFSZ-1);
	s = strtok(bf," \t<>(){}\"");
	for (;s!=NULL;) {
		if (strchr(s,'@')!=NULL) return s;
		s = strtok(NULL," \t<>(){}\"");
	}
	return orig;
}
/*
x y <a@b>       = x y
a@b             = a
a@b (name)      = name
*/
char *h_replace(char *xbf, char c, char *with)
{
    char out[BFSZ];
    char *o;
    char *bf=xbf;
	
    for (o=out;*bf!=0;bf++) {
		if (o-out>BFSZ-10) break;
		if (*bf==c) {
			strcpy(o,with);
			o+= strlen(o);
		} else {
			*o++ = *bf;
		}
    }
    *o = 0;
    strcpy(xbf,out);
    return xbf;
}

char *h_from_hide(char *s)
{
    char tmp[BFSZ];
    static char tmp2[BFSZ];
    ncpy(tmp,h_from_email(s),200);
    h_replace(tmp,'.'," dot ");
    h_replace(tmp,'@'," at ");
    ncpy(tmp2,tmp,BFSZ-1);
    return tmp2;
}

char *h_from_trim(char *s)
{
	static char bf[BFSZ];
	char *s2;
	ncpy(bf,s,BFSZ-1);
	/* 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 (!xisspace(*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;
		}
	}
	if (strchr(bf,'@')==NULL) return bf;
	
	/*a@b*/
	for (s=bf;*s!=0; s++) if (!xisspace(*s)) break;
	for (s2=s;*s2!=0;s2++) if (xisspace(*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++;
	ncpy(bf,s,BFSZ-1);
	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;
}

char* month_n(char *month) {
    char bf[BFSZ];
    char *m;
	
    ncpy(bf,month,10);
    strlwr(bf);
    m="1";
    if (strncmp(month,"jan",3)==0) m="1";
    else if (strncmp(bf,"feb",3)==0) m="2";
    else if (strncmp(bf,"mar",3)==0) m="3";
    else if (strncmp(bf,"apr",3)==0) m="4";
    else if (strncmp(bf,"may",3)==0) m="5";
    else if (strncmp(bf,"jun",3)==0) m="6";
    else if (strncmp(bf,"jul",3)==0) m="7";
    else if (strncmp(bf,"aug",3)==0) m="8";
    else if (strncmp(bf,"sep",3)==0) m="9";
    else if (strncmp(bf,"oct",3)==0) m="10";
    else if (strncmp(bf,"nov",3)==0) m="11";
    else if (strncmp(bf,"dec",3)==0) m="12";
    
    return m;
}

/* Tue, 3 Jan 98 23:33:33 GMT */
/* 3.1.98 23:33 */
char *h_date_numeric(char *instr)
{
    
    char bf[BFSZ];
    char *bf2;
    static char numdate[BFSZ];
    char stuff[8][100];
    int i;
    bf2 = strchr(instr,',');
    if (bf2==NULL) bf2 = instr; else bf2++;
    ncpy(bf,bf2,100);
    ncpy(numdate,"date",10);
    for(i=0;i<8;i++) ncpy(stuff[i],"",90);
    bf2=strtok(bf," ,:");
    for(i=0;i<6;i++) {
		if (bf2==NULL) break;
		ncpy(stuff[i],bf2,90);
		bf2=strtok(NULL," ,:");
    }
    lprintf(numdate,BFSZ-1,"%s.%s.%s %s:%s",stuff[0],month_n(stuff[1]),stuff[2],stuff[3],stuff[4]);
    return numdate;    
}
int date_tonum(char *instr)
{
    
    char bf[BFSZ];
    char *bf2;
    static char numdate[BFSZ];
    int year,mon,day,hour,min;
    char stuff[8][100];
    int i;
    bf2 = strchr(instr,',');
    if (bf2==NULL) bf2 = instr; else bf2++;
    ncpy(bf,bf2,100);
    ncpy(numdate,"date",10);
    for(i=0;i<8;i++) ncpy(stuff[i],"",90);
    bf2=strtok(bf," ,:");
    for(i=0;i<6;i++) {
		if (bf2==NULL) break;
		ncpy(stuff[i],bf2,90);
		bf2=strtok(NULL," ,:");
    }
    lprintf(numdate,BFSZ-1,"%s.%s.%s %s:%s",stuff[0],month_n(stuff[1]),stuff[2],stuff[3],stuff[4]);
	
    day = atoi(stuff[0]);
    mon = atoi(month_n(stuff[1]));
    year = atoi(stuff[2]);
    hour = atoi(stuff[3]);
    min = atoi(stuff[4]);
	
    if (year>200) year -= 1900;
    if (year<50) year += 100;
    year -= 70;
    
	/*    imsg("sort (%s) (%d) \n",instr, year*365*60*24 + mon*31*60*24 + day*60*24 + hour*60 + min);
    imsg("year (%d) mon (%d) day (%d) \n",year,mon,day);
    imsg("year (%d) mon (%d) day (%d)  (%s)\n",year,mon,day,instr);
    */
    return year*365*60*24 + mon*31*60*24 + day*60*24 + hour*60 + min;
}

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

void show_raw(void)
{
    int item = atoi(query_find("item"));
    imsg("show_raw called doing content\n");
    printf("Content-type: text/plain\n\n");
    printf("%s",str_s(instr));
    imsg("tpl_show_raw(%d)\n",str_len(instr));
}

void show_article(void)
{ 
	int part = atoi(query_find("part"));
	if (part>0) { 
		imsg("Show article part\n");
		show_article_part(part);        
		imsg("Show article part done\n");
		return;
	}
	tpl_init_item();
	
	if (strlen(utag)>0) {
		if (islogin()&&(strcmp(guser,authenticated_user)==0)) var_set("cancel","cancel");
	}
	if (!viewitems) tpl_show("item.tpl");
}

int get_body_n(char *dst, int n) {
	char *s;
	*dst=0;
	if (instr==NULL) return FALSE;
	s = str_s(instr);
	s += gupto;
	if (*s=='\r') s++;
	if (*s=='\n') s++;
	if (s==NULL) return FALSE;
	ncpy(dst,s,n);
	return TRUE;
}
void var_set_log(char *var, char *val, char *info)
{
	imsg("var_set(%s=%s)  %s\n",var,val,info);
	var_set(var,val);
}	
char *trim_quotes(char *s)
{
	static char bf[BFSZ];
	char *out=bf;
	for (;*s!=0;s++) {
		if (*s=='"') continue;
		*out++ = *s;
	}
	*out++ = 0;
	return bf;
}
static char boundary[BFSZ];
static int isimage = FALSE;
static char mime[BFSZ];
static char xhtml[BFSZ];
static char encoding[BFSZ];
static char i_subject[BFSZ];
static char i_content[BFSZ];
void get_attach_name(char *in);
static char attach_name[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 mid[BFSZ];
	static char organization[BFSZ];
	static char content[BFSZ];
	static char nntpposthost[BFSZ];
	static char body200[BFSZ];
	char bf[BFSZ];
	char *s2;
	int gothead = FALSE;
	int multi,got,istext;
	char newsgroups[BFSZ];
	char newsgroups_space[BFSZ];
	char temp[BFSZ];
	char *i;
	char *p;
	char varh[BFSZ];
	char head[BFSZ];
	char var[BFSZ];
	char header[BFSZ];
	int temp_len;
	char tmp2[BFSZ];
	int related;
	
	int item = atoi(query_find("item"));
	if (viewitems) item=atoi(var_find("item"));
	related = item;
	
	if (b_item>=0) item = related = b_item;
	
	
	multi = got = istext = FALSE;
	strcpy(subject,"");
	strcpy(from,"");
	strcpy(date,"");
	strcpy(organization,"");
	strcpy(newsgroups,"");
	strcpy(newsgroups_space,"");
	strcpy(nntpposthost,"");
	strcpy(body200,"");
	related = item;
	
	if (docache) printf("Pragma: cache\n");
	if (!viewitems) printf("Content-type: %s\n\n",content_type());
	done_content = TRUE;
	strcpy(attach_name,"attach.jpg");
	var_set("group",value_encode(group));
	var_set("groupe",towww(group));
	var_set_log("item",int_str(item),"seve");
	lprintf(bf,BFSZ-1,"%s?cmd=xover&group=%s&related=%d&utag=%s",path_script,towww(group),related,utage);
	var_set("b_related",bf);
	lprintf(bf,BFSZ-1,"%s?cmd=xover&group=%s&utag=%s",path_script,towww(group),utage);
	var_set("b_recent",bf);
	lprintf(bf,BFSZ-1,"%s?cmd=xthread&group=%s&utag=%s",path_script,towww(group),utage);
	var_set("b_trecent",bf);
	if (strlen(utage)>0) {
		lprintf(bf,BFSZ-1,"%s?cmd=user&utag=%s",path_script,utage);
	} else {
		lprintf(bf,BFSZ-1,"%s?cmd=list&utag=%s",path_script,utage);
	}
	var_set("b_topgroups",bf);
	lprintf(bf,BFSZ-1,"%s?cmd=listall&utag=%s",path_script,utage);
	var_set("b_allgroups",bf);
	lprintf(bf,BFSZ-1,"%s?cmd=post&group=%s&utag=%s",path_script,towww(group),utage);
	var_set("b_post",bf);
	lprintf(bf,BFSZ-1,"%s?cmd=follow&group=%s&item=%d&utag=%s",path_script,towww(group),item,utage);
	var_set("b_follow",bf);
	lprintf(bf,BFSZ-1,"%s?cmd=raw&group=%s&item=%d&utag=%s",path_script,towww(group),item,utage);
	var_set("item_raw",bf);
	
	if (!gothead) s = get_line_head(instr);
	else s = get_line(instr);
	for (;s!=NULL;) {
		if (strcmp(s,"")==0) {
			get_body_n(body200,200);
			break;
		};
		/* check for a var_header setting - if present then set variables from the
		from the specified headers
		*/
		if (strlen(ini_get(I_VAR_HEADER))>0) {                
			ncpy(varh,ini_get(I_VAR_HEADER),BFSZ-10);
			for(p=strtok(varh,", ");p!=NULL;p=strtok(NULL,", ")) {
				ncpy(head,p,BFSZ-10);
				if (p==NULL) break;
				ncpy(var,p,BFSZ-10);
				if (strlen(head)<=0) continue;
				if (strlen(var)<=0) continue;
				if (strchr(head,':')==NULL) continue;
				if (strlen(var_find(var))>0) {
					imsg("found existing var (%s)\n",var_find(var));
					continue;
				}
				strlwr(head);
				strcpy(header,"");
				head_get(head,header,s);
				if (strlen(header)<=0) continue;
				s2= strchr(var,':'); if (s2!=NULL) *s2 = 0;
				lprintf(tmp2,BFSZ-1,"h_%s",var);
				imsg("setting template variable from message header variable='%.200s' header='%.200s' value='%.200s'\n",tmp2,head,header);
				var_set(tmp2,header);
			}
		}
		head_get("subject:",subject,s);
		if (ini_true(I_NON_ASCII)) ncpy(subject,rfc1522s(subject),BFSZ-10);
		head_get("date:",date,s);
		head_get("message-id:",mid,s);
		head_get("from:",from,s);
		if (ini_true(I_NON_ASCII)) ncpy(from,rfc1522s(from),BFSZ-10);
		head_get("organization:",organization,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);
		head_get("nntp-posting-host:",nntpposthost,s);
		head_get("x-authenticated-user:",authenticated_user,s);
		get_attach_name(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;
		}
		if (!gothead) s = get_line_head(instr);
		else s = get_line(instr);
	}
	
	/* variable newsgroups_space with comma followed by space as newsgroup delimter */
	ncpy(temp,newsgroups,BFSZ-100);
	*newsgroups_space=0;
	temp_len=0;
	for(i=strtok(temp,",");i!=NULL;) {
		temp_len+=strlen(i);
		if (temp_len>BFSZ-100) break;
		ncat(newsgroups_space,i,BFSZ-100);
		if (temp_len>BFSZ-300) break;
		if ((i=strtok(NULL,","))!=NULL) { strcat(newsgroups_space,", "); temp_len+=2;}
	}
	var_set("h_from",value_encode(from));
	var_set("h_from_trim",value_encode(h_from_trim(from)));
	var_set("h_from_hide",value_encode(h_from_hide(from)));
	var_set("h_from_email",value_encode(h_from_email(from)));
	var_set("h_from_noquotes",value_encode(trim_quotes(from)));
	var_set("h_references",eoltospace(value_encode(ref)));
	var_set("h_subject",value_encode(subject));
	if (strlen(subject)==0) var_set("h_subject",value_encode("(No subject)"));
	
	var_set("h_organization",value_encode(organization));
	var_set("h_date",value_encode(date));
	/* 29.09.1999 13:35 brian123*/
	var_set("h_date_numeric",value_encode(h_date_numeric(date)));
	var_set("h_message-id",value_encode(mid));
	var_set("h_newsgroups",value_encode(newsgroups));
	var_set("h_newsgroups_space",value_encode(newsgroups_space));
	var_set("h_content-type",value_encode(content));
	ncpy(i_subject,subject,300);
	ncpy(i_content,content,300);
	var_set("h_nntp-posting-host",value_encode(nntpposthost)); /* brianabc */
	var_set("body200",value_encode(body200));
	strlwr(content);
	if (strlen(xhtml)==0) if (strstr(content,"text/html")!=NULL) ncpy(xhtml,content,BFSZ-1);
}

void tpl_set_latest_vars(void)
{
	char *s,*x;
	char subject[BFSZ];
	static char from[BFSZ];
	static char date[BFSZ];
	static char ref[BFSZ];
	static char mid[BFSZ];
	static char organization[BFSZ];
	static char content[BFSZ];
	static char body[11000];
	int lines = 0;
	int body_total = 0;
	char bf[BFSZ];
	char *s2;
	int gothead = FALSE;
	char newsgroups[BFSZ];
	
	strcpy(subject,"");
	strcpy(from,"");
	strcpy(date,"");
	strcpy(organization,"");
	strcpy(newsgroups,"");
	strcpy(body,"");
	
	if (!gothead) s = get_line_head(instr);
	else s = get_line(instr);
	for (;s!=NULL;) {
		if (strcmp(s,"")==0) gothead=TRUE; /*bazil*/
		if (!gothead) {
			head_get("newsgroups:",group,s);
			head_get("subject:",subject,s); /* Removed trailing space - breaks things */
			if (ini_true(I_NON_ASCII)) ncpy(subject,rfc1522s(subject),BFSZ-10);
			head_get("date:",date,s);
			head_get("message-id:",mid,s);
			head_get("from:",from,s);
			if (ini_true(I_NON_ASCII)) ncpy(from,rfc1522s(from),BFSZ-10);
			head_get("organization:",organization,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);
		}
		else {
			body_total += strlen(s);
			if (body_total>10000) break;
			if (body_lines>0) if (lines++>body_lines) {
				if (ini_true(I_FORMAT_HTML))
					strcat(body,"...<br>");
				else
					strcat(body,"...\n");
				break;
			}
			strcat(body,show_text_fn(s));
		}
		if (!gothead) s = get_line_head(instr);
		else s = get_line(instr);
	}
	var_set("h_group",value_encode(group));
	var_set("h_from",value_encode(from));
	var_set("h_from_trim",value_encode(h_from_trim(from)));
	var_set("h_from_hide",value_encode(h_from_hide(from)));
	var_set("h_from_email",value_encode(h_from_email(from)));
	var_set("h_references",eoltospace(value_encode(ref)));
	var_set("h_subject",value_encode(subject));
	if (strlen(subject)==0) var_set("h_subject",value_encode("(No subject)"));
	var_set("h_organization",value_encode(organization));
	var_set("h_date",value_encode(date));
	/* brian 12/10/99 */
	var_set("h_date_trim",value_encode(h_date_trim(date)));
	var_set("h_message-id",value_encode(mid));
	var_set("h_newsgroups",value_encode(newsgroups));
	var_set("h_content-type",value_encode(content));
	strlwr(content);
	if (strlen(xhtml)==0) if (strstr(content,"text/html")!=NULL) ncpy(xhtml,content,BFSZ-1);
	var_set("body",body);
}

char *quote_encode(char *s);
void tpl_quote_body(void)
{ 
	char *s;
	int skip_encoding = 0;
	int skip_next = 0;
	int uusection = 0;
	int yencsection = 0;
	
	if (instr==NULL) return;
	s = strchr(from,'\015'); 
	if (s!=NULL) *s = 0;
	
	if (ini_true(I_NON_ASCII)) printf("%s wrote:\n",rfc1522s(from));
	else printf("%s wrote:\n",from);	
	
	if (*ini_get(I_KEEP_FORMATING) == 't'){
		s = str_s(instr);
		if (s==NULL) return;
		s+=gupto;
		JLLformat_wrap_output(JLLcomment_write_s, s, atoi(ini_get(I_WRAP_TEXT)));
	}else{
		s = get_line(instr);
		for (;s!=NULL;) {
			if (skip_next) { skip_next = 0; goto skipit; };
			if (strncmpnc(s,"Content-Transfer-Encoding: base64",33)==0) skip_encoding = 1;
			if (skip_encoding>0) if (strlen(s)==1) skip_encoding++;
			if (skip_encoding>2) skip_encoding = 0;
			if (skip_encoding==1) goto skipit;                        
			if (s[0]=='M') if ((strlen(s)==62)||(strlen(s)==61)) goto skipit;
			/* check for 'begin xxx ' where x is a digit 0-9 */
			if (strncmpnc(s,"begin ",6)==0) 
				if (isdigit(s[6])&&isdigit(s[7])&&isdigit(s[8])&&(s[9]==' ')) uusection = TRUE;
				if (uusection) if (strcmpnc(s,"end")==0) { uusection = FALSE; goto skipit; }
				if (uusection) goto skipit;
				/* RL - check for yEnc section */
				if (strncmpnc(s,"=ybegin ",8)==0) yencsection = TRUE;
				if (yencsection) if (strncmpnc(s,"=yend",5)==0) { yencsection = FALSE; goto skipit; }
				if (yencsection) goto skipit;
				/* brian - skip some of the common MIME headers */
				if ((strncmpnc(s,"Content-Transfer-Encoding:",26)==0) ||
					(strncmpnc(s,"Content-Type:",13)==0) ||
					(strncmpnc(s,"Content-Disposition:",20)==0))
				{
					if (s[strlen(s)-1]==';') skip_next = 1;
					goto skipit;
				};
				if (strncmpnc(s,"This is a multi-part message in MIME format.",44)==0) goto skipit;
				/* if this is mime then skip the boundaries as detected in show_article1() */
				if (strlen(mimeboundary)!=0) if (strstr(s,mimeboundary)!=NULL) goto skipit;                        
				/* brian - removed rfc1522 encoding only applies in headers
				if (ini_true(I_NON_ASCII)) printf("%s",rfc1522s(quote_encode(s)));
				else*/
				printf("%s\n",quote_encode(s));
skipit:                 s = get_line(instr);
		}
	}
}
char *quote_encode(char *s)
{
	static char bf[4000];
	int i;
	char *out=bf;
	int len;
	int w = atoi(ini_get(I_WRAP_TEXT));
	w -= 8;
	if (w<=0) w = 1000;
	len = 0;
	*out++ = '>';
	for (i=0; (i<2000) && (*s!=0);i++,s++,len++) {
		if (*s=='\n') len = 0;
		if (*s=='\r') len = 0;
		if (len>w) {
			if (xisspace(*s)) {
				*out++ = '\n';
				*out++ = '>';
				len = 0;
				continue;
			}
		} 
		*out++ = *s;
	}
	*out++ = 0;
	return bf;
}


/*
For clarinet *.web groups

  <img src='*clari.net  --> <img src='news:*clari.net
  <a href='*clari.net'  --> <a href='news:*clari.net'
*/
char *str_sanitize(char *s) /* Remove spaces and unsavory macintosh crap */
{
	static char bf[BFSZ];
	char *out=bf;
	for (;*s!=0;s++) {
		if (isalnum(*s) || (*s=='.') || (*s=='_')) *out++ = *s;
		if (*s=='/') out=bf;
		if (*s=='\\') out=bf;
	}
	*out++ = 0;
	return bf;
}
void get_attach_name(char *in)
{
	char *s;
	if (strncmp(in,"content-",8)!=0) return;
	s = strstr(in,"name="); if (s==NULL) return;
	imsg("get attach processing %s\n",s);
	s += 5;
	for (;*s!=0;s++) if (!xisspace(*s)) break;
	if (*s=='"') s++;
	ncpy(attach_name,s,BFSZ-1);
	s = strchr(attach_name,'"'); 
	if (s!=NULL) *s = 0;
	else {
		s = strchr(attach_name,' '); if (s!=NULL) *s = 0;
	}
	ncpy(attach_name,str_sanitize(attach_name),BFSZ-1);
	if (strlen(attach_name)==0) strcpy(attach_name,"noname.jpg");
	imsg("get attach set %s\n",attach_name);
}
char *savename(char *s)
{
	if (ini_true(I_REMOVE_NAME)) return "";
	return s;
}
char *clari_encode(char *ss);
void tpl_show_item_image(void)
{
	int item = atoi(query_find("item"));
	if (b_item>=0) item = b_item;
	
	strlwr(encoding);
	printf("<img src=\"%s/%s?cmd=article&group=%s&item=%d&part=200&utag=%s\"><br>",path_attach,savename(attach_name),towww(group),item,utage);
}
static int nbound;
static char *bound[200];

void boundary_add(char *s)
{
	char *s2;
	char *start;
	char bf[BFSZ];
	s2 = strstr(s,"boundary=");
	if (s2==NULL) s2 = strstr(s,"Boundary=");
	if (s2==NULL) s2 = strstr(s,"BOUNDARY=");
	if (s2!=NULL) {
		start = s2;
		strcpy(bf,"--");
		s2 = strchr(s2,'"');
		if (s2!=NULL) {
			ncpy(bf+2,s2+1,BFSZ-10);
			s2 = strchr(bf,'"');
			if (s2!=NULL) *s2 = 0;
		} else {
			ncpy(bf+2,start+9,BFSZ-10);
			s2 = strchr(bf,';');
			if (s2!=NULL) *s2 = 0;
			s2 = strchr(bf,' ');
			if (s2!=NULL) *s2 = 0;
		}
		bound[nbound++] = strdup(bf);
		imsg("Boundary added (%s)\n",bf);
	}
}
static int endbound;
int boundary_check(char *in)
{
	
	int i;
	endbound = FALSE;
	for (i=0; i<nbound; i++) {
		if (strncmp(in,bound[i],strlen(bound[i]))==0) {
			if (strncmp("--",in+strlen(bound[i]),2) ==0) {
				imsg("end of boundary\n");
				endbound = TRUE;
			} else {
				imsg("Boundary matched (%s) \n",bound[i]);
			}
			return TRUE;
		}
	}
	return FALSE;
}

void tpl_show_item(void)
{
	char *temp;
	int uucount,ycount;
	int uucontent = FALSE; /* brian */
	static int multi;
	char *ss;
	int istext = FALSE;
	int ishtml = FALSE;
	char *x;
	int ismsg = FALSE;
	int gothead = FALSE;
	int isquoted=FALSE;
	int isref = FALSE;
	char *z;
	int isbase64=FALSE;
	char bf[BFSZ];
	int got;
	char *s;
	int item = atoi(query_find("item"));
	char *s2;
	char subject[BFSZ];
	int parti,partmax=0;
	char fname[BFSZ];
	int first_line= TRUE;
	int r;
	int israw = FALSE;
	char custom_href[BFSZ];
	char *t;
	int isbase64text=FALSE;
	int isvcard = FALSE;
	int isyenc = FALSE;
	char b0[BFSZ];
	char b1[BFSZ];
	char b2[BFSZ];
	char b3[BFSZ];
	
	if (viewitems) item=atoi(var_find("item"));    
	/* get any cutomisation to put in the <a href's */
	ncpy(custom_href,"",0);
	if (strlen(ini_get(I_LINK_ADD))>0) {
		t=strchr(ini_get(I_LINK_ADD),'%');
		if (t==NULL) lprintf(custom_href,BFSZ-1," %.500s",ini_get(I_LINK_ADD));
		else emsg("tpl_show_item: dnewsweb.ini LINK_ADD setting invalid remove %%\n");
	}
	
	if (strstr(content_type(),"text")!=NULL) isbase64text=TRUE;
	
	if (b_item>=0) item = b_item;
	
	if (instr==NULL) return;
	imsg("tpl_show_item(%d)\n",str_len(instr));
	strlwr(encoding);
	imsg("show item encoding (%s)\n",encoding);
	if (strstr(encoding,"quoted")!=NULL) isquoted = TRUE; /*Added by chrisp */
	imsg("isquoted %d\n",isquoted);
	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) {
		/* not all that is base64 coded is an image! */
		if (!isbase64text) {
			printf("<img src=\"%s/%s?cmd=article&group=%s&item=%d&part=99&utag=%s\"><br>",path_attach,savename(attach_name),towww(group),item,utage);
			goto endbit;
		}
	}
	got = 0;
	if (multi) {
		if (!gothead) s = get_line_head(instr);
		else 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 ((strstr(s,boundary)!=NULL) || boundary_check(s)) {
				/*			if (isvcard) printf("</pre>");*/
				
				if (!pre_disable) if (istext) if (!ishtml) printf("</pre>\n"); 
				gothead = FALSE; istext = FALSE; got++;
				isimage = FALSE; isbase64 = FALSE;
				ismsg = FALSE; ishtml = FALSE;
				isvcard = FALSE;
				uucount=0; /*brian*/
				uucontent=FALSE; /*brian*/
			}
			if (!gothead) if (strlen(s)==0) {
				if (ismsg)  { /* double header */
					ismsg = FALSE;
					goto next_line;
				}
				if (!(isimage||isbase64||ishtml||uucontent)) istext=TRUE; /*bazil*/
				imsg("show part istext %d................\n",istext);
				if (istext) { 
					if (!pre_disable) if (!ishtml) printf("<pre>\n");
					else printf("<hr>");
				}
				gothead = TRUE;
				
				/*if (isvcard) printf("<pre>");*/
				if ((isimage) && (!uucontent)) /*brian*/ {
					imsg("show image part=%d\n",got);
					printf("<img src=\"%s/%s?cmd=article&group=%s&item=%d&part=%d&utag=%s\"> <br>",path_attach,savename(attach_name),towww(group),item,got,utage);
				} else 
					if (isbase64) {
						imsg("show attachment part=%d\n",got);
						printf("<P><a%s href=\"%s/%s?cmd=article&group=%s&item=%d&part=%d&utag=%s&/%s\">%s</a><P>",custom_href,path_attach,savename(attach_name),towww(group),item,got,utage,attach_name,attach_name);
					} else if (isvcard) {
						printf("<P><a%s href=\"%s/%s?cmd=article&group=%s&item=%d&part=%d&utag=%s&/%s\">%s</a><P>",custom_href,path_attach,savename(attach_name),towww(group),item,got,utage,attach_name,attach_name);
						istext = FALSE;
					}
					goto next_line;
			}
			/* Content-Type: message/rfc822*/
			
			if (!gothead) {
				/* printf("%s<br>\n",s);*/
				boundary_add(s);
				strlwr(s);
				if (strstr(s,"message/rfc822")!=NULL) ismsg = TRUE;
				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;
				if (strstr(s,"text/x-vcard")!=NULL) isvcard = TRUE; 
				/* brian */
				if (strstr(s,"x-uuencode")!=NULL) uucontent = TRUE;
				if (strstr(s,"x-uue")!=NULL) uucontent = TRUE;
				get_attach_name(s);
			} else {
				/* brian */
				if (uucontent) {
					goto munged_uuencoding;
				}
finished_uu_munging:   /* brian */
				
				
				/*imsg("uu %d istext %d isquoted %d (%s)\n",uucontent,istext,isquoted,s);*/
				
				
				if(!uucontent) if (istext) {  /*brian - was ishtml*/
					if (isquoted) {
						printf("%s",decode_quote(s));
						printf("%s",decode_quote_eol());
					}
					else if (isbase64) printf("%s",decode_base64s(s));
					else if (ishtml) printf("%s",s);
					else show_text(s,"\n"); /*printf("%s",s); brian06/07/2000*/
					
					/*				if (isvcard) printf("\n");*/
				} else if ((istext)&&(!isbase64)) { /* brian */
					if (*ini_get(I_KEEP_FORMATING) == 't')
						JLLformat_wrap_output(JLLtext_encode, s, atoi(ini_get(I_WRAP_TEXT)));
					else
						show_text(s,"\n");
				}
			}
next_line:    
			if (!gothead) s = get_line_head(instr);
			else s = get_line(instr);
		}
		goto endbit;
	}
	
	gothead = TRUE;
	if (!pre_disable) if (!israw) printf("<pre>\n");
	if (!gothead) s = get_line_head(instr);
	else s = get_line(instr);
	isimage = FALSE; 
	uucontent = FALSE; /* brian */
	uucount = 0;
	ycount = 0;
	isquoted = strstr(encoding,"quoted-printable")!=NULL;
	isbase64 = strstr(encoding,"base64")!=NULL;
munged_uuencoding: /* brian */
	for (;s!=NULL;) {
		if (strlen(s)!=0) if (first_line) {
			first_line = FALSE;
			if (*s=='M') if (str_len(instr)>2000) {
				if (parts_subject(i_subject,subject,fname,&parti,&partmax)) {
					imsg("Subject is (%s)\n",i_subject);
					imsg("Looks like part %d of %d, fname=(%s), subj=(%s)\n",parti,partmax,fname,subject);
					printf("<a href=\"%s/%s?cmd=parts&group=%s&item=%d&partmax=%d&subj=%s&utag=%s&/%s\">Re-assemble multipart attachment: %s</a><p>",path_script,lib_encode_more(b0,fname),towww(group),item,partmax,lib_encode_more(b1,subject),utage,lib_encode_more(b2,fname),fname);
					isimage = TRUE;
				}
			}
			strlwr(i_content);
			if (strstr(i_content,"partial")!=NULL) {
				if (parts_subject(i_subject,subject,fname,&parti,&partmax)) {
					imsg("Found mime Subject is (%s) (%s)\n",i_subject,subject);
					imsg("Looks like part %d of %d, fname=(%s), subj=(%s)\n",parti,partmax,fname,subject);
					printf("<a href=\"%s/%s?cmd=parts&group=%s&item=%d&partmax=%d&subj=%s&utag=%s&/%s\">Re-assemble multipart attachment: %s</a><p>",path_script,lib_encode_more(b0,fname),towww(group),item,partmax,lib_encode_more(b1,subject),utage,lib_encode_more(b2,fname),fname);
					isimage = TRUE;
				} else {
					imsg("Looked like mime but wasn't %s\n",i_content);	  
				}
			}
		}
		if (strlen(s)!=0) first_line = FALSE;
		if (strncmp(s,"begin ",6)==0) {
			/*imsg("Found begin in message, probably uuencoded\n");*/
			uucount++;
			ncpy(bf,s,BFSZ-100);
			isref = FALSE;
			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,"\0\012\015"); if (x==NULL) goto skip;                        
			strlwr(x);
			/* brian 13/11/00*/
			for(temp=x;*temp!=0;temp++) if (*temp==' ') *temp='_';
			/*
			if (strchr(x,'.')==NULL) {
			x = strtok(NULL," \012\015"); if (x==NULL) goto skip;
			}
			*/
			isref=TRUE;
			if (strstr(x,".jpg")!=NULL) {isref=FALSE;strcpy(attach_name,x); goto isok;}
			if (strstr(x,".jpeg")!=NULL) {isref=FALSE;strcpy(attach_name,x); goto isok;}
			if (strstr(x,".gif")!=NULL) {isref=FALSE;strcpy(attach_name,x); goto isok;}
			if (strstr(x,".avi")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".mpg")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".mpeg")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".zip")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".exe")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".doc")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".xls")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".com")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".txt")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".ini")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".log")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".html")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".htm")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			if (strstr(x,".pdb")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			z = strstr(x,".");
			if (z!=NULL) if (strlen(z+1)>=1) {isref = TRUE; strcpy(attach_name,x); goto isok;}
			goto skip;
			
isok:                   isimage = TRUE;
                        /* brian */
						if (multi) {
							if (!isref) 
								printf("<img src=\"%s/%s?uucount=%d&cmd=article&group=%s&item=%d&part=%d&utag=%s\"><br>",path_attach,savename(attach_name),uucount,towww(group),item,got,utage);
                            else {
                                if (ini_true(I_NON_ASCII)) printf("<a%s href=\"%s/%s?uucount=%d&cmd=article&group=%s&item=%d&part=%d&utag=%s&/%s\">%s</a><p>",custom_href,path_attach,savename(attach_name),uucount,towww(group),item,got,utage,attach_name,rfc1522s(attach_name));
                                else printf("<a%s href=\"%s/%s?uucount=%d&cmd=article&group=%s&item=%d&part=%d&utag=%s&/%s\">%s</a><p>",custom_href,path_attach,savename(attach_name),uucount,towww(group),item,got,utage,attach_name,attach_name);
                            }
						}
						else {
							r = parts_subject(i_subject,subject,fname,&parti,&partmax);
							if (r && (partmax>1)) {
								imsg("Subject is (%s)\n",i_subject);
								imsg("Looks like part %d of %d, fname=(%s), subj=(%s)\n",parti,partmax,fname,subject);
								printf("<a href=\"%s/%s?cmd=parts&group=%s&item=%d&partmax=%d&subj=%s&utag=%s&/%s\">Re-assemble multipart attachment: %s</a><p>",path_script,lib_encode_more(b0,fname),towww(group),item,partmax,lib_encode_more(b1,subject),utage,lib_encode_more(b2,fname),fname);
								isimage = TRUE;
							} else {				
								if (!isref) 
									printf("<img src=\"%s/%s?uucount=%d&cmd=article&group=%s&item=%d&part=100&utag=%s\"><br>",path_attach,savename(attach_name),uucount,towww(group),item,utage);
								else {
									if (ini_true(I_NON_ASCII)) printf("<a%s href=\"%s/%s?uucount=%d&cmd=article&group=%s&item=%d&part=100&utag=%s&/%s\">%s</a><p>",custom_href,path_attach,savename(attach_name),uucount,towww(group),item,utage,attach_name,rfc1522s(attach_name));
									else printf("<a%s href=\"%s/%s?uucount=%d&cmd=article&group=%s&item=%d&part=100&utag=%s&/%s\">%s</a><p>",custom_href,path_attach,savename(attach_name),uucount,towww(group),item,utage,attach_name,attach_name);
								}
							}
						}
		}
		
		if (strncmp(s,"=ybegin ",8)==0) {
			/* Probably yEnc encoded message */
			ycount++;
			ncpy(bf,s,BFSZ-100);
			x=strstr(bf," name="); if(!x) goto skip;
			x+=6;
			if(!*x) goto skip;
			strlwr(x);
			for(temp=x;*temp!=0;temp++) if (*temp==' ') *temp=*(temp+1)?'_':'\0';
			
			isref=TRUE;
			if (strstr(x,".jpg")!=NULL) {isref=FALSE;strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".jpeg")!=NULL) {isref=FALSE;strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".gif")!=NULL) {isref=FALSE;strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".avi")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".mpg")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".mpeg")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".zip")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".exe")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".doc")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".xls")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".com")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".txt")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".ini")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".log")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".html")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".htm")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			if (strstr(x,".pdb")!=NULL) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			z = strstr(x,".");
			if (z!=NULL) if (strlen(z+1)>=1) {isref = TRUE; strcpy(attach_name,x); goto isok_y;}
			goto skip;
			
isok_y:			isimage = TRUE;
				
				r = parts_subject_yenc(i_subject,b2,&parti,&partmax);
				
				if(partmax>1) { /* multi */
					imsg("Subject is (%s)\n",i_subject);
					imsg("Looks like part %d of %d, fname=(%s), subj=(%s)\n",parti,partmax,fname,i_subject);
					lib_encode_more(b1,group);
					/*lib_encode_more(b0,fname);*/
					
					printf("<a href=\"%s/%s?cmd=parts&ycount=%d&group=%s&item=%d&partmax=%d&subj=%s&utag=%s&/%s\">Re-assemble multipart attachment: %s</a><p>",path_script,attach_name,ycount,b1,item,partmax,towww(b2),utage,attach_name,attach_name);
					/*isimage = TRUE;*/
				} else {
					if (!isref) 
						printf("<img src=\"%s/%s?ycount=%d&cmd=article&group=%s&item=%d&part=100&utag=%s\"><br>",path_attach,savename(attach_name),ycount,towww(group),item,utage);
					else {
						if (ini_true(I_NON_ASCII)) printf("<a%s href=\"%s/%s?ycount=%d&cmd=article&group=%s&item=%d&part=100&utag=%s&/%s\">%s</a><p>",custom_href,path_attach,savename(attach_name),ycount,towww(group),item,utage,attach_name,rfc1522s(attach_name));
						else printf("<a%s href=\"%s/%s?ycount=%d&cmd=article&group=%s&item=%d&part=100&utag=%s&/%s\">%s</a><p>",custom_href,path_attach,savename(attach_name),ycount,towww(group),item,utage,attach_name,attach_name);
					}
				}
		}
		
skip:
		/* brian */
		if (uucontent) goto finished_uu_munging;
		if (!israw) {
			if (!isimage) if (!isref)
				if (*ini_get(I_KEEP_FORMATING) == 't') {
					if (isquoted) JLLformat_wrap_output(JLLtext_encode, decode_quote(s), atoi(ini_get(I_WRAP_TEXT)));
					else JLLformat_wrap_output(JLLtext_encode, s, atoi(ini_get(I_WRAP_TEXT)));
				}
				else {
					if (isquoted) {
						ss = decode_quote(s);
						show_text(ss,decode_quote_eol());
					} else if (isbase64) {
						if (isbase64text)
							printf("%s",text_encode(decode_base64text(s)));
						else
							printf("%s",text_encode(decode_base64s(s)));
					}
					else show_text(s,"\n");
				}
		} else {
			if (isquoted) {
				printf("%s",decode_quote(s));
				printf("%s",decode_quote_eol());
			} else printf("%s\n",clari_encode(s));
		}
		if (isimage) {
			if (strncmp(s,"end",3)==0) isimage = FALSE;
		}
		if (!gothead) s = get_line_head(instr);
		else s = get_line(instr);
        }
        if (!pre_disable) if (!israw) printf("</pre>\n");
endbit:;
	   
}
void show_text(char *s,char *xeol)
{
	int r;
	if (!format_html) {
		printf("%s%s",text_encode(s),xeol);
		return; 
	}
	r = strspn(s,"\r\n \t");
	if (r==strlen(s)) printf("<p>\n");
	else if (*s=='>') printf("<br>\n");
	printf("%s ",text_encode(s));	
	/*	if (strlen(xeol)>0) printf("<br>");*/
}

char *show_text_fn(char *s)
{
	int r;
	static char bf[10000];
	if (!format_html) {
		lprintf(bf,10000-1,"%s\n",text_encode(s)); return bf;
	}
	r = strspn(s,"\r\n \t");
	strcpy(bf,"");
	if (r==strlen(s)) strcat(bf,"<p>\n");
	else if (*s=='>') strcat(bf,"<br>\n");
	strcat(bf,text_encode(s));	
	strcat(bf," ");
	return bf;
}

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
*/
#ifdef WIN32
char *nt_other(void)
{
	return "";
}
#endif
#ifdef NEED_STRERROR
extern int sys_nerr;
extern char *sys_errlist[];
char *strerror(int x)
{
	if ((x<sys_nerr) && (x>=0)) return sys_errlist[x];
	return "No error";
}
#endif
void fimsg(char * arg_list, ...)
{
}

char *cr_encode(char *s)
{
	char *o;
	static char out[5000];
	for (o=out;*s!=0;s++) {
		if (*s=='\n') {*o++ ='\\'; *o++ = 'n';}
		else if (*s=='\\') {*o++ ='\\'; *o++ = '\\';}
		else if (*s=='\r') continue;
		else *o++ = *s;
	}
	*o++ = 0;
	return out;
}
char *cr_decode(char *s)
{
	char *o;
	static char out[5000];
	for (o=out;*s!=0;s++) {
		if (*s=='\\') {
			s++;
			if (*s=='\\') {*o++ = '\\';}
			else if (*s=='n')  {*o++ = '\n';}
		} else *o++ = *s;
	}
	*o++ = 0;
	return out;
}
char *get_quote(char *s)
{
	static char bf[BFSZ];
	if (*s=='"') {
		ncpy(bf,s+1,BFSZ-1);
		s = strchr(bf,'"');
		if (s!=NULL) *s = 0;
	} else {
		ncpy(bf,s,BFSZ-1);
		s = strchr(bf,' ');if (s!=NULL) *s = 0;
		s = strchr(bf,';');if (s!=NULL) *s = 0;
		s = strchr(bf,',');if (s!=NULL) *s = 0;
		s = strchr(bf,'\n');if (s!=NULL) *s = 0;
	}
	return bf;
}
void cgi_addvar(char *name, char *value)
{
	char *s;
	if (strlen(name)==0) return;
	s = strdup(value);
	imsg("cgi_addvar(%.40s) = len %d (%.40s)\n",name,strlen(value),value);
	if (strlen(s)>1) s[strlen(s)-2] = 0;
	form_val[nform] = s;
	form_name[nform++] = strdup(name);
	/* imsg("Variable added (%s) = (%s) \n",name,s);*/
	strcpy(name,"");
	strcpy(value,"");
}

char *pad_right(char *s, int n)
{
	static char bf[BFSZ];
	strcpy(bf,s);
	if (strlen(bf)>=n) return bf;
	memset(bf+strlen(s),' ',n);
	bf[n] = 0;
	return bf;
}


void r_xdebug(void)
{
	int i;
	zmsg("Debugging info %d %s<br>\n",dodebug,log_file);
	
	
	for (i=0; ini_names[i]!=NULL; i++) {
		if (strcmp(ini_names[i],"tellnews_pass")==0) continue;
		if (ini_values[i]!=NULL) zmsg("Config (%s) = (%s) <br>\n",ini_names[i],ini_values[i]);
	}
	
}


void tpl_set_gl(void)
{
	char bf[BFSZ];
	char nam[BFSZ];
	int i;
	for (i=0; i<nform; i++) {
		if (strncmp(form_name[i],"gl_",3)==0) {
			var_set(form_name[i],value_encode(form_val[i]));
			ncpy(bf,form_val[i],BFSZ-10);
			lib_encode(bf);
			lprintf(nam,BFSZ-1,"%s_r",form_name[i]);
			var_set(nam,bf);
		}
	}
	for (i=0; i<nquery; i++) {
		if (strncmp(query_name[i],"gl_",3)==0) {
			var_set(query_name[i],value_encode(query_val[i]));
			ncpy(bf,query_val[i],BFSZ-10);
			lib_encode(bf);
			lprintf(nam,BFSZ-1,"%s_r",query_name[i]);
			var_set(nam,bf);
		}
	}
}
/*

  2156175 KORN - ISSUES - 02 - Korn - For You.mp3 (08/20)
  2156176 Attn DrPenalty  - Veruca Salt - Seether.mp3 (09/16)
  2156182 Attn DrPenalty  - Veruca Salt - Seether.mp3 (04/16)
  2156183 Early Presents - Joy to the World - Nat King Cole.mp3 [03/06]
  
*/

void tomail_read_item(char *group, int item, char *mid, char *subj)
{
	char bf[BFSZ];
	char *s;
	if (!open_status()) {zmsg("News host not happy %s",tohtml(nntp_status)); return;}
	if (!do_authent()) {zmsg("News host authentication failed: %s",tohtml(nntp_status)); return;}
	lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host group: %s",tohtml(nntp_status)); return;}
	
	lprintf(bf,BFSZ-1,"xover %d\015\012",item); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host not responding: %s",tohtml(nntp_status)); return;}
	read_xover();
	
	/* find first message-id in the references */
	s = get_line(instr);
	if (s==NULL) {zmsg("News host not responding thread: %s",tohtml(nntp_status)); return;}
	s = xstrtok(s,"\t");
	if (s!=NULL) s = xstrtok(NULL,"\t");	/* skip nntp response code*/
	if (s!=NULL) {				/* get subject*/
		strlwr(s);
		if (strncmp(s,"re:",3)==0)  s += 3; 
		if (*s == ' ') s++;
		ncpy(subj,s,BFSZ-1);
	}
	if (s!=NULL) s = xstrtok(NULL,"\t");	/*/ skip from*/
	if (s!=NULL) s = xstrtok(NULL,"\t");	/*/ skip date*/           
	if (s!=NULL) s = xstrtok(NULL,"\t");	/*/ skip mid */       
	if (s!=NULL) ncpy(mid,s,BFSZ-1);	/*/ get the message-id of the root thread*/
	if (s!=NULL) s = xstrtok(NULL,"\t");	/*/ get root mid */       
	if (s!=NULL) {
		if (s[0]=='<') 	ncpy(mid,s,BFSZ-1);	/*/ get the message-id of the root thread*/
		s = strchr(mid,' '); if (s!=NULL) *s = 0;
	}
	
	str_clear(instr);
	
	
}
void cmd_tomail_add_thread(void)
{
	char mid[BFSZ];
	char bf[BFSZ];
	int item;
	char group[BFSZ];
	char subj[BFSZ];
	
	item = atoi(query_find("item"));
	if (item==0) item = atoi(query_find("xrelated"));
	ncpy(group,query_find("group"),BFSZ-1);
	if (!group_allow(group)) {zmsg("Cannot read that group"); return;}
	strcpy(mid,""); strcpy(subj,"");
	tomail_read_item(group,item,mid,subj);
	if ((strlen(mid)==0) || (strlen(subj)==0)) {
		zmsg("No subject specified, so cannot follow thread");
		return;
	}
	imsg("tomail_add_thread %s %s %s\n",group,mid,subj);
	
	lprintf(bf,BFSZ-1,"xtomail add group=%s to=%s thread=%s subject=\"%s\"\015\012",
		group,rfc1522s(user_get("reply"))
		,mid,subj); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host group: %s",tohtml(nntp_status)); return;}
	var_set("group",value_encode(group));
	var_set("groupe",towww(group));
	var_set("to",rfc1522s(user_get("reply")));
	var_set("subject",subj);
	var_set("status",tohtml(nntp_status));
	if (atoi(nntp_status)!=200) {
		tpl_show("tomail_bad.tpl");		
	}
	tpl_show("tomail_ok.tpl");
}
void cmd_tomail_add(void)
{
	char mid[BFSZ];
	char bf[BFSZ];
	int item;
	char group[BFSZ];
	char subj[BFSZ];
	
	if (!open_status()) {zmsg("News host not happy %s",tohtml(nntp_status)); return;}
	if (!do_authent()) {zmsg("News host authentication failed: %s",nntp_status); return;}
	ncpy(group,query_find("group"),BFSZ-1);
	if (!group_allow(group)) {zmsg("Cannot read that group"); return;}
	lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host group: %s",tohtml(nntp_status)); return;}
	imsg("tomail_add %s %s\n",group,rfc1522s(user_get("reply")));
	
	lprintf(bf,BFSZ-1,"xtomail add group=%s to=%s\015\012",
		group,rfc1522s(user_get("reply"))); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host group: %s",nntp_status); return;}
	var_set("group",value_encode(group));
	var_set("groupe",towww(group));
	var_set("to",rfc1522s(user_get("reply")));
	var_set("status",nntp_status);
	if (atoi(nntp_status)!=200) {
		tpl_show("tomail_bad.tpl");		
	}
	tpl_show("tomail_ok.tpl");
}
void cmd_tomail_del(void)
{
	char mid[BFSZ];
	char bf[BFSZ];
	int item;
	char group[BFSZ];
	char subj[BFSZ];
	
	if (!open_status()) {zmsg("News host not happy %s",nntp_status); return;}
	if (!do_authent()) {zmsg("News host authentication failed: %s",nntp_status); return;}
	
	ncpy(group,query_find("group"),BFSZ-1);
	if (!group_allow(group)) {zmsg("Cannot read that group"); return;}
	lprintf(bf,BFSZ-1,"group %s\015\012",group); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host group: %s",nntp_status); return;}
	imsg("tomail_del %s %s\n",group,rfc1522s(user_get("reply")));
	
	lprintf(bf,BFSZ-1,"xtomail del group=%s to=%s\015\012",
		group,rfc1522s(user_get("reply"))); nntp_write_s(chan,bf,strlen(bf)); 
	if (!read_status()) {zmsg("News host group: %s",nntp_status); return;}
	var_set("group",value_encode(group));
	var_set("groupe",towww(group));
	var_set("to",rfc1522s(user_get("reply")));
	var_set("status",nntp_status);
	if (atoi(nntp_status)!=200) {
		tpl_show("tomail_bad.tpl");		
	}
	tpl_show("tomail_del.tpl");
}
/*                else if (ispressed("cmd_tomail_add_thread")) cmd_tomail_add_thread();
else if (ispressed("cmd_tomail_add")) cmd_tomail_add();
else if (ispressed("cmd_tomail_del")) cmd_tomail_del();

*/

/* display the DNewsweb version number, ini file settings and other related information */
void display_info(char *epath) {
    char bf[BFSZ];
    char bf2[BFSZ];
    char bf3[BFSZ];
    char xroot[BFSZ];
    char xin[5000];
    char *last;
    FILE *f;
	int k;
    int i;
    char *s;
	
	if (!done_content) {
		printf("Content-Type: text/html\n\n");
		done_content = TRUE;
	}
	if (!ini_true(I_SHOWINI)) {
		printf("<title>DNEWSWEB version %s - Netwin Ltd.</title>\n",DNEWS_VERSION);
		printf("showini currently disabled - to enable add 'showini true' setting in dnewsweb.ini<br>\n");
		return;
	};
	printf("<html>\n");
	printf("<head>\n");
	printf("<title>DNEWSWEB version %s - Netwin Ltd.</title>\n",DNEWS_VERSION);
	printf("</head>\n");
	printf("<body>\n");
	printf("<h2>DNEWSWEB version %s - Netwin Ltd.</h2><hr><br>\n",DNEWS_VERSION);
	printf("<table border=1>\n");
    db_setscript(mygetenv("SCRIPT_NAME"));
    lprintf(bf,BFSZ-1,"%s.ini",rootname);
    f = fopen(bf,"r");
    strcpy(bf3,bf);
    if (f==NULL) {
		printf("<tr><td>ini file not in working dir, searching in path {%s}</tr></td>\n",epath);
        strcpy(xroot,epath);
        last = xroot;
        for (s=xroot; *s!=0; s++) {
			if (*s=='/') last = s;
            if (*s=='\\') last = s;
        }
        *last = 0;
        lprintf(bf,BFSZ-1,"%s/%s.ini",xroot,rootname);
        printf("<tr><td>Looking in (%s) for ini file</tr></td>\n",bf);
		f = fopen(bf,"r");
		strcpy(bf2,bf);
	}
	if (f==NULL) {
		lprintf(bf,BFSZ-1,"/etc/%s.ini",rootname);
		printf("<tr><td>Last resort, looking in (%s) for ini file</tr></td>\n",bf);
		f = fopen(bf,"r");
	}
    if (f==NULL) {
		printf("<tr><td>Unable to find ini file in <br> {%s}\n</tr></td>",bf3);
        printf("<tr><td>Also looked in {%s} <br> and {%s}</tr></td>\n",bf2,bf);
	} else {
		printf("<tr><td><h2>dnewsweb.ini</h2></tr></td>\n");
		printf("<tr><td>\n");
		for (;!feof(f);) {
			if (fgets(xin,4000,f)!=NULL) {
				printf("%.200s<br>\n",xin);
			}
        } 
		printf("</tr></td>\n");
        fclose(f);      
	}
	printf("<tr><td><h2>environment variables</h2></tr></td>\n");
	printf("<tr><td>\n");
	for (i=0;environ[i]!=NULL;i++) {
		printf("%.200s<br>\n",environ[i]);
	}
	printf("</tr></td>\n");
	printf("<tr><td>To disable remove 'showini true' from dnewsweb.ini</tr></td>\n");
	printf("</table>\n");
	printf("</body>\n");
	printf("</html>\n");
}

int ip_banned(char *ip) {
	FILE *f;
	char *s;
	char bf[BFSZ];
	if (strlen(ini_get(I_IP_BAN_FILE))<=0) return FALSE;
	f=fopen(ini_get(I_IP_BAN_FILE),"r");
	if (f==NULL) {
		imsg("couldn't access ip ban file '%.200s' (%.200s)\n",ini_get(I_IP_BAN_FILE),strerror(errno));
		return FALSE;
	}
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-10,f)==NULL) break;
		if (bf[0]=='#') continue;
		s=strtok(bf,"\r\n \t");
		if (s==NULL) s=bf;
		if (strcmp(s,ip)==0) {
			fclose(f);
			f=NULL;
			imsg("refusing access to banned ip (%.200s)\n",ip);
			return TRUE;
		}
	}
	fclose(f);
	f=NULL;
	return FALSE;
}


char *tpl_get_nitems(char *bf) /* Roydon - %%nitems=group.name%% */
{
	char lbf[BFSZ],*stat,*group;
	static char res[20];
	
	*res='\0';
	if(group=strchr(bf,':')) {
		lprintf(lbf,BFSZ-1,"group %s\015\012",group+1);
		nntp_write_s(chan,lbf,strlen(lbf));
		
		if (!read_status()) {
			zmsg("News host not responding: %s",tohtml(nntp_status));
			return res;
		}
		
		if(stat=strtok(nntp_status," ")) {
			if(strcmp(stat,"211")==0) {
				if(stat=strtok(NULL," ")) {
					if(strlen(stat)<20) memcpy(res,stat,20);
				}
			}
		}
	}
	return res;
}

char *tpl_get_lastxhdr(char *bf) /* Roydon - %%lastxhdr:xhdr:group.name%% */
{
	int skip;
	char lbf[BFSZ],*stat,*xhdr,*group,*s;
	static char res[100];
	
	stat=strtok(bf,":"); if(stat==NULL) return ""; /* lastxhdr - ignore */
	xhdr=strtok(NULL,":"); if(xhdr==NULL) return ""; /* xhdr */
	group=strtok(NULL,":"); if(group==NULL) return ""; /* group */
	imsg("lastxhdr %s %s\n",group,xhdr);
	
	lprintf(lbf,BFSZ-1,"group %s\015\012",group);
	nntp_write_s(chan,lbf,strlen(lbf));
	
	if (!read_status()) {
		imsg("News host not responding: %s",tohtml(nntp_status));
		zmsg("News host not responding: %s",tohtml(nntp_status));
		return "";
	}
	
	stat=strtok(nntp_status," "); if(strcmp(stat,"211")) return ""; /* Status */
	stat=strtok(NULL," "); if(stat==NULL) return ""; /* Article count */
	stat=strtok(NULL," "); if(stat==NULL) return ""; /* Lowest number */
	stat=strtok(NULL," "); if(stat==NULL) return ""; /* Highest number */
	
	skip=strlen(stat)+1;
	
	lprintf(lbf,BFSZ-1,"xhdr %s %s\015\012",xhdr,stat);
	nntp_write_s(chan,lbf,strlen(lbf));
	
	imsg("Sent xhdr %s %s",xhdr,stat);
	
	if (!read_status()) {
		zmsg("News host not responding2: %s",tohtml(nntp_status));
		imsg("News host not responding2: %s",tohtml(nntp_status));
	} else {
		if(strncmp(nntp_status,"221 ",4)) return ""; /* Status */
		read_xover();
		
		/* find first message-id in the references */
		s = get_line(instr);
		if (s==NULL) return "";
		if(*s=='.') {
			imsg("returning here as dot found\n");
			return "";
		}
		
		lprintf(res,BFSZ-1,s+skip);
		return res;
	}
	return "";
}

void post_log_do(char *arg_list,...)
{
	FILE *f;
	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, POST_LOG ENTRY ERROR TOO LONG\n");
	
	f = fopen(post_log_file,"a");
	if (f==NULL) {perror(post_log_file); return; }
	fprintf(f,"%s\n",output);
	fclose(f);         
}