/*
 *	Copyright (C) 1984, 1985  SRI International
 *	Copyright (C) 1988, 1989, 1990, 1992	TGV, Incorporated
 *
 *	Check for new mail and integrate it into the mail file
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <file.h>
#include <ctype.h>
#include <prvdef.h>
#include <jpidef.h>
#include <stat.h>
#include "vax-mm.h"

static int Lock_ID;
static int Super_Lock_Count = 0;
static int Local_Ignore_Control_C_Flag = 0;

static unsigned long int Next_Check;
static Read_In_Additional_Mail();
static Read_In_New_Mail();
static int Update_New_Headers();

extern char *Topsify_Filename();
/*
 *	Initialize time next check to be done (set to zero so check is forced)
 */
Init_Mail_Check_Time(n) {Next_Check = n;}

/*
 *	Check for new mail
 */
Check_For_New_Mail(Print)
int Print;
{
	int New_Mail_Read_In;
	struct stat s, newmail_s;
	char *cp;
	char Local1[256], Local2[256];

	/*
	 *	Only check every "MAIL_CHECK_INTERVAL" seconds
	 */
	if ((unsigned long int)time(0) < Next_Check) return;

	/*
	 *	Don't check for mail if there is no mail file!!
	 */
	if (Current_Mail_File[0] == 0) return;

	/*
	 *	Don't allow Control-C right now
	 */
	Ignore_Control_C_Flag = 1;

	/*
	 *	Deal with interlocks on the user's mail file
	 */
	Super_Lock();

	/*
	 *	Check for new mail arrival (i.e. appended to the current
	 *	mail file)
	 */
	New_Mail_Read_In = 0;
	if (stat(Current_Mail_File,&s) >= 0) {
		if (s.st_size > Last_Message_File_EOF) {
			New_Mail_Read_In = 1;
			Read_In_Additional_Mail(&s, Print);
		}
	}
	if (File_Delivery_Done) {
		File_Delivery_Done = 0;
		fstat(Text_File_FD,&s);
		if (s.st_size > Last_Message_File_EOF) {
			New_Mail_Read_In = 1;
			Read_In_Additional_Mail(&s, Print);
		}
	}

	/*
	 *	See if the new mail file exists if we are on the home mail file
	 */
	if (!Read_Only) {
		strcpy(Local1, Topsify_Filename(Current_Mail_File));
		strcpy(Local2, Topsify_Filename(Home_File(MM_MAIL_FILE)));
			if ((strcmp(Local1, Local2) == 0) && New_Mail_File[0]) {
				if (stat(Home_File(New_Mail_File),
					       &newmail_s) >= 0) {
					if (newmail_s.st_size > 0) {
						New_Mail_Read_In = 1;
						Read_In_New_Mail(New_Mail_File,
								 &newmail_s,
								 Print);
					}
				}
			}
	}

	Next_Check = (unsigned long int)time(0) + MAIL_CHECK_INTERVAL;
	if (Number_Of_Messages_To_Update != 0) Update_Header_File(0);

	/*
	 *	Release interlocks on user's mail file (so he can start
	 *	receiving mail again)
	 */
	Super_Unlock();

	/*
	 *	Print new mail headers and summary
	 */
	if ((New_Mail_Read_In == 1) && Print) {
		Recent(1);
		Summary();
		printf(" Currently at message %d\n",Current_Message);
	}

	Ignore_Control_C_Flag = 0;
	return;
}


/*
 *	Lock the "MM_MAIL_FILE" file
 */
int Lock_MM_Mail_File()
{
	char Local1[256];
	char *cp;
	int Retry = 5;

	/*
	 *	Disable Control-C for the time being; if already disabled,
	 *	remember
	 */
	if (!Ignore_Control_C_Flag) {
		Local_Ignore_Control_C_Flag = 1;
		Ignore_Control_C_Flag = 1;
	} else
		Local_Ignore_Control_C_Flag = 0;
	
	/*
	 *	Get the names of the files
	 */
	New_Extension(Current_Mail_File,Local1,"mm1");

	/*
	 *	Repeat as necessary
	 */
	while(--Retry >= 0) {
		/*
		 *	Try to move the "MM_MAIL_FILE"
		 */
		if (link(Current_Mail_File,Local1) >= 0) {
			if (unlink(Current_Mail_File) < 0) {
				if (Local_Ignore_Control_C_Flag)
					Ignore_Control_C_Flag = 0;
				return(-1);
			}
			/*
			 *	Success
			 */
			return(0);
		}
		/*
		 *	See if someone else has it locked!!
		 */
		if (Xaccess(Local1,0) >= 0) {
			if (Local_Ignore_Control_C_Flag)
				Ignore_Control_C_Flag = 0;
			return(-1);
		}
		/*
		 *	No: try for the "MM_MAIL_FILE" once more
		 */
		if (Xaccess(Current_Mail_File,0) < 0) {
			/*
			 *	No: Create a new "MM_MAIL_FILE" file
			 */
			close(Xcreat(Current_Mail_File,0600,0));
		}
	}
}


/*
 *	Unlock the "MM_MAIL_FILE" file
 */
UnLock_MM_Mail_File()
{
	char Local[256];
	char *cp;

	/*
	 *	Just move it back!
	 */
	New_Extension(Current_Mail_File,Local,"mm1");
	link(Local,Current_Mail_File);
	unlink(Local);

	/*
	 *	Re-enable Control-C
	 */
	if (Local_Ignore_Control_C_Flag)
		Ignore_Control_C_Flag = 0;
}


/*
 *	Read in additional mail that has been appended to the mail file
 */
static Read_In_Additional_Mail(s, Print)
struct stat *s;
int Print;
{
	register struct Msg *Msg;
	struct Msg *Msg1;
	register char *cp;
	register char *cp1;	/* for debugging */
	register int i,j;
	int Msg_index, Msg_size;
	int fd;
	int Number_Of_New_Messages = 0;
	char Local[128];
	FILE *debugfile = NULL;	/* for debugging */

	/*
	 *	   Make sure there is enough room for the
	 *	   the additional text.
	 */
	if (Message_Text_Size < s->st_size) {
		cp = (char *)mm_realloc(Message_Text,s->st_size);
		if (cp == 0) {
			printf("?Insufficient memory for new mail.\n");
			return;
		}
		Message_Text = cp;
		Message_Text_Size = s->st_size;
	}
	/*
	 *	Read in the new mail
	 */
	if (Text_File_FD >= 0) close(Text_File_FD);
#ifndef vax11c
	Text_File_FD = open(Current_Mail_File,0);
#else	vax11c
	Text_File_FD = Vax11_C_Shared_Open(Current_Mail_File,0);
#endif	vax11c
	if (Text_File_FD < 0) {
		printf("?Failed to re-open \"%s\" getting new mail.\n",
				Current_Mail_File);
		exit_MM(-1);
	}
	lseek(Text_File_FD,Message_File_Size,0);
	cp = Message_Text + Message_File_Size;
	cp1 = Message_Text + Message_File_Size;
	i = read(Text_File_FD, cp, Message_Text_Size - Message_File_Size);
	if (i <= Message_Text_Size - Message_File_Size)
		Message_File_Size += i;
	/*
	 *	Update the headers
	 */
	Msg = Msg1 = &Messages[Last_Message];
        Msg_index = Last_Message;
	Number_Of_New_Messages = 0;
	while(1) {
		/*
		 *	When chars are exhausted, break
		 */
		if ((i <= 0) || (*cp == 0))break;
		/*
		 *	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;
			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);
			}
			Msg = Msg1 = &Messages[Last_Message];
		}
		/*
		 *	Parse the message header
		 */
		j = Parse_Message_Header(Msg,cp,i);
		if (j < 0) {
			printf("?Header error\n");
			return;
		}
		i -= j;
		cp += j;
		/*
		 *	Increment the message count
		 */
		Last_Message++;
		Msg++;
		Number_Of_New_Messages++;
	}

	/*
	 *	Update the Header file
	 */
	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;
	Last_Message_File_EOF = s->st_size;
	Update_Header_File(1);

	/*
	 *	Print the new message INFO
	 */
	if (Print)
		printf(Number_Of_New_Messages == 1 ?
		       " There is %d additional message.\n" :
		       " There are %d additional messages.\n",
		       Number_Of_New_Messages);

	return;
}


/*
 *	Read in mail from newmail file, then delete it
 */
static Read_In_New_Mail(newmail, s, Print)
char *newmail;
struct stat *s;
int Print;
{
	register char *cp;
	register int i, j;
	int Number_Of_New_Messages;
	int New_Mail_FD;
	char Local[128], New_Mail_File[128], New_Mail_Header_File[128];
	register struct Msg *Msg;
    	static unsigned int exquota[2] = {PRV$M_EXQUOTA,0};
    	unsigned int prvprv[2];

/*
**  If we don't have EXQUOTA privilege available, then we check to make
**  sure the user has sufficient diskquota to perform the expunge.
**  This test will only work if the user is actually going to wind
**  up owning the file that gets created (which might not happen
**  if the user has SYSPRV or something).
*/
    	{
    	    unsigned int curpriv[2], code=JPI$_CURPRIV;
    	    enable_image_privilege();
    	    lib$getjpi(&code, 0, 0, curpriv);
    	    disable_image_privilege();
    	    if (!(curpriv[0] & PRV$M_EXQUOTA)) {
    	    	i = Diskquota_Remaining(Current_Mail_File);
    	    	if ((i >= 0) && (i < (s->st_size+511)/512)) {
    	    	    printf("Insufficient diskquota to perform expunge operation.\n");
    	    	    goto done;
    	    	}
    	    }
    	}

	/*
	 *	Potentially realloc the space that we need
	 */
	if (Message_Text_Size < Message_File_Size + s->st_size) {
		cp = (char *)mm_realloc(Message_Text,
					Message_File_Size+s->st_size);
		if (cp == 0) {
			printf("?Insufficient memory to integrate new mail.\n");
			goto done;
		}
		Message_Text = cp;
	}
	cp = Message_Text + Message_File_Size;

	/*
	 *	Open the new mail file
	 */
	strcpy(New_Mail_File,Home_File(newmail));
	sprintf(Local, "$hdrs$%s", newmail);
	strcpy(New_Mail_Header_File,Home_File(Local));
	New_Mail_FD = Xopen(New_Mail_File,0);
	if (New_Mail_FD < 0) {
		printf("?Failed to open \"%s\" getting new mail.\n",
				New_Mail_File);
		goto done;
	}

	/*
	 *	Read the new mail in, then close newmail file
	 */
	i = read(New_Mail_FD, cp, s->st_size);
	if (i != s->st_size) {
		printf("?Didn't read in the amount of new mail indicated by file size\n");
		printf("?Amount read = %n, file size = %n\n", i, s->st_size);
		exit_MM(-1);
	}
	close(New_Mail_FD);

	/*
	 *	Update the in memory headers, if errors, die
	 */
	if (Update_New_Headers(s->st_size, Print) < 0)
		exit_MM(-1);

	/*
	 *	Everything is happy (so far)!  Append to the Message Text file
	 */
	if (Text_File_FD >= 0) close(Text_File_FD);
	Text_File_FD = Xopen(Current_Mail_File, 2);
	if (Text_File_FD < 0 ) {
		printf("?Failed to open \"%s\" to append new mail.\n",
				Current_Mail_File);
		exit_MM(-1);
	}
	lseek(Text_File_FD, 0, 2);
    	sys$setprv(1, exquota, 0, prvprv);
	if (write(Text_File_FD, cp, i) != i ) {
		printf("?Error appending to \"%s\"\n",
				Current_Mail_File);
    	    	if (!(prvprv[0] & PRV$M_EXQUOTA)) sys$setprv(0, exquota, 0, 0);
		exit_MM(-1);
	}

	/*
	 *	Close text file for exclusive access, and reopen for read-only
	 */
	close(Text_File_FD);
    	if (!(prvprv[0] & PRV$M_EXQUOTA)) sys$setprv(0, exquota, 0, 0);
	Text_File_FD = Vax11_C_Shared_Open(Current_Mail_File,0);
	if (Text_File_FD < 0) {
		printf("?Failed to re-open \"%s\" getting new mail.\n",
				Current_Mail_File);
		exit_MM(-1);
	}

	/*
	 *	Close and delete newmail files
	 */
	delete(New_Mail_File);
	delete(New_Mail_Header_File);

	/*
	 *	Get the NEW eof position
	 */
	stat(Current_Mail_File,s);
	Last_Message_File_EOF = Message_File_Size = s->st_size;

done:
	return;
}

int Append_In_Core(cp, Size, Print)
char *cp;
int Size;
int Print;
{
	char *cp1;
	int i, j;

	/*
	 *	Append to the in-core mail
	 */
	if (Message_Text_Size < Message_File_Size + Size) {
		cp1 = (char *)mm_realloc(Message_Text,Message_File_Size+Size);
		if (cp1 == 0) {
			printf("Insufficient memory to integrate new mail.\n");
			return -1;
		}
		Message_Text = cp1;
	}
	cp1 = Message_Text + Message_File_Size;
	for(j = 0; j < Size; j++) cp1[j] = cp[j];
	mm_free(cp);

	Update_New_Headers(Size, Print);
}

int Update_New_Headers(Size, Print)
int Size;
int Print;
{
	char *cp;
	int i, j;
	int Number_Of_New_Messages;
	register struct Msg *Msg;

	/*
	 *	Update the headers
	 */
	i = Size;
	Msg = &Messages[Last_Message];
	cp = Message_Text + Message_File_Size;
	Number_Of_New_Messages = 0;
	while(1) {
		/*
		 *	When chars are exhausted, break
		 */
		if ((i <= 0) || (*cp == 0))break;
		/*
		 *	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;
			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);
			Msg = &Messages[Last_Message];
		}
		/*
		 *	Parse the message header
		 */
		j = Parse_Message_Header(Msg,cp,i);
		if (j < 0) {
			printf("?Header error\n");
			return(-1);
		}
		i -= j;
		cp += j;
		/*
		 *	Increment the message count
		 */
		Message_File_Size += Msg->Real_Size;
		Last_Message++;
		Msg++;
		Number_Of_New_Messages++;
		/*
		 *	Ensure that the header will be updated
		 */
		Update_Message(Last_Message);
	}
	/*
	 *	Update the Header file
	 */
	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;
	Update_Header_File(1);

	/*
	 *	Print the new message INFO
	 */
	if (Print)
		printf(Number_Of_New_Messages == 1 ?
		       " There is %d additional message.\n" :
		       " There are %d additional messages.\n",
		       Number_Of_New_Messages);

	return(0);
}

/*
 *	Routines for taking/releasing a "Super" lock on the user's mail file
 */
int Super_Lock()
{
#ifdef	DEBUG
	printf("Entering Super_Lock, Super_Lock_Count = %d\n",
	       Super_Lock_Count);
#endif
	if (Super_Lock_Count++ == 0) {
		enable_image_privilege();
		Lock_User_Mailbox(Real_User_Name,&Lock_ID);
		disable_image_privilege();
	}
}

int Super_Unlock()
{
	if (--Super_Lock_Count == 0) {
		enable_image_privilege();
		Remove_Lock(Lock_ID);
		disable_image_privilege();
	}
#ifdef	DEBUG
	printf("Exiting Super_Unlock, Super_Lock_Count = %d\n",
	       Super_Lock_Count);
#endif
	if (Super_Lock_Count < 0) {
		printf("Super_Unlock() called too many times!\n");
		Super_Lock_Count = 0;
	}
}
