/*
 * newnews - Read in list of ids of new articles
 *
 * Copyright (C) 1992/93 Stephen Hebditch. All rights reserved.
 * TQM Communications, BCM Box 225, London, WC1N 3XX.
 * steveh@orbital.demon.co.uk  +44 836 825962
 *
 * 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 a binary tree in memory.
 *
 * $Id: newnews.c,v 1.5 1993/03/01 17:45:16 root Exp $
 *
 * $Log: newnews.c,v $
 * 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 "slurp.h"

static int hit_max = FALSE;

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

static int  groups_no;
static int  not_groups_no;


static void parse_groups ();
static int  add_id (char *msgid);
static void do_newnews (char *line);
static int  restreql (register char *w, register char *s);
static int  get_not_groups (char *group);


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

	static void
parse_groups ()
	{
	int i, got, not;
	char *cp;

	/* Calculate number of group entries */
	for (i = 1, cp = nn_newsgroups; *cp != '\0'; cp++)
		if (*cp == ',')
			i++;

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

	if ((not_group_array = (char **) malloc (i * (sizeof (char *)))) == NULL)
		log_sys ("parse_groups: malloc %d bytes", i * sizeof (char **));

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

	/* Now start parsing the newsgroup list */
	groups_no = 0;
	not_groups_no = 0;
	got = TRUE;
	not = FALSE;

	for (cp = nn_newsgroups; *cp != '\0'; ++cp)
		{
		if (*cp == '!')
			got = FALSE;

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

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

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

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


/*
 * store_node - Store a new node in the binary tree
 */

	static struct mnode *
store_node (char *msgid)
	{
	struct mnode *node;

	node = (struct mnode *) malloc (sizeof (struct mnode));
	if (node == NULL)
		log_sys ("add_id: malloc %d bytes", sizeof (struct mnode));
	node->left = NULL;
	node->right = NULL;
	node->used = FALSE;
	node->msgid = (char *) malloc (strlen (msgid) + sizeof (char));
	if (node->msgid == NULL)
		log_sys ("store_node: malloc %d bytes",
				 strlen (msgid) + sizeof (char));
	(void) strcpy (node->msgid, msgid);
	entries++;
	return (node);
	}


/*
 * add_id - Add a message id to the binary tree if not already present.
 * Returns -1 if the maximum number of entries in the tree has been
 * reached, 0 if the item is added okay, 1 if an entry with that 
 * particular message id already exists.
 */

	static int
add_id (char *msgid)
	{
	struct mnode *current;
	int test;

	/* Test if hit the maximum number of entries in the cache */
	if (entries >= MAXCACHE)
		return (-1);

	/* Handle the case when the tree is empty */
	if (root == NULL) {
		root = store_node (msgid);
		return (0);
	}

	/* Search the tree for correct position to insert node */
	current = root;
	
	for (;;)
		{
		test = strcmp (msgid, current->msgid);
		if (test < 0)
			{
			if (current->left == NULL)
				{
				current->left = store_node (msgid);
				return (0);
				}
			else
				current = current->left;
			}
		else if (test > 0)
			{
			if (current->right == NULL) {
				current->right = store_node (msgid);
				return (0);
				}
			else
				current = current->right;
			}
		else
			return (1);
		}
	}


/*
 * 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;

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

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

	if (check_id (msgid))
		{
		switch (add_id (msgid))
			{
			case -1 :
				hit_max = TRUE;
				break;
			case  0 :
				newart++;
				if (debug_flag)
					(void) fprintf (stderr, " new\n");
				break;
			default :
				break;
			}
		}
	else
		{
		dupart++;
		if (debug_flag)
			(void) fprintf (stderr, " dup\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 *line)
	{
	char buf [NNTP_STRLEN];

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

	(void) sprintf (buf, "NEWNEWS %s %s %s", line, nn_time,
					nn_distributions);

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

		process_id (buf);
		}
	}


/*
 * 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_not_groups - Add appropriate groups from the exclusion list to
 * a group that is to be requested from the server.
 */

	static int
get_not_groups (char *group)
	{
	char matchgroups [NNTP_STRLEN];
	int i;
	size_t tlen;

	matchgroups [0] = '\0';
	tlen = strlen (group);
	for (i = 0 ; i < not_groups_no ; i ++)
		if (!used_not_group_array [i])
			if (restreql (group, not_group_array [i]))
				if ((strlen (matchgroups) + tlen + 3) < NNTP_STRLEN)
					{
					(void) strcat (matchgroups, ",!");
					(void) strcat (matchgroups, not_group_array [i]);
					used_not_group_array [i] = TRUE;
    				}
    			else
    				return (1);
	(void) strcat (group, matchgroups);
	return (0);
	}


/*
 * get_ids - Store in memory a tree of the message ids of new article 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, add_comma;

	parse_groups ();

	line [0] = '\0';
	bzero ((char *) used_not_group_array, not_groups_no * sizeof (int));
	add_comma = FALSE;

	for (i = 0 ; i < groups_no ; i++)
		{
		(void) strcpy (newgroups, group_array [i]);

		if (get_not_groups (newgroups))
			{
			log_msg ("get_ids: Not enough room in NNTP line for exclusion list %s",
					 newgroups);
			exit (2);
			}

		if ((strlen (line) + strlen (newgroups) + 
			 strlen (nn_distributions) + (size_t) 31) > NNTP_STRLEN)
			{
			do_newnews (line);
			line [0] = '\0';
			bzero ((char *) used_not_group_array, not_groups_no * sizeof (int));
			add_comma = FALSE;
			}

		if ((strlen (line) + strlen (newgroups) + 
			 strlen (nn_distributions) + (size_t) 31) > NNTP_STRLEN)
			{
			log_msg ("get_ids: Not enough room in NNTP line for newsgroup %s",
					 newgroups);
			exit (2);
			}
		else
			{
			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 */
	if (hit_max)
		{
		log_msg ("Maximum limit of %d messages hit", MAXCACHE);
		no_time_flag++;
		}
	}

/* END-OF-FILE */
