/*
 * newnews - Read in list of ids of new articles
 *
 * Copyright (C) 1992/93/94 Stephen Hebditch <steveh@tqmcomms.co.uk>.
 * All rights reserved. TQM Communications, BCM Box 225, London, WC1N 3XX.
 *
 * See README for more information and disclaimers
 *
 * Using a previously initialised list of newsgroups, carries out a series
 * of NEWNEWS requests to the connected NNTP server, storing the message
 * ids of new articles in memory.
 *
 * $Id: newnews.c,v 1.9 1995/01/10 12:59:33 root Exp $
 *
 * $Log: newnews.c,v $
 * Revision 1.9  1995/01/10  12:59:33  root
 * Moved includes from slurp.h.
 * Removed store_node and add_id - now in cache.c.
 * Added progress reporting.
 * If MAXCACHE limit reached, then uncollected ids written to a
 * temp file which will be copied to the hostfile by write_hostfile().
 *
 * Revision 1.7  1993/06/14  15:22:24  root
 * Modified parse_groups to only malloc enough space for arrays.
 * Rewrote get_ids, incorporating the get_not_groups function. This
 * makes the algorithm neater, fixes a problem with using memset on a
 * zero length block, and fixes a problem whereby with certain
 * newsgroup combinations a line could overflow its buffer.
 * In process_id when in debug mode print after a message ID if ID
 * discarded due to hitting maximum number of articles or was already
 * present in the tree.
 *
 * Revision 1.5  1993/03/01  17:45:16  root
 * Added cast to bzeroing of used_not_group_array.
 *
 * Revision 1.4  1993/02/14  14:55:41  root
 * Malloc msgid space separately from mnode.
 * Split-out process_id from do_newnews so it can be used in get_ntime
 * to load the tree with the unretrieved message ids.
 *
 * Revision 1.3  1992/12/14
 * Only malloc enough space for msgid, not whole mnode structure.
 * Minor tidy-ups.
 *
 * Revision 1.1  1992/12/06
 * Set no_time_flag if hit max no of messages
 * Print line before it is sent to server when debugging is on.
 * No longer need to handle null nn_distributions.
 *
 * Revision 1.0  1992/11/30
 * Transferred functions from slurp.c
 */

#include "conf.h"

/* POSIX headers */

#define _POSIX_SOURCE 1
#include <sys/types.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Local headers */

#include "nntp.h"
#include "slurp.h"
#include "syslog.h"

/* File-scope variables */

static char **group_array;
static char **not_group_array;
static int  *used_not_group_array;

static int  groups_no = 1;
static int  not_groups_no = 0;


/*
 * parse_groups - Turn list of groups into two arrays containing 
 * pointers to groups to include and groups to exclude.
 */

	static void
parse_groups (void)
	{
	char *cp;
	int g = 0;
	int got = TRUE;
	int n = 0;
	int not = FALSE;

	/* Calculate number of group entries */
	for (cp = nn_newsgroups; *cp != '\0'; cp++)
		{
		if (*cp == ',')
			groups_no++;
		if (*cp == '!')
			{
		 	not_groups_no++;
		 	groups_no--;
		 	}
		 }

	/* Malloc space for include and exclude group arrays */
	if ((group_array = (char **) malloc ((size_t) groups_no * sizeof (char *))) == NULL)
		log_sys ("parse_groups: malloc %d bytes", groups_no * sizeof (char **));

	if (not_groups_no > 0)
		{
		if ((not_group_array = (char **) malloc ((size_t) not_groups_no * sizeof (char *))) == NULL)
			log_sys ("parse_groups: malloc %d bytes", not_groups_no * sizeof (char **));

		if ((used_not_group_array = (int *) malloc ((size_t) not_groups_no * sizeof (int))) == NULL)
			log_sys ("parse_groups: malloc %d bytes", not_groups_no * sizeof (int));
		}

	/* Now start parsing the newsgroup list */
	for (cp = nn_newsgroups; *cp != '\0'; cp++)
		{
		if (*cp == '!')
			got = FALSE;

		if (got)
			{
			group_array [g++] = cp;
			got = FALSE;
			}

		if (not)
			{
			not_group_array [n++] = cp;
			not = FALSE;
			}

		if (*cp == ',')
			{
			*cp = '\0';
			got = TRUE;
			}

		if (*cp == '!')
			not = TRUE;
		}
	}


/*
 * process_id - Check if id already exists in local history file, if not
 * then add it to the message id tree if it isn't already in there.
 */

	void
process_id (char *msgid)
	{
	char *cp;
#ifdef SITEEXCLUDING
	char *host;
#endif /*SITEXCLUDING*/

	/* Modify the message id appropriate to C-News history files */
	if ((cp = strchr (msgid, '@')) != NULL)
		{
#ifdef SITEEXCLUDING
		host = cp;
#endif /*SITEXCLUDING*/
		for (; *cp != '\0'; cp++)
			if (isupper (*cp))
				*cp = tolower (*cp);
		}

	if (debug_flag)
		(void) fprintf (stderr, "-> %s", msgid);

#ifdef SITEEXCLUDING
	if((host)&&(site_check(host)))
	{
		if (debug_flag)
			(void) fprintf (stderr, " (Excluded host %s)", host);
		/* Excluded host */
		site_droparticle(msgid);
		exclart++;
		return;
	}
#endif /*SITEXCLUDING*/

	/* If hit maximum cache size, then store in a temporary file */
#ifdef MAXCACHE
	if (waiting >= MAXCACHE)
		{
		if (no_id_write_flag)
			{
			if (debug_flag)
				(void) fprintf (stderr, " discarded\n");
			}
		else
			{
			if (overflowfp == NULL)
				if ((overflowfp = tmpfile ()) == NULL)
					log_sys ("process_id: can't create overflow temporary file");
			(void) fprintf (overflowfp, "%s\n", msgid);
			if (debug_flag)
				(void) fprintf (stderr, " overflowed\n");
			}
		return;
		}
#endif

	if (check_id (msgid))
		{
		if (add_id (msgid))
			{
			/* Article not in history file and isn't in cache yet */
			waiting++;
			if (debug_flag)
				(void) fprintf (stderr, " new\n");
			if (progress_flag)
				{
				if ((waiting % 20) == 0)
					{
					(void) fprintf (stdout, "%d waiting\r", waiting);
					(void) fflush (stdout);
					}
				}
			}
		else
			{
			/* Article is already in cache */
			if (debug_flag)
				(void) fprintf (stderr, " present\n");
			}
		}
	else
		{
		/* Article is already in history file */
		dupart++;
		if (debug_flag)
			(void) fprintf (stderr, " duplicate\n");
		}
	}


/*
 * do_newnews - Process a newnews for supplied list of groups, adding the
 * resultant data to the message id tree.
 */

	static void
do_newnews (char *groups)
	{
	char line [NNTP_STRLEN];

	/* Create a full string to send to the server */
	(void) sprintf (line, "NEWNEWS %s %s GMT %s", groups, nn_time,
					nn_distributions);

	/* Do the actual NEWNEWS */
	if (debug_flag)
		(void) fprintf (stderr, "<- %s\n", line);
	put_server (line);
	
	/* Get the response and check it's okay */
	get_server (line, sizeof (line));
	if (debug_flag)
		(void) fprintf (stderr, "-> %s\n", line);
	if (atoi (line) != OK_NEWNEWS)
		{
		log_msg ("do_newnews: NNTP protocol error: got '%s'", line);
		exit (4);
		}
	                
	/* Now get the data and stick it in the tree */
	for (;;)
		{
		get_server (line, sizeof (line));
		if (strcmp (line, ".") == 0)
			break;

		process_id (line);
		}
	}


/*
 * restreql -- A small regular expression string equivalence routine
 * purloined from nntp 1.5.11 which credits <lai@shadow.berkeley.edu>
 * for its creation. Returns 1 if the string pointed to by 's' matches
 * the asterisk-broadened regexp string pointed to by 'w', otherwise
 * returns 0.
 */

	static int
restreql (register char *w, register char *s)
	{
	while (*s && *w)
		{
		switch (*w)
			{
			case '*':
				for (w++; *s; s++)
					if (restreql (w, s))
						return (1);
				break;
			default:
				if (*w != *s)
					return (0);
				w++, s++;
				break;
			}
		}
	if (*s)
		return (0);
	while (*w)
		if (*w++ != '*')
			return 0;

	return (1);
	}


/*
 * get_ids - Store in memory a tree of the message ids of new articles at
 * the server which match the specified set of groups and distributions
 * for the currently connected host.
 */

	void
get_ids ()
	{
	char line [NNTP_STRLEN];
	char newgroups [NNTP_STRLEN];
	int i, j, add_comma;
	size_t newlen, linelen;
	size_t startlen = 30 + strlen (nn_distributions);

	/* Turn comma-separated list of groups into 2 arrays */
	parse_groups ();

	/* Initialiase for first list of groups to send to server */
	*line = '\0';
	linelen = startlen;
	if (not_groups_no)
		(void) memset (used_not_group_array, 0, not_groups_no * sizeof (int));
	add_comma = FALSE;

	for (i = 0 ; i < groups_no ; i++)
		{
		/* Check group isn't so big it doesn't fit at all */
		newlen = strlen (group_array [i]);
		if ((newlen + startlen) > NNTP_STRLEN)
			{
			log_msg ("get_ids: not enough room in NNTP line for newsgroup %s",
					 group_array [i]);
			exit (2);
			}

		/* Get list containing new group and matching ! groups */
		(void) strcpy (newgroups, group_array [i]);
		for (j = 0 ; j < not_groups_no ; j++)
			if (!used_not_group_array [j])
				if (restreql (group_array [i], not_group_array [j]))
					{
					newlen += strlen (not_group_array [j]) + 2;
					if ((newlen + startlen) > NNTP_STRLEN)
						{
						log_msg ("get_ids: not enough room in NNTP line for exclusion list %s",
								 newgroups);
						exit (2);
						}
					(void) strcat (newgroups, ",!");
					(void) strcat (newgroups, not_group_array [j]);
					used_not_group_array [j] = TRUE;
					}

		/* If can't add new groups to existing list, then do a newnews */
		if ((linelen + newlen + add_comma) > NNTP_STRLEN)
			{
			do_newnews (line);
			*line = '\0';
			linelen = startlen;
			if (not_groups_no)
				(void) memset (used_not_group_array, 0, not_groups_no * sizeof (int));
			add_comma = FALSE;
			}

		linelen += newlen + add_comma;
		if (add_comma)
			(void) strcat (line, ",");
		else
			add_comma = TRUE;
		(void) strcat (line, newgroups);
		}

	do_newnews (line);

	/* Report if couldn't fit everything in the tree */
#ifdef MAXCACHE
	if (waiting >= MAXCACHE)
		log_msg ("Maximum limit of %d messages hit", MAXCACHE);
#endif

	if (debug_flag)
		(void) fprintf (stderr, "%d articles waiting\n", waiting);

	}

/* END-OF-FILE */


syntax highlighted by Code2HTML, v. 0.9.1