/*
 *	Copyright (C) 1984, 1985  SRI International
 *	Copyright (C) 1988, 1989  TGV, Incorporated
 *
 *	Routines to read in a mail file
 *
 */
#include "gtjfn.h"
#include "jfns.h"
#include <stat.h>
#include <stdio.h>
#include <ctype.h>
#include "vax-mm.h"

static Get_Examine_Common();
extern char *Topsify_Filename();
extern int Do_Create_Init();

/*
 *	Process the "GET" command
 */
CMD_Get() {Get_Examine_Common(0);}

/*
 *	Process the "EXAMINE" command
 */
CMD_Examine() {Get_Examine_Common(1);}


/*
 *	Common code for get/examine commands
 */
static Get_Examine_Common(Open_Read_Only)
{
	static struct comnd_function file = {COMND_INPUT_FILE};
	static struct gtjfn_block gtjfn;
	int i;
	char Filename[128];

	/*
	 *	Noise
	 */
	Noise("msgs from file");
	/*
	 *	Parse the filename
	 */
	gtjfn.default_filename = "MAIL";
	gtjfn.default_extension = "TXT";
	Command_State.gtjfn = &gtjfn;
	if (COMND(&Command_State,&file,&i,0) < 0) return;
	if (local_jfns_jsys(Filename,i) < 0) return;
	tops_free_jfn(i);	/* TEMP */
	/*
	 *	Confirm
	 */
	Confirm();
	/*
	 *	Close the OLD file
	 */
	if (DOING_BBOARD) Set_Bboard_Read_Date1();
	Update_Header_File(0);
	close(Text_File_FD);
	/*
	 *	Get the file
	 */
	Read_Only = Open_Read_Only;
	GetFile(Filename);
	/*
	 *	Do the summary
	 */
	if (!Doing_Rscan) {Recent(1); Summary();}
}


/*
 *	Process the "BBOARD" command
 */
CMD_MSbboard()
{
	static unsigned long int Break_Mask[4] = {
		  0x0,		/* */
		  0xfc009fff,	/* space->comma,slash,colon->question */
		  0x78000001,	/* atsign, open bracket->caret */
		  0xf8000001};	/* accent grave, close bracket->tilde */
	static struct comnd_function file = {COMND_INPUT_FILE};
	static struct comnd_function keyword = {
			COMND_KEYWORD,
			COMND_HELP_VALID | COMND_BREAK,
			&file,
			0,
			"bulletin board mailbox, ",
			0,
			Break_Mask};
	register char *cp,*cp1;
	int i,jfn;
	struct comnd_function *f;
	char Filename[128];
	int String_Size;
	int String_Space_Left;
	static char *Strings = 0;
	static struct Bboard_Keywords {
		int current_entries;
		int maximum_entries;
		struct tbluk_keyword keywords[1];
		} *Bboard_Keywords = 0;
	static struct gtjfn_block gtjfn;

	/*
	 *	Setup the defaults from the DEFAULT-BBOARD variable
	 */
	bzero(&gtjfn,sizeof(gtjfn));
	cp = Default_Bboard;
	cp1 = Filename;
	if (*cp) {
		/*
		 *	Device spec:
		 */
		while(*cp)
			if ((*cp1++ = *cp++) == ':') break;
		if (*cp) {
			gtjfn.default_device = Filename;
			*cp1++ = 0;
		} else {
			cp = Default_Bboard;
		}
	}
	if ((*cp == '<') || (*cp == '[')) {
		gtjfn.default_directory = cp1;
		while(*cp) {
			if ((*cp1++ = *cp++) == '>') break;
			if (cp[-1] == ']') break;
		}
		*cp1++ = 0;
	}
	if (*cp != '.') {
		gtjfn.default_filename = cp1;
		while(*cp)
			if ((*cp1++ = *cp++) == '.') {cp--; cp1--; break;}
		*cp1++ = 0;
	}
	if (*cp == '.') {
		cp++;
		gtjfn.default_extension = cp1;
		while((*cp1++ = *cp++) != 0) ;
		*cp1 = 0;
	}
	/*
	 *	If we already have the list of bboards, we can
	 *	skip the following code that generates the list
	 *
	 *	(we can also skip the code if we are doing Rscan)
	 */
	if (!Bboard_Keywords && !Doing_Rscan) {
	  /*
	   *	Get a wildcard JFN on which to search for BBOARDS
	   */
	  gtjfn.flags = GTJFN_OLD_FILE | GTJFN_INPUT_FILE_GROUP;
	  if (gtjfn_jsys(&gtjfn,"*.TXT",&jfn) >= 0) {
		/*
		 *	Make an initial allocation of 2kb strings
		 */
		String_Size = 0;
		String_Space_Left = 2048;
		Strings = (char *)mm_malloc(2048);
		if (Strings == 0) {
			Bboard_Keywords = 0;
			goto Done;	/* Out of Memory! */
		}
		/*
		 *	Make an initial allocation of 32 keyword entries
		 */
		Bboard_Keywords =
			(struct Bboard_Keywords *)
				mm_malloc(sizeof(struct Bboard_Keywords) +
				       (31 * sizeof(struct tbluk_keyword)));
		if (Bboard_Keywords == 0) goto Done; /* Out of Memory! */
		Bboard_Keywords->current_entries = 0;
		Bboard_Keywords->maximum_entries = 32;
		/*
		 *	Scan all the matching filenames and build
		 *	the BBOARD lookup table
		 */
		while(1) {
			char Buffer[1024];

			/*
			 *	Get the next JFN
			 */
			if (gnjfn_jsys(jfn,&i) < 0) break;
			/*
			 *	Make sure there are enough keywords
			 */
			if (Bboard_Keywords->current_entries ==
				Bboard_Keywords->maximum_entries) {
				/*
				 *	Double the # of keywords
				 */
				Bboard_Keywords->maximum_entries *= 2;
				Bboard_Keywords =
				  (struct Bboard_Keywords *)
				    mm_realloc(
					Bboard_Keywords,
					sizeof(struct Bboard_Keywords) +
					 (Bboard_Keywords->maximum_entries - 1) *
					   sizeof(struct tbluk_keyword));
				if (Bboard_Keywords == 0) goto Done;
			}
			/*
			 *	Get the FILENAME part of the filespec
			 */
			jfns_jsys1(Buffer,
				   jfn,
				   JFNS_DEVICE(JFNS_DONT_OUTPUT) |
				    JFNS_DIRECTORY(JFNS_DONT_OUTPUT) |
				    JFNS_VERSION(JFNS_DONT_OUTPUT));
			/*
			 *	Check (and ignore) filenames starting with
			 *	$HDRS$ (these are header cache files)
			 */
			if (strncmp(Buffer,"$HDRS$",6) == 0) continue;
			/*
			 *	Kill the extension
			 */
			cp = (char *)strrchr(Buffer,'.');
			if (cp) *cp = 0;
			/*
			 *	Save this in string space
			 *	and set as keyword name offset
			 */
			i = strlen(Buffer) + 1;
			if (String_Space_Left < i) {
				/*
				 *	Bump string space by 4kb
				 */
				String_Space_Left += 4096;
				Strings = (char *)mm_realloc(Strings,
							  String_Size +
							   String_Space_Left);
				if (Strings == 0) goto Done;
			}
			cp = Strings + String_Size;
			bcopy(Buffer,cp,i);
			Bboard_Keywords->
				keywords[Bboard_Keywords->current_entries].
					flags = 0;
			Bboard_Keywords->
				keywords[Bboard_Keywords->current_entries].
					keyword = (char *)String_Size;
			String_Size += i;
			String_Space_Left -= i;
			/*
			 *	Get the local FILENAME representation
			 */
			local_jfns_jsys(Buffer,jfn);
			/*
			 *	Save this in string space
			 *	and set as keyword data
			 */
			i = strlen(Buffer) + 1;
			if (String_Space_Left < i) {
				/*
				 *	Bump string space by 4kb
				 */
				String_Space_Left += 4096;
				Strings = (char *)mm_realloc(Strings,
							  String_Size +
							   String_Space_Left);
				if (Strings == 0) goto Done;
			}
			cp = Strings + String_Size;
			bcopy(Buffer,cp,i);
			Bboard_Keywords->
				keywords[Bboard_Keywords->current_entries].
					user_data = String_Size;
			String_Size += i;
			String_Space_Left -= i;
			/*
			 *	Insert this entry in the lookup table
			 */
			Bboard_Keywords->current_entries++;
		}
		/*
		 *	Turn offsets into string pointers
		 */
		for (i = 0; i < Bboard_Keywords->current_entries; i++) {
			Bboard_Keywords->keywords[i].keyword += (int)Strings;
			Bboard_Keywords->keywords[i].user_data += (int)Strings;
		}
		/*
		 *	Make it the data for the "keyword" comnd_function
		 */
		keyword.data = (int)Bboard_Keywords;
		/*
		 *	Release the JFN
		 */
Done:		rljfn_jsys(jfn);
	  } else {
		/*
		 *	No match -- no bboards!
		 */
		Bboard_Keywords = 0;
	  }
	}
	/*
	 *	Setup the gtjfn block so that we can look for matching
	 *	filenames
	 */
	gtjfn.flags = 0;
	Command_State.gtjfn = &gtjfn;
	/*
	 *	Parse the filename/keyword
	 */
	file.default_string = Default_Bboard;
	keyword.default_string = Default_Bboard;
	if (Default_Bboard[0] != 0) {
		file.flags |=  COMND_DEFAULT_VALID;
		keyword.flags |=  COMND_DEFAULT_VALID;
	} else {
		file.flags &= ~COMND_DEFAULT_VALID;
		keyword.flags &= ~COMND_DEFAULT_VALID;
	}
	f = Bboard_Keywords ? &keyword : &file;
	if (COMND(&Command_State,f,&i,&f) < 0) return;
	/*
	 *	Get the bboard filename local representation
	 */
	if (f->code == COMND_KEYWORD) {
		/*
		 *	Get it from the keyword data
		 */
		strcpy(Filename,((struct tbluk_keyword *)i)->user_data);
	} else {
		/*
		 *	Get it from the JFN
		 */
		if (local_jfns_jsys(Filename,i) < 0) return;
		tops_free_jfn(i);	/* TEMP */
	}
	/*
	 *	Confirm
	 */
	Confirm();
	/*
	 *	Get the bboard file
	 */
	if (DOING_BBOARD) Set_Bboard_Read_Date1();
	close(Text_File_FD);
	Read_Only = 2;
	GetFile(Filename);
	Read_Only = 2;		/* GetFile() may have set this to 1 */
	/*
	 *	Get the last message read info
	 */
	Get_Bboard_Read_Date1();
	/*
	 *	Do the summary
	 */
	if (!Doing_Rscan) {Recent(1); Summary();}
}

/*
 *	Get the BBoard last read date
 */
Get_Bboard_Read_Date1()
{
	int i;

	/*
	 *	Get the last read date for this bboard
	 */
	Get_Bboard_Read_Date(Current_Mail_File, &Message_File_Last_Modified);
	/*
	 *	Fix all the read messages
	 */
	for(i = 0; i < Last_Message; i++) {
		if ((unsigned long int)Messages[i].Date <= Message_File_Last_Modified)
				Messages[i].Flags |= MSG_SEEN;
	}
}

/*
 *	Set the BBoard last read date
 */
Set_Bboard_Read_Date1()
{
	int i;
	int Date;

	i = Last_Message;
	while(--i >= 0) {
		if (Messages[i].Flags & MSG_SEEN) {
			Date = Messages[i].Date;
			break;
		}
	}
	if (i < 0) Date = 0;
	Set_Bboard_Read_Date(Current_Mail_File, Date);
}


/*
 *
 *	Read in a specified mail file
 *
 */
GetFile(File_Name)
char *File_Name;
{
	struct stat s;
	register char *cp,*cp1;
	register int i;
	register struct Msg *msg;
	char Local[256],Local1[256];
	int j, Getting_Home_Mail_File = 0;

	strcpy(Local, Topsify_Filename(File_Name));
	strcpy(Local1, Topsify_Filename(Home_File(MM_MAIL_FILE)));
	if (strcmp(Local, Local1) == 0) {
		Getting_Home_Mail_File = 1;
		if (Second_MM_Invocation) Read_Only = 1;
		/*
		 *	Take out delivery lock if home mail file
		 */
		Super_Lock();
	}

	/*
	 *	Check for write access
	 */
Again:	if (Xaccess(File_Name,2) < 0) Read_Only = 1;

	/*
	 *	Open the text file, and get its size
	 */
#ifndef vax11c
	Text_File_FD = Xopen(File_Name,0);
#else	vax11c
	Text_File_FD = Vax11_C_Shared_Open(File_Name,0);
#endif	vax11c
	if (Text_File_FD < 0) {
		if (Getting_Home_Mail_File) {
			int fd, done = 0;
			static struct {
				int current_entries;
				int maximum_entries;
				struct tbluk_keyword keywords[3];
				} Table = {
				3,
				3,
				{{0,	"no",	0},
				 {0,	"quit", 1},
				 {0,	"yes",	2}}};
			static struct comnd_function Responses =
				{COMND_KEYWORD,
				 COMND_HELP_VALID|COMND_SUPPRESS_DEFAULT_HELP,
				 0,
				 (int)&Table,
				 "YES, NO or QUIT"};

			static struct {
				int current_entries;
				int maximum_entries;
				struct tbluk_keyword keywords[2];
				} Delivery_Table = {
				2,
				2,
				{{0,	"no",	0},
				 {0,	"yes",	1}}};
			static struct comnd_function Delivery_Responses =
				{COMND_KEYWORD,
				 COMND_HELP_VALID|COMND_SUPPRESS_DEFAULT_HELP,
				 0,
				 (int)&Delivery_Table,
				 "YES or NO"};

			struct tbluk_keyword *t;

			/*
			 *	Make the user confirm that a new MAIL.TXT
			 *	file is to be created!
			 */
			Command_State.prompt =
			 "No MAIL.TXT file exists, create it? (YES/NO/QUIT) ";
			while(1) {
				COMND_INIT(&Command_State);
				if (COMND(&Command_State,&Responses,&t,0) < 0)
					goto Again;
				Confirm();
				/*
				 *	Dispatch on keyword
				 */
				switch(t->user_data) {
					case 0: goto Again;
					case 1: exit(0);
					case 2: break;
					default: goto Again;
				}
				break;
			}

			/*
			 *	Ask if the user wants all mail delivered to
			 *	MM, or if he wants to continue it to go to
			 *	VMS MAIL.
			 */
			Command_State.prompt =
			 "\nHave all mail delivered to MM? (YES/NO) ";
			while (!done) {
				COMND_INIT(&Command_State);
				if (COMND(&Command_State,
					  &Delivery_Responses,&t,0) < 0)
				Confirm();
				/*
				 *	Dispatch on keyword
				 */
				switch(t->user_data) {
					case 0: Delivery_To_MM = 0;
						Do_Create_Init();
					case 1:	done++;
						break;
				}
				break;
			}

			/*
			 *	Create the HOME mail file
			 */
			close(Xcreat(File_Name,Mail_File_Mode,0));
			Text_File_FD = Vax11_C_Shared_Open(File_Name,0);
			/*
			 *	Create the empty header file
			 */
			Header_File_Name(File_Name,Local);
			fd = Xcreat(Local,Mail_File_Mode,0);
			if (fd >= 0) {
				Header_File_Header.Last_Read = 0;
				Header_File_Header.Number_Of_Messages = 0;
				Header_File_Header.Size_Of_Text_File = 0;
				write(fd,
				      &Header_File_Header,
				      sizeof(Header_File_Header));
				close(fd);
			}
		}
		if (Text_File_FD < 0) {
			printf("Couldn't open %s\n",File_Name);
			/*	Release delivery lock if taken out */
			if (Getting_Home_Mail_File) Super_Unlock();
			return(-1);
		}
		Read_Only = 0;
	}
	if (fstat(Text_File_FD,&s) < 0) {
		printf("Stat error on %s\n",File_Name);
		close(Text_File_FD);
		/*	Release delivery lock if taken out */
		if (Getting_Home_Mail_File) Super_Unlock();
		return;
	}
	Text_File_Mode = s.st_mode;		/* Remember text file mode */
	Header_File_Mode = s.st_mode;		/* and header file mode    */
	i = (s.st_size*20)/100;
	if (i < 5000) i = 5000; /* Allocate text size + 20% or 5000 */
	if (Message_Text) mm_free(Message_Text);
	Message_Text_Size = s.st_size + i;
	Message_Text = (char *)mm_malloc(Message_Text_Size);
	if (Message_Text == 0) {
		printf("ERROR, insufficient memory for MAIL.TXT file\n");
		exit_MM(-1);
	}
	Message_File_Modified = 0;
	Message_File_Last_Modified = s.st_mtime;
	Message_File_Size = s.st_size;
	Last_Message_File_EOF = s.st_size;
	/*
	 *	Declare the new mail file name
	 */
	strcpy(Current_Mail_File,File_Name);
	/*
	 *	Try to open the header file
	 */
	Header_File_Name(File_Name,Local);
	Header_File_FD = Vax11_C_Shared_Open(Local,Read_Only ? 0 : 2);
	/*
	 *	If we failed, try renaming the OLD-style header file name
	 */
	if (Header_File_FD < 0) {
		/*
		 *	Rename the File_Name with the extension ".hdr"
		 *	to the REAL header file name.
		 */
		New_Extension(File_Name,Local1,"hdr");
		if (Vax11_C_rename(Local1,Local) == 0) {
			Header_File_FD = Vax11_C_Shared_Open(Local,Read_Only ? 0 : 2);
		}
	}
	/*
	 *	If the header file is open read it in
	 */
	if (Header_File_FD > 0) {
		/*
		 *	Got it, read it in
		 */
		if (read(Header_File_FD,
			 &Header_File_Header,
			 sizeof(Header_File_Header)) !=
				sizeof(Header_File_Header)) {
			printf("[ERROR: Header file \"header\" too small]\n");
			goto New_Header_File;
		}
		if (Header_File_Header.Size_Of_Text_File !=
							Message_File_Size) {
			printf("[ERROR: Header and text file don't agree on size (%d vs %d bytes)]\n",
				Header_File_Header.Size_Of_Text_File,
				Message_File_Size);
			goto New_Header_File;
		}
		Last_Message = Header_File_Header.Number_Of_Messages;
		if (Max_Messages < Last_Message) {
			Max_Messages = Last_Message;
			if (Max_Messages == 0) Max_Messages = 10;
			if (Messages == 0) {
				Messages = (struct Msg *)
					mm_malloc(sizeof(struct Msg)*Max_Messages);
			} else {
				Messages = (struct Msg *)
					mm_realloc(Messages,
						sizeof(struct Msg)*Max_Messages);
			}
		}
		if (read(Header_File_FD,
			 Messages,
			 sizeof(struct Msg)*Last_Message) !=
				sizeof(struct Msg)*Last_Message) {
			mm_free(Messages);
			Messages = 0;
			printf("[ERROR: Header file missing some headers]\n");
			goto New_Header_File;
		}
		/*
		 *	Clear all the message pointers and calculate max size
		 */
		j = 0;
		for(i = 0; i < Last_Message; i++) {
			msg = &Messages[i];
			msg->Header = 0;
			msg->Flags &= ~MSG_NEEDS_UPDATING;
			j += msg->Real_Size;
		}
		if (j < Message_File_Size) Message_File_Size = j;
		/*
		 *	Update modified times
		 */
		Message_File_Last_Modified = Header_File_Header.Last_Read;
		Header_File_Header.Last_Read = time(0);
		Update_Header_File(1);
		/*
		 *	Close the header file
		 */
		close(Header_File_FD);
		Header_File_FD = -1;
		/*
		 *	Done
		 */
		goto Done;

New_Header_File:
		/*
		 *	Delete the header file and re-create it
		 */
		close(Header_File_FD);
Again1:		if (!Read_Only) {
			static struct {
				int current_entries;
				int maximum_entries;
				struct tbluk_keyword keywords[3];
				} Table = {
				3,
				3,
				{{0,	"no",	0},
				 {0,	"quit", 1},
				 {0,	"yes",	2}}};
			static struct comnd_function Responses =
				{COMND_KEYWORD,
				 COMND_HELP_VALID|COMND_SUPPRESS_DEFAULT_HELP,
				 0,
				 (int)&Table,
				 "YES, NO or QUIT"};
			struct tbluk_keyword *t;

			/*
			 *	Make the user confirm that a new header file
			 *	file is to be created!
			 */
			if (Doing_Take_File == 0) {
			  Command_State.prompt =
			     "Inconsistent header file, re-create from text file? (YES/NO/QUIT) ";
			  COMND_INIT(&Command_State);
			  if (COMND(&Command_State,&Responses,&t,0) < 0)
								goto Again1;
			  Confirm();
			  /*
			   *	Dispatch on keyword
			   */
			  switch(t->user_data) {
				case 0: Read_Only = 1;
					printf("[Changing mail file access to Read/Only]\n");
					goto Again1;
				case 1: exit(0);
				case 2: break;
				default: goto Again1;
			  }
			}
			/*
			 *	Re-create the header file
			 */
			unlink(Local);
			printf("[Inconsistent Header file, recreating from Text file]\n");
		}
	} else {
		if ((Message_File_Size != 0) && !Read_Only)
				printf("[Creating Header file]\n");
	}
	/*
	 *	Read in the text file
	 */
	s.st_size = read(Text_File_FD,Message_Text,s.st_size);
	Message_File_Size = s.st_size;
	/*
	 *	Start out with space for 300 messages
	 */
	Last_Message = 0;
	Max_Messages = 300;
	if (Messages) mm_free(Messages);
	Messages = (struct Msg *)mm_malloc(sizeof(struct Msg)*Max_Messages);
	msg = &Messages[0];
	/*
	 *	Start parsing the text
	 */
	i = s.st_size;
	cp = Message_Text;
	while(1) {
		/*
		 *	When chars are exhausted, break
		 */
		if ((i <= 0) || (*cp == 0))break;
		/*
		 *	New message, make sure we have room for another
		 *	Msg structure and initialize it
		 */
		if (Last_Message == Max_Messages) {
			/*
			 *	No room, allocate double the space!
			 */
			Max_Messages *= 2;
			Messages = (struct Msg *)
				mm_realloc(Messages,sizeof(struct Msg)*Max_Messages);
			msg = &Messages[Last_Message];
		}
		/*
		 *	Parse the message header
		 */
		j = Parse_Message_Header(msg,cp,i);
		if (j < 0) goto Header_Error;
		i -= j;
		cp += j;
		/*
		 *	Increment the message count
		 */
		Last_Message++;
		msg++;
	}
	/*
	 *	Write the Header file
	 */
	if (!Read_Only) {
		Header_File_FD = Xcreat(Local,Mail_File_Mode,0);
		Header_File_Header.Last_Read = time(0);
		Header_File_Header.Number_Of_Messages = Last_Message;
		Header_File_Header.Size_Of_Text_File = Message_File_Size;
		write(Header_File_FD,&Header_File_Header,sizeof(Header_File_Header));
		write(Header_File_FD,Messages,sizeof(struct Msg)*Last_Message);
		close(Header_File_FD);
		Header_File_FD = -1;
	}

	/*
	 *	Done
	 */
Done:	Current_Message = Last_Message;
	if (Current_Message == 0) Current_Message = 1;
	/*
	 *	Release delivery lock if taken out
	 */
	if (Getting_Home_Mail_File) Super_Unlock();
	return;

Header_Error:
	/*
	 *	Error found in header
	 */
	printf("Malformed mail file\n");
	goto Done;
}


int Parse_Message_Header(Msg,String,Max_Size)
register struct Msg *Msg;
char *String;
int Max_Size;
{
	register char *cp,*cp1;
	register int i,j;
	int msg_num;
	int Time[2], LocalTime[2];
	char Local[128];
	static char flip_bits[] = {0,04,02,06,01,05,03,07};
	char error_message[128] = "?Generic header parse error";

	cp = String;
	i = Max_Size;
	msg_num = (Msg - Messages) + 1;

	/*
	 *	Initialize the start of the message structure
	 */
	Msg->Size = 0;
	Msg->Real_Size = 0;
	Msg->Header = cp;

	/*
	 *	Get the Header Line Date
	 */
	cp1 = Local;
	j = i;
	if (j > (sizeof(Local) - 1)) j = (sizeof(Local) - 1);
	while((*cp != ',') && (j > 0)) {*cp1++ = *cp++; j--;}
	i -= (sizeof(Local) - 1) - j;
	*cp1 = 0;
	if (idtim_jsys(Local,0,Time) < 0) {
		strcpy(error_message, "?Invalid timestamp");
		goto Header_Error;
	}
    	gmt_to_localtime(Time, LocalTime, 0);
	Msg->Date = LocalTime[0];
	/*
	 *	Get message size (\n = 2 characters!)
	 */
	if (*cp++ != ',') {
		strcpy(error_message, "?Missing comma after timestamp");
		goto Header_Error;
	}
	i--;
	j = 0;
	while(*cp != ';') {
		if(--i < 0) {
			strcpy(error_message, "?Ran out of message while looking for message length");
			goto Header_Error;
		}
		if ((*cp < '0') || (*cp > '9')) {
			strcpy(error_message, "?Length not a number");
			goto Header_Error;
		}
		j *= 10;
		j += *cp++ - '0';
	}
	Msg->Size = j;
	cp++;
	if(--i < 0) {
		strcpy(error_message, "?Ran out of message before reading flags");
		goto Header_Error;
	}

	/*
	 *	Get keyword flags
	 */
	i -= 13;
	if (i < 0) {
		strcpy(error_message, "?Ran out of message while reading flags");
		goto Header_Error;
	}
	Msg->Keyword_Flags = 0;
	for(j = 0;j < 10; j++) {
		if ((*cp < '0') || (*cp > '7')) {
			strcpy(error_message, "?Invalid keyword flags");
			goto Header_Error;
		}
		Msg->Keyword_Flags |=
			(flip_bits[*cp++ - '0']) << (3*j);
	}

	/*
	 *	Get normal flags
	 */
	Msg->Flags = 0;
	if ((*cp < '0') || (*cp > '7')) {
		strcpy(error_message, "?Invalid normal flags");
		goto Header_Error;
	}
	Msg->Flags = (*cp++ - '0') << 3;
	if ((*cp < '0') || (*cp > '7')) {
		strcpy(error_message, "?Invalid normal flags");
		goto Header_Error;
	}
	Msg->Flags |= *cp++ - '0';

	/*
	 *	Skip any blanks at the end of this line
	 *	(fix for HERMES mail files)
	 */
	while((*cp == ' ') && (i > 0)) {cp++; i--;}

	/*
	 *	Calculate REAL size (in bytes)
	 */
	if (*cp++ != '\n') {
		strcpy(error_message, "?Date/size line not terminated properly");
		goto Header_Error;
	}
	Msg->Header_Size = cp - Msg->Header;
	{
		int Size = Msg->Size;

		Msg->Real_Size = Msg->Header_Size;
		while(--Size >= 0) {
			if (--i < 0) {
				sprintf(error_message, "?Message too short (%d left)", Size);
				goto Header_Error;
			}
			Msg->Real_Size++;
			if (*cp++ == '\n') Size--;
		}
	}

	/*
	 *	ROBUSTNESS CODE:  Scan until the next valid
	 *			  Header and include the text
	 *			  in the current message
	 */
	i++;
	Msg->Real_Size--;
	cp--;
	while(--i >= 0) {
		Msg->Real_Size++;
		if (*cp++ != '\n') continue;
		if (i < 21) continue;	/* Insufficient characters for hdr! */
		if (cp[2] == '-') {
			if ( (cp[6] == '-') &&
			     (cp[9] == ' ') &&
			     (cp[12] == ':') &&
			     (cp[15] == ':') &&
			     (cp[18] == '-') ) break;
		} else {
			if (cp[1] == '-') {
				if ( (cp[5] == '-') &&
				     (cp[8] == ' ') &&
				     (cp[11] == ':') &&
				     (cp[14] == ':') &&
				     (cp[17] == '-') &&
				     (cp[21] == ',') ) break;
			}
		}
	}

	/*
	 *	Find the entire message HEADER
	 *	(delimited by a blank line [white space is allowed!])
	 */
	cp1 = Msg->Header + Msg->Header_Size;
	j = Msg->Real_Size - Msg->Header_Size;
	while(--j >= 0) {

		/*
		 *	Look for beginning of line
		 */
		if (*cp1++ != '\n') continue;

		/*
		 *	Skip any white space
		 */
		if (((*cp1 == ' ') || (*cp1 == '\t')) && (j > 0)) {
			j--;
			cp1++;
		}

		/*
		 *	If this is the end of the line,
		 *	we are now at the end of the header
		 */
		if (*cp1 == '\n') {cp1++; break;}
	}
	Msg->Body_Offset = cp1 - Msg->Header;

	/*
	 *	Find the "Subject" field
	 */
	cp1 = Msg->Header + Msg->Header_Size;
	j = Msg->Body_Offset - Msg->Header_Size;
	if (strncasecmp("Subject:",cp1,8)) {
		while(--j >= 0) {
			if (*cp1++ != '\n') continue;
			if (!strncasecmp("Subject:",cp1,8)) break;
		}
	}

	if (j < 0) {
		Msg->Subj_Offset = 0;
		Msg->Subj_Size = 0;
	} else {
		Msg->Subj_Offset = cp1 - Msg->Header;
		Msg->Subj_Size = 0;
		while((*cp1++ != '\n') && (--j >= 0)) Msg->Subj_Size++;
		Msg->Subj_Offset += 9;		/* Length of "Subject: " */
		Msg->Subj_Size -= 9;
	}

	/*
	 *	Find the "From" field
	 */
	cp1 = Msg->Header + Msg->Header_Size;
	j = Msg->Body_Offset - Msg->Header_Size;
	if (strncasecmp("From:",cp1,5)) {
		while(--j >= 0) {
			if (*cp1++ != '\n') continue;
			if (!strncasecmp("From:",cp1,5)) break;
		}
	}
	if (j < 0) {
		/*
		 *	Try for "Sender:"
		 */
		cp1 = Msg->Header + Msg->Header_Size;
		j = Msg->Body_Offset - Msg->Header_Size;
		if (strncasecmp("Sender:",cp1,7)) {
			while(--j >= 0) {
				if (*cp1++ != '\n') continue;
				if (!strncasecmp("Sender:",cp1,7)) break;
			}
		}
	}
	if (j < 0) {
		Msg->From_Offset = 0;
		Msg->From_Size = 0;
	} else {
		Msg->From_Offset = cp1 - Msg->Header;
		Msg->From_Size = 0;
		while((*cp1++ != '\n') && (--j >= 0)) Msg->From_Size++;
		cp1 = Msg->Header + Msg->From_Offset;
		j = ((*cp1 == 'F') || (*cp1 == 'f')) ? 6 : 8;
		while((cp1[j] == ' ') || (cp1[j] == '\t')) j++;
		Msg->From_Offset += j;
		Msg->From_Size -= j;
	}

	/*
	 *	Return the size
	 */
	return(cp - String);

	/*
	 *	Header error, return -1
	 */
Header_Error:
	printf("%s in message number %d\n", error_message, msg_num);
	return(-1);
}
