/*
 *	File Name:		article.c
 *	Project:		BARN - Bah's Amiga ReadNews.
 *	Purpose:		Parse header fields in a news article.
 *	Functions:		ParseArticle, GetNextHeader, DumpArticle, DestroyArticle.
 *	Author:			Jeff Van Epps
 *	Created:		02 Sep 89
 *	Last Modified:	05 Jan 91
 *	Comments:
 *	History:
 *		02 Sep 89/JVE	Created.
 *		28 Sep 89/JVE	ParseArticle now stores position within file where
 *						headers end and text begins in article->textpos.
 *		30 Dec 89/JVE	Added "Sender:" to list of useful headers.  This list
 *						will go away when there is a config file to do same.
 *		18 Oct 90/JVE	Use HDR_xxx defines instead of hardcoded strings.
 *						Added GetHeader.
 *		21 Oct 90/JVE	Don't end header search until blank line.
 *		05 Jan 91/JVE	New "subject" field in article_info initialized to
 *						a NULL pointer and set to point at the SUBJECT header
 *						fieldvalue after all headers have been read.
 */

# include	<stdio.h>
# include	<stdlib.h>
# include	<string.h>
# include	"standard.h"
# include	"article.h"
# include	"configure.h"
# include	"variables.h"


/****************************************************************************/
/*	FUNCTION:	ParseArticle												*/
/*																			*/
/*	PURPOSE:	Parses headers from article & creates article/header structs*/
/*																			*/
/*	INPUT PARAMETERS:														*/
/*		NAME		I/O		DESCRIPTION										*/
/*		----		---		-----------										*/
/*		filename	 I		Name of file containing article.				*/
/*																			*/
/*	RETURNS:																*/
/*		(ARTICLE_INFO *)	Pointer to newly allocated article structure.	*/
/*		NULLP(ARTICLE_INFO)	Indication of failure.							*/
/*																			*/
/*	COMMENTS:																*/
/*		Assumes filename contains only digits and is same as article 		*/
/*		number.																*/
/*																			*/
/*	HISTORY:																*/
/*		1.	02 Sep 89		Created.										*/
/*		2.	28 Sep 89		Added textpos to say where headers end.			*/
/*																			*/
/****************************************************************************/

ARTICLE_INFO *ParseArticle( filename )

char	*filename;

{
FILE			*fp;
long			id;			/* article number (aka filename) */
ARTICLE_INFO	*article;	/* ptr to article information */
HEADER_INFO		**hdr;		/* handle for header field allocation */
HEADER_INFO		*h;

if ( ( fp = fopen( filename, "r" ) ) == NULLP( FILE ) )
	{
	perror( filename );
	return NULLP( ARTICLE_INFO );
	}
else if ( ( id = atol( filename ) ) <= 0 )
	{
	printf( "%s: Bad article number.\n", filename );
	return NULLP( ARTICLE_INFO );
	}

/*
 *	Initialize article info structure.
 */

article = (ARTICLE_INFO *) malloc( sizeof( ARTICLE_INFO ) );
article->number = id;
article->next = NULLP( ARTICLE_INFO );
article->headers = NULLP( HEADER_INFO );
article->beenread = FALSE;
article->textpos = 0L;
article->done = FALSE;
article->subject = NULLP( char );

/*
 *	Get all header fields.
 */

hdr = &article->headers;
while ( GetNextHeader( fp, &hdr ) ) 
	article->textpos = ftell( fp );
fclose( fp );
for ( h = article -> headers; h != NULLP( HEADER_INFO ); h = h -> next )
	if ( strcmp( h -> fieldname, HDR_SUBJECT ) == 0 )
		{
		article -> subject = h -> fieldvalue;
		break;
		}
return article;
}


/****************************************************************************/
/*	FUNCTION:	GetNextHeader												*/
/*																			*/
/*	PURPOSE:	Parses the next header field out of an article.				*/
/*																			*/
/*	INPUT PARAMETERS:														*/
/*		NAME		I/O		DESCRIPTION										*/
/*		----		---		-----------										*/
/*		fp			 I		FILE pointer to file containing article.		*/
/*		hdr			I/O		Where to put pointer to newly allocated			*/
/*							HEADER_INFO structure if we allocate one.		*/
/*																			*/
/*	RETURNS:																*/
/*		TRUE	Successfully parsed another header from article.			*/
/*		FALSE	Failed/no more headers in article.							*/
/*																			*/
/*	COMMENTS:																*/
/*		Adjusts hdr when done so next call will set correct pointer.		*/
/*		BBS version terminates header search on a line without a colon,		*/
/*		or with a space before the colon.  Usenet version terminates		*/
/*		header search on blank line, ignoring "malformed" header lines.		*/
/*																			*/
/*	HISTORY:																*/
/*		1.	02 Sep 89		Created.										*/
/*		2.	30 Dec 89		Added "Sender:" to list of useful headers.		*/
/*		3.	21 Oct 90		Don't end header search until blank line.		*/
/*																			*/
/****************************************************************************/

GetNextHeader( fp, hdr )

FILE			*fp;
HEADER_INFO		***hdr;

{
int				rc;				/* return code from function */
char			buf[BUFSIZ];	/* holds input line from article */
char			*firstspace, *firstcolon, *data;	/* for parsing */

if ( fgets( buf, BUFSIZ, fp ) == NULL )
	rc = FALSE;
else
	{
	firstspace = strchr( buf, ' ' );
	firstcolon = strchr( buf, ':' );
	if ( ( firstcolon == NULL ) || 
		( ( firstspace != NULL ) && ( firstspace < firstcolon ) ) )
			{
			if ( GetVar( VAR_BBS ) || strspn( buf, " \t\r\n" ) == strlen(buf) )
				rc = FALSE;
			else					/* junk line, don't end header search */
				rc = TRUE;
			}
	else
		{
		*firstcolon = NULL;		/* terminate fieldname string */
# ifndef ARN_CONFIG
/*
 *	Until a useful way is found to restrict output to interesting headers,
 *	use this kluge.
 *
 *	Don't actually allocate a header struct because we're not interested in
 *	other headers, but return TRUE so that we continue to try to get headers.
 */
	if ( strcmp( buf, HDR_FROM ) && strcmp( buf, HDR_SUBJECT ) &&
		strcmp( buf, HDR_DATE ) && strcmp( buf, HDR_SENDER ) && 
		strcmp( buf, HDR_TO ) )
			return TRUE;
# endif
		firstcolon++;			/* advance past colon/NULL to value string */
		data = firstcolon + strspn( firstcolon, " \t" );	/* skip white */
		data[strlen(data)-1] = NULL;	/* remove trailing newline */

		/*
		 *	Allocate and fill a new header structure.
		 */

		**hdr = (HEADER_INFO *) malloc( sizeof( HEADER_INFO ) );
		(**hdr)->fieldname = strdup( buf );
		(**hdr)->fieldvalue = strdup( data );
		(**hdr)->next = NULLP( HEADER_INFO );

		/*
		 *	Reset the hdr pointer so next call will continue chain correctly.
		 */

		*hdr = &((**hdr)->next);
		rc = TRUE;
		}
	}
return rc;
}


/****************************************************************************/
/*	FUNCTION:	DumpArticle													*/
/*																			*/
/*	PURPOSE:	Print article/header info for an article.					*/
/*																			*/
/*	INPUT PARAMETERS:														*/
/*		NAME		I/O		DESCRIPTION										*/
/*		----		---		-----------										*/
/*		article		 I		Pointer to article structure to dump.			*/
/*																			*/
/*	RETURNS: none															*/
/*																			*/
/*	COMMENTS:																*/
/*		Debugging function.													*/
/*																			*/
/*	HISTORY:																*/
/*		1.	02 Sep 89		Created.										*/
/*																			*/
/****************************************************************************/

void DumpArticle( article )

ARTICLE_INFO	*article;

{
HEADER_INFO		*hdr;

printf( "Article: %ld\n", article->number );
for ( hdr = article->headers; hdr != NULLP( HEADER_INFO ); hdr = hdr->next )
	printf( "\t%s: %s\n", hdr->fieldname, hdr->fieldvalue );
printf( "\n" );
}


/****************************************************************************/
/*	FUNCTION:	DestroyArticle												*/
/*																			*/
/*	PURPOSE:	Free all space taken by article/header structure.			*/
/*																			*/
/*	INPUT PARAMETERS:														*/
/*		NAME		I/O		DESCRIPTION										*/
/*		----		---		-----------										*/
/*		article		 I		The article being destroyed.					*/
/*																			*/
/*	RETURNS:																*/
/*																			*/
/*	COMMENTS:																*/
/*																			*/
/*	HISTORY:																*/
/*		1.	02 Sep 89		Created.										*/
/*																			*/
/****************************************************************************/

void DestroyArticle( article )

ARTICLE_INFO	*article;

{
HEADER_INFO		*hdr, *tmp;

for ( hdr = article->headers; hdr != NULLP( HEADER_INFO ); hdr = tmp )
	{
	free( hdr->fieldname );
	free( hdr->fieldvalue );
	tmp = hdr->next;
	free( hdr );
	}
free( article );
}


/****************************************************************************/
/*	FUNCTION:	GetHeader													*/
/*																			*/
/*	PURPOSE:	Retrieve a specific header from a message.					*/
/*																			*/
/*	INPUT PARAMETERS:														*/
/*		NAME		I/O		DESCRIPTION										*/
/*		----		---		-----------										*/
/*		headers		 I		List of headers.								*/
/*		name		 I		Name of header to retrieve.						*/
/*																			*/
/*	RETURNS:																*/
/*																			*/
/*	COMMENTS:																*/
/*		Makes a local copy of the header value to be returned.  Gets		*/
/*		overwritten each call.												*/
/*																			*/
/*	HISTORY:																*/
/*		1.	18 Oct 90		Created.										*/
/*																			*/
/****************************************************************************/

char	*GetHeader( headers, name )

HEADER_INFO		*headers;
char			*name;

{
static char		local[MAXLINE];

while ( headers )
	{
	if ( strcmp( headers -> fieldname, name ) == 0 )
		{
		strcpy( local, headers -> fieldvalue );
		return local;
		}
	headers = headers -> next;
	}
return (char *) NULL;
}
