#include "global.h"
#include "commands.h"
#include "files.h"
#ifndef MSDOS
#include "ctype.h"
#include <sys/stat.h>
#endif
#ifdef UNIX
#include <unistd.h>
#endif
#include "timer.h"
#include "proc.h"
#include "bm.h"
#include "pool.h"


#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: bid_wp.c,v 1.26 2000/05/09 16:48:24 brian Exp $";
#endif

extern char *wpageCheck (char *string, int bbs, int updateit, char which);
extern void sortit (const char *fname, int entrysize, int searchsize, int strsize, time_t age);
static void Oldbidtick (void *p);
static void Oldbidprocess (int a, void *v1, void *v2);
static int dotimer (int argc, char *argv[], void *p);
static int doexpkick (int argc, char *argv[], void *p);
static int dobidkick (int argc, char *argv[], void *p);
static int doage (int argc, char *argv[], void *p);
int dombbid (int argc, char *argv[], void *p);

#ifdef WPAGES
static void exp_function (const char *funcname, const char *filename, const char *fname, int theage, struct timer * thetimer, int strsize, int sortsize);
static int dotempage (int argc, char *argv[], void *p);
static void TempWPages (void);
extern void wpageAdd (char *entry, int bbs, int updateit, char which);
static int dowpsupport (int argc, char *argv[], void *p);
int wpserver (FILE * fp, const char *from);
static int dowpserver (int argc, char *argv[], void *p);
static int dowpclient (int argc, char *argv[], void *p);
static int dowpupdate (int argc, char *argv[], void *p);
static int dowpdest (int argc, char *argv[], void *p);
static int dowpclicall (int argc, char *argv[], void *p);
static void WPUpdatetick (void *p);
static void Oldwpagestick (void *p);
static void Oldwpagesprocess (int a, void *v1, void *v2);
void add_WPUpdate (char *call, char *home, char *name, char which);
#endif

#ifdef POOLED
static int dobidmemory (int argc, char **argv, void *p);
static int dobidcount (int argc, char *argv[], void *p);
static void bid_load (int mode);
#endif

extern int expired;
void bid_delete (register char *string);
int bid_check (register char *string);
void bid_add (char *bid, time_t now, int tofile, char *to);

static char OpenErr[] = "expire: err %d opening %s";
static char RenErr[] = "expire: err %d renaming %s to %s";
static char WriteErr[] = "expire: err %d writing %s";

#define MSPMINUTE (1000L*60L)

extern char *BIDsuffix;


#ifdef WPAGES
#define WPDEST	10
#define MSPHOUR (1000L*60L*60L)
#define MSPDAY  (1000L*60L*60L*24)

static char *WPdestinations[WPDEST];
static char *WPcall;
static struct timer WPUpdatetimer;
static int WPClient, WPServer;
int MbWpages = 1;
static int WPtempAge = 30;
#endif


extern char *Mbhaddress;
extern char *mbxRCall;


#ifdef POOLED
static int MemBid = 0;

struct membid {
	struct membid *next;
	char bid[14];
	long time;
	char to[14];
};

#define NULLMEMBID ((struct membid *)0)

static struct membid *MemBidTop;

static struct mempool membid_pool = { NULLPOOLBLK, NULLPOOLBLK, 0, sizeof (struct membid), 50 };



static int
dobidmemory (int argc, char *argv[], void *p OPTIONAL)
{
int was, retval;

	was = MemBid;
	retval = setbool (&MemBid, "Access bid history from memory", argc, argv);
	if (was != MemBid)
		pool_free (&membid_pool);
	if (MemBid)
		bid_load (1);
	return (retval);
}



static int
dobidcount (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
struct mempoolblock *mpb = (struct mempoolblock *) 0;
long count = 0;
int blocks = 0;

	if (!MemBid)
		tputs ("Bids are not being stored in memory\n");
	else {
		for (mpb = membid_pool.top; mpb; mpb = mpb->next) {
			if (!mpb->next)
				count += (long) membid_pool.index;
			else
				count += membid_pool.numentries;
			blocks++;
		}
		tprintf ("There are %ld Bids in memory in %d memory blocks\n", count, blocks);
	}
	return (0);
}
#endif



#ifdef WPAGES
static struct cmds OLDcmds[] =
{
	{ "age",		doage,		0, 0, NULLCHAR },
	{ "client",		dowpclient,	0, 0, NULLCHAR },
	{ "clientcall",		dowpclicall,	0, 0, NULLCHAR },
	{ "destinations",	dowpdest,	0, 0, NULLCHAR },
	{ "kick",		doexpkick,   2048, 0, NULLCHAR },
	{ "timer",		dotimer,	0, 0, NULLCHAR },
	{ "server",		dowpserver,	0, 0, NULLCHAR },
	{ "support",		dowpsupport,	0, 0, NULLCHAR },
	{ "temporaryage",	dotempage,	0, 0, NULLCHAR },
	{ "update",		dowpupdate,	0, 0, NULLCHAR },
	{ NULLCHAR,		NULL,		0, 0, NULLCHAR }
};
#endif



static struct cmds OLDbidcmds[] =
{
	{ "age",	doage,		0, 0, NULLCHAR },
#ifdef POOLED
	{ "count",	dobidcount,	0, 0, NULLCHAR },
#endif
	{ "kick",	dobidkick,   2048, 0, NULLCHAR },
#ifdef POOLED
	{ "memory",	dobidmemory,	0, 0, NULLCHAR },
#endif
	{ "timer",	dotimer,	0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR }
};



int
dooldbids (int argc, char **argv, void *p OPTIONAL)
{
	return (subcmd (OLDbidcmds, argc, argv, (void *) 1));
}



#ifdef WPAGES
int
dooldwpages (int argc, char *argv[], void *p OPTIONAL)
{
	return (subcmd (OLDcmds, argc, argv, (void *) 0));
}



static void
exp_function (funcname, filename, fname, theage, thetimer, strsize, sortsize)
const char *funcname, *fname OPTIONAL;
const char *filename;
int theage;
struct timer *thetimer;
int strsize, sortsize;
{
time_t age;
int didit;

	stop_timer (thetimer);
	log (-1, "%s: processing", funcname);
	kwait (NULL);

	didit = merge (filename);	/* add in contents of '.new' file */
	if (!didit)
		return;		/* skip expire if no NEW extries - for performance */
	age = (time_t) (theage * 86400L);
	sortit (filename, strsize + 17, sortsize, strsize, age);	/* sort entire file */

	if (expired)
		log (-1, "%s: %d expired", funcname, expired);
	start_detached_timer (thetimer);
	return;
}
#endif /* WPAGES */



/******************************************************************/
/* This program will deleted old BID's from the history file,
 * after making a backup copy.
 *
 *  Eg. 'oldbids 24 30' will try to delete all bids older then 30 days
 *      every 24 hours.
 *      'oldbids now' will do it now, with set value of age.
 *
 * Copyright 1992, Johan. K. Reinalda, WG7J/PA3DIS
 *      email : johan@ece.orst.edu
 *      packet: wg7j@wg7j.or.usa.na
 *
 * Any part of this source may be freely distributed for none-commercial,
 * amateur radio use only, as long as credit is given to the author.
 *
 * v1.0 920325
 * Modified to add oldwpages using shared code TNOS 1.0     BAL
 */
static struct timer Oldbidtimer;
static int Oldbid_age = 30;

#ifdef WPAGES
static struct timer Oldwpagestimer;
static int Oldwpages_age = 30;
#endif



static void
Oldbidprocess (int a OPTIONAL, void *v1 OPTIONAL, void *v2 OPTIONAL)
{
int i, hasexpired = 0;
char *cp, *cp2;
FILE *old, *new;
time_t now;
time_t age;
time_t bidtime;
#define LEN 80
char *newfile;
char buf[LEN];


	stop_timer (&Oldbidtimer);
	log (-1, "Oldbid: processing");
	age = (time_t) (Oldbid_age * 86400L);
	kwait (NULL);


	newfile = (char *) mallocw (strlen (Historyfile) + 5);
	sprintf (newfile, "%s.new", Historyfile);
	unlink (newfile);
	if ((old = fopen (Historyfile, READ_TEXT)) == NULLFILE) {
		log (-1, OpenErr, errno, Historyfile);
		return;
	}
	if ((new = fopen (newfile, WRITE_TEXT)) == NULLFILE) {
		(void) fclose (old);
		log (-1, OpenErr, errno, newfile);
		free (newfile);
		return;
	}
	now = time (&now);

	while (fgets (buf, LEN, old) != NULL) {
		kwait (NULL);
		rip (buf);
		if (*buf == '$')
			continue;	/* skip deleted bid */
		if ((cp = strchr (buf, ' ')) != NULLCHAR) {
			/*found one with timestamp*/
			*cp++ = '\0';	/* now points to timestamp */
			cp2 = skipwhite (cp);
			cp2 = skipnonwhite (cp2);
			cp2 = skipwhite (cp2);
			if ((bidtime = atol (cp)) == 0L)
				/*something wrong, re-stamp */
				fprintf (new, "%-13.13s %-14ld %s\n", buf, now, cp2);
			else {
				/* Has this one expired yet ? */
				if (now - bidtime < age)
					fprintf (new, "%-13.13s %-14ld %s\n", buf, bidtime, cp2);
				else
					hasexpired++;
			}
		} else {
			/* This is an old one without time stamp,
			 * add to the new file with current time as timestamp
			 */
			fprintf (new, "%-13.13s %-14ld unknown\n", buf, now);
		}
		kwait (NULL);
	}

	i = ferror (new);
	(void) fclose (old);
	(void) fclose (new);

	if (!i) {
		unlink (Historyfile);
		if (rename (newfile, Historyfile) == -1)
			log (-1, RenErr, errno, newfile, Historyfile);
	} else
		log (-1, WriteErr, errno, newfile);

	free (newfile);
	if (hasexpired)
		log (-1, "Oldbid: %d expired", hasexpired);
	start_detached_timer (&Oldbidtimer);
	return;
}



static void
Oldbidtick (void *p OPTIONAL)
{
	if (newproc ("Oldbid", 2048, Oldbidprocess, 0, NULL, NULL, 0) == NULLPROC)
		log (-1, "Couldn't start Oldbid process");
}



#ifdef WPAGES
static int
dotempage (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc < 2)
		tprintf ("WP Temporary Assignment age: %d days\n", WPtempAge);
	else
		WPtempAge = atoi (argv[1]);
	return 0;
}



static void
TempWPages ()
{
FILE *fp;
long offset, validentries = 0;
char buf[LINELEN], *cp;
time_t then, now;


	(void) time (&now);
	if ((fp = fopentmp (WhitePages, READ_TEXT)) != NULLFILE) {
		while (offset = ftell (fp), fgets (buf, LINELEN - 1, fp)) {
			kwait (NULL);
			if (*buf == '$')
				continue;
			cp = skipnonwhite (buf);
			*cp++ = 0;
			cp = skipwhite (cp);
			then = atol (cp);
			if ((then + (WPtempAge * MSPDAY)) > now) {
				validentries++;
#if 0
				tcmdprintf ("temp: entry '%s' to remain %ld days\n", buf,
					    ((then + (WPtempAge * MSPDAY)) - now) / MSPDAY);
#endif
				continue;
			}
			wpageAdd (buf, 0, 1, 'T');
			mark_forwarded (fp, offset, '$');
		}
		(void) fclose (fp);
		if (!validentries) {
			sprintf (buf, "%s.tmp", WhitePages);
			unlink (buf);
		}
	}
	kwait (NULL);
}



void
RenewWPages ()
{
	exp_function ("Oldwpages", WhitePages, "wpages", Oldwpages_age, &Oldwpagestimer, 13, 6);
}



static void
Oldwpagesprocess (int a OPTIONAL, void *v1 OPTIONAL, void *v2 OPTIONAL)
{
	setMaintenance ();
	exp_function ("Oldwpages", WhitePagesBBS, "wpagebbs", Oldwpages_age, &Oldwpagestimer, 32, 6);
	RenewWPages ();
	TempWPages ();
	clearMaintenance ();
}



static void
Oldwpagestick (void *p OPTIONAL)
{
	if (newproc ("Oldwpages", 2048, Oldwpagesprocess, 0, NULL, NULL, 0) == NULLPROC)
		log (-1, "Couldn't start Oldwpages process");
}

#endif /* WPAGES */


static const char *names[] = { "White Pages", "Bulletin ID" };



static int
dotimer (int argc, char *argv[], void *p)
{
struct timer *thetimer;
void (*thefunc) (void *);	/* Function to call at expiration */

#ifdef WPAGES
	thetimer = (p) ? &Oldbidtimer : &Oldwpagestimer;
	thefunc = (p) ? Oldbidtick : Oldwpagestick;
#else
	thetimer = &Oldbidtimer;
	thefunc = Oldbidtick;
#endif
	if (argc < 2) {
		tprintf ("%s timer: %lu/%lu minutes\n", names[(int) p],
			 read_timer (thetimer) / MSPMINUTE,
			 dur_timer (thetimer) / MSPMINUTE);
		return 0;
	}
	stop_timer (thetimer);	/* just in case */
	thetimer->func = (void (*)(void *)) thefunc;	/* what to call on timeout */
	thetimer->arg = NULL;	/* dummy value */
	set_timer (thetimer, atol (argv[1]) * MSPMINUTE);	/* set timer duration */
	/*	(*thefunc)(NULL);    *//*  Do one now and start it all!*/
	start_detached_timer (thetimer);	/* fire it up */
	return 0;
}



static int
doexpkick (int argc OPTIONAL, char *argv[]OPTIONAL, void *p OPTIONAL)
{
void (*thefunc) (void *);	/* Function to call at expiration */

#ifdef WPAGES
	thefunc = (p) ? Oldbidtick : Oldwpagestick;
#else
	thefunc = Oldbidtick;
#endif
	(*thefunc) (NULL);
	return 0;
}



static int
dobidkick (int argc, char *argv[], void *p)
{
#ifdef POOLED
int hasexpired = 0;
time_t now;
time_t age;
struct membid *mb;

	if (!MemBid)
#endif
		return (doexpkick (argc, argv, p));
#ifdef POOLED
	stop_timer (&Oldbidtimer);
	log (-1, "Oldbid: processing");
	age = (time_t) (Oldbid_age * 86400L);
	kwait (NULL);

	unlink (Historyfile);
	now = time (&now);

	for (mb = MemBidTop; mb; mb = mb->next) {
		kwait (NULL);
		if (mb->bid[0] == '$')
			continue;	/* skip deleted bid */
		if (now - mb->time < age)
			bid_add (mb->bid, mb->time, 2, mb->to);
		else
			hasexpired++;
	}

	if (hasexpired)
		log (-1, "Oldbid: %d expired", hasexpired);
	bid_load (0);
	start_detached_timer (&Oldbidtimer);
	return 0;
#endif
}



static int
doage (int argc, char *argv[], void *p)
{
int *theage;

#ifdef WPAGES
	theage = (p) ? &Oldbid_age : &Oldwpages_age;
#else
	theage = &Oldbid_age;
#endif
	if (argc < 2)
		tprintf ("%s age: %d days\n", names[(int) p], *theage);
	else
		*theage = atoi (argv[1]);
	return 0;
}



#ifdef POOLED
static void
bid_load (int mode)
{
FILE *fp;
char *cp, *cp2, buf[LINELEN + 1];
long count = 0;

	if (MemBid) {
		pool_free (&membid_pool);
		MemBidTop = NULLMEMBID;
		if ((fp = fopen (Historyfile, READ_TEXT)) == NULLFILE)
			return;

		while (!feof (fp)) {
			kwait (NULL);
			(void) fgets (buf, LINELEN, fp);
			if (feof (fp))
				continue;
			rip (buf);
			cp = strchr (buf, ' ');
			if (!cp)
				continue;
			*cp++ = 0;
			cp2 = skipwhite (cp);
			cp2 = skipnonwhite (cp2);
			cp2 = skipwhite (cp2);
			bid_add (buf, atol (cp), 0, cp2);
			count++;
		}
		(void) fclose (fp);
	}
	if (mode)
		tprintf ("There were %ld bids read\n", count);
}

#endif



void
bid_add (char *bid, time_t now, int tofile, char *to)
{
FILE *fp;
#ifdef POOLED
struct membid *mb;
#endif

	/* tofile == 0 - add in memory only - don't check for bid_load
	 * tofile == 1 - add to memory and file
	 * tofile == 2 - add to file only
	 */

#ifdef POOLED
	if (tofile != 2 && MemBid) {
		if (tofile && !membid_pool.top)
			bid_load (0);
		mb = (struct membid *) pool_alloc (&membid_pool);
		mb->next = MemBidTop;
		MemBidTop = mb;
		strncpy (mb->bid, bid, 13);
		strncpy (mb->to, to, 13);
		mb->time = (long) now;
	}
#endif
	if (tofile && (fp = fopen (Historyfile, APPEND_TEXT)) != NULL) {
		fprintf (fp, "%-13.13s %-14ld %s\n", bid, now, to);	/* Save BID */
		(void) fclose (fp);
	}
}



int
bid_check (register char *string)
{
int retval = 0;
char buf[LINELEN];
register FILE *fp;

#ifdef POOLED
struct membid *mb;

	if (MemBid) {
		if (!membid_pool.top)
			bid_load (0);
		for (mb = MemBidTop; mb; mb = mb->next) {
			if (mb->bid[0] == '$')
				continue;
			if (!strnicmp (string, mb->bid, strlen (string))) {
				retval = 1;
				break;
			}
		}
	} else {
#endif
		if ((fp = fopen (Historyfile, READ_TEXT)) == NULLFILE)
			return 0;

		while (!feof (fp)) {
			kwait (NULL);
			(void) fgets (buf, LINELEN, fp);
			if (feof (fp))
				continue;
			if (*buf == '$')
				continue;
			if (!strnicmp (string, buf, strlen (string))) {
				retval = 1;
				break;
			}
		}
		(void) fclose (fp);
#ifdef POOLED
	}
#endif
	return (retval);
}



/* Releases a bid from the history file. Used when a personal message
 * is forwarded off the system. Lets it be able to come back here,
 * if needed.
 */
void
bid_delete (register char *string)
{
register FILE *fp;
char buf[LINELEN];
long theindex;
#ifdef POOLED
struct membid *mb;
#endif

	if (string == NULLCHAR)
		return;

#ifdef POOLED
	if (MemBid) {
		if (!membid_pool.top)
			bid_load (0);
		for (mb = MemBidTop; mb; mb = mb->next) {
			if (mb->bid[0] == '$')
				continue;
			if (!strnicmp (string, mb->bid, strlen (string))) {
				mb->bid[0] = '$';
				break;
			}
		}
	} else {
#endif
		if ((fp = fopen (Historyfile, UPDATE_TEXT)) == NULLFILE)
			return;

		while (!feof (fp)) {
			kwait (NULL);
			theindex = ftell (fp);
			(void) fgets (buf, LINELEN, fp);
			if (feof (fp))
				continue;
			if (*buf == '$')
				continue;
			if (!strnicmp (string, buf, 13)) {
				fseek (fp, theindex, SEEK_SET);
				fputc ('$', fp);
				break;
			}
		}
		(void) fclose (fp);
#ifdef POOLED
	}
#endif
	kwait (NULL);
}



#ifdef WPAGES

static int
dowpsupport (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&MbWpages, "Use White Pages dbase", argc, argv);
}



static int
dowpserver (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&WPServer, "Enable WP Server - process incoming WP messages", argc, argv);
}



static int
dowpclient (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&WPClient, "Enable WP Client - process WP updates", argc, argv);
}



static int
dowpclicall (int argc, char *argv[], void *p OPTIONAL)
{
char buf[128], *cp;

	strncpy (buf,
#ifdef MBFWD	/* just in case ;-) */
		(BIDsuffix) ? BIDsuffix :
#endif
		Hostname, 128);
	if ((cp = strchr (buf, '.')) != NULLCHAR)
		*cp = 0;

	if (argc > 1)	{
		free (WPcall);
		WPcall = strdup (argv[1]);
	}
		
	tputs ("WP updates will be sent from ");
	if (!WPcall)
		tprintf ("WP@%s - no clientcall set\n", buf);
	else
		tprintf ("%s@%s\n", WPcall, buf);

	return 0;
}



static int
dowpdest (int argc, char *argv[], void *p OPTIONAL)
{
int k;

	if (argc == 1) {
		if (!WPdestinations[0])
			tputs ("No WP update destinations are defined\n");
		else {
			tputs ("WP updates will be sent to the following PBBSs...\n\n");
			for (k = 0; k < WPDEST; k++)
				if (WPdestinations[k])
					tprintf ("%s\n", WPdestinations[k]);
		}
		return 0;
	}
	for (k = 0; k < WPDEST; k++)
		if (!WPdestinations[k]) {
			WPdestinations[k] = strdup (argv[1]);
			break;
		}
	if (k == WPDEST)
		tprintf ("Sorry, but the maximum of %d update destinations were already defined!\007\n", WPDEST);
	return 0;
}



static int
dowpupdate (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc < 2) {
		tprintf ("timer: %lu/%lu hrs\n",
			 read_timer (&WPUpdatetimer) / MSPHOUR,
			 dur_timer (&WPUpdatetimer) / MSPHOUR);
		return 0;
	}
	if (*argv[1] == 'n') {
		WPUpdatetick (NULL);
		return 0;
	}
	/* set the timer */
	stop_timer (&WPUpdatetimer);	/* Just in case */
	WPUpdatetimer.func = (void (*)(void *)) WPUpdatetick;	/* what to call on timeout */
	WPUpdatetimer.arg = NULL;	/* dummy value */
	set_timer (&WPUpdatetimer, atol (argv[1]) * MSPHOUR);	/* set timer duration */
	start_detached_timer (&WPUpdatetimer);
	return 0;
}



static void
WPUpdatetick (void *p OPTIONAL)
{
int k;
FILE *fp;
char here[128];
char there[128];

	start_detached_timer (&WPUpdatetimer);

	if (WPClient) {
		fp = fopen (WPUpdateFile, READ_TEXT);
		if (!fp)
			return;
		sprintf (here, "%s@%s", (WPcall) ? WPcall : "WP", Hostname);
		for (k = 0; k < WPDEST; k++) {
			if (WPdestinations[k]) {
				log (-1, "WP Update sent to: %s", WPdestinations[k]);
				rewind (fp);
				sprintf (there, "WP@%s", WPdestinations[k]);
				(void) rdaemon (fp, NULLCHAR, here, there, "WP Update", 'P', 0);
			}
		}
		(void) fclose (fp);
	}
	unlink (WPUpdateFile);
}



void
add_WPUpdate (char *origcall, char *home, char *name, char which)
{
FILE *fp;
time_t now;
struct tm *t;
char *bbshier, *who = NULLCHAR, *cp;
char here[80];
char *call;

	if (!WPClient)
		return;
	if (which == 'X' || which == 'T')
		return;
	fp = fopen (WPUpdateFile, APPEND_TEXT);
	if (!fp)
		return;
	(void) time (&now);
	t = localtime (&now);

	call = strdup (origcall);
	(void) strupr (call);
	cp = strpbrk (call, ".#@[");
	if (cp)
		*cp = 0;
	if (mbxRCall)
		strncpy (here, mbxRCall, 80);
	else
		(void) pax25 (here, Mycall);
	if (!home || !stricmp (here, home)) {
		if (Mbhaddress) {
			strcat (here, ".");
			strcat (here, Mbhaddress);
		}
		bbshier = strdup (here);
	} else
		bbshier = wpageCheck (home, 1, 0, 'X');
	if (bbshier) {
		if (name) {
			who = strdup (&name[1]);
			cp = strpbrk (who, " )");
			if (cp)
				*cp = 0;
		}
		fprintf (fp, "On %02d%02d%02d %s/%c @ %s zip ? %s ?\n",
				 (t->tm_year % 100), t->tm_mon + 1, t->tm_mday, call, which,
				 bbshier, (who && *who) ? who : "?");
		if (who)
			free (who);
		free (bbshier);
	}
	free (call);
	(void) fclose (fp);
}



int
wpserver (FILE *fp, const char *from OPTIONAL)
{
char buf[512], subject[256], realfrom[128], *cp;
long startat;
char call[12], bbs[80], zip[12], name[64], *qth, updatetype = 'I';
int k;

	if (!WPServer)
		return 0;
	parseheader (fp, realfrom, subject, NULLCHAR, NULLCHAR, buf, &startat);
	cp = strchr (realfrom, '@');
	if (cp)
		*cp++ = 0;
	log (-1, "WP Updates received from %s", (cp) ? cp : "unknown source");
	fseek (fp, startat, SEEK_SET);
	if (realfrom[0] && subject[0]) {
		while (fgets (buf, 512, fp) != NULLCHAR) {
			if (!strnicmp ("On ", buf, 3)) {	/* this is a WP Update line */
				rip (buf);
				sscanf (&buf[10], "%s @ %s zip %s %s",
					call, bbs, zip, name);
				qth = buf;
				for (k = 0; k < 8; k++) {
					qth = strchr (qth, ' ');
					if (qth)
						qth++;
					else
						break;
				}
				if (!qth)
					continue;
				cp = strchr (call, '/');
				if (cp) {
					*cp++ = 0;
					updatetype = *cp;
				}
#if 0
				tcmdprintf ("Received WP Update [%c]: %s @ %s (%s) zip=%s qth=%s\n",
				     updatetype, call, bbs, name, zip, qth);
#endif
				if (updatetype != 'I') {
					sprintf (name, "%s@%s", call, bbs);
					wpageAdd (name, 0, (updatetype == 'G') ? 0 : 1, 'X');
				} else
					wpageAdd (bbs, 1, 1, 'X');
			}
		}
	}
	return 1;
}
#endif



#ifdef MBFWD
int
dombbid (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
{
char buf[LINELEN], *cp = NULLCHAR;
register FILE *fp;

#ifdef POOLED
struct membid *mb;

	if (MemBid) {
		if (!membid_pool.top)
			bid_load (0);
		for (mb = MemBidTop; mb; mb = mb->next) {
			if (strnicmp (mb->bid, argv[1], strlen (argv[1])))
				continue;
			cp = mb->to;
			break;
		}
	} else {
#endif
		if ((fp = fopen (Historyfile, READ_TEXT)) == NULLFILE)
			return 0;

		while (!feof (fp)) {
			kwait (NULL);
			(void) fgets (buf, LINELEN, fp);
			if (feof (fp))
				continue;
			if (strnicmp (argv[1], buf, strlen (argv[1])))
				continue;
			rip (buf);
			cp = skipnonwhite (buf);
			cp = skipwhite (cp);
			cp = skipnonwhite (cp);
			cp = skipwhite (cp);
			break;
		}
		(void) fclose (fp);
#ifdef POOLED
	}
#endif
	if (cp == NULLCHAR || !*cp)
		tprintf ("\nBid '%s' is either NOT on the system, or cannot be located\n", argv[1]);
	else
		tprintf ("\nBid '%s' is in area '%s'\n", argv[1], cp);
	return 0;

}
#endif
