/*
 *	Copyright (C) 1984, 1985  SRI International
 *	Copyright (C) 1990, 1992  TGV, Incorporated
 *
 *	Expunge deleted messages
 *
 */
#include "vax-mm.h"
#include <stat.h>
#include <prvdef.h>
#include <jpidef.h>

extern char *Topsify_Filename();

CMD_Expunge()
{

	/*
	 *	Noise
	 */
	Noise("deleted messages");
	/*
	 *	Confirm
	 */
	Confirm();
	/*
	 *	Expunge
	 */
	Expunge();
}


/*
 *	Expunge code
 */
Expunge()
{
	int fd;
	register char *cp,*cp1;
	register int i,j;
	register struct Msg *Msg,*Msg1;
	int Squished = 0;
	int Some_Messages_Left = 0;
	int Real_Size, Post_Expunge_Size;
	char Local1[256], Local2[256];
    	static unsigned int exquota[2] = {PRV$M_EXQUOTA,0};
    	unsigned int prvprv[2];
	struct stat s;

	/*
	 *	If read only, do nothing
	 */
	if (Read_Only) {
		printf(" In read-only mode; mail file not expunged.\n");
		return;
	}

	/*
	 *	Keep the user's mail file locked around all this code
	 */
	Super_Lock();

	/*
	 *	Check for new mail now:  To avoid deleting a mail
	 *				 file to which mail has just arrived!
	 */
	Init_Mail_Check_Time(0);  Check_For_New_Mail(1);

	/*
	 *	See if we have anything to expunge or if EVERYTHING is deleted
    	 *  	Also figure out how big the new message file will be
	 */
    	Post_Expunge_Size = Message_File_Size + Last_Message*sizeof(struct Msg) + sizeof(Header_File_Header);
	for(i = 0; i < Last_Message; i++) {
		if (Messages[i].Flags & MSG_DELETED) {
    	    	    	Post_Expunge_Size -= Messages[i].Real_Size + sizeof(struct Msg);
			Squished = 1;
		} else {
			Some_Messages_Left = 1;
		}
	}
	if (!Squished) {
		printf(" No messages deleted, so no update needed.\n");
		Super_Unlock();
		return;
	}
	if (!Some_Messages_Left) {
		strcpy(Local1, Topsify_Filename(Current_Mail_File));
		strcpy(Local2, Topsify_Filename(Home_File(MM_MAIL_FILE)));
		if (strcmp(Local1, Local2) != 0) {
			printf(" All messages deleted, deleting file\n");
			unlink(Current_Mail_File);
			Header_File_Name(Current_Mail_File,Local1);
			unlink(Local1);
			Zero_Sizes();
			Super_Unlock();
			return;
		}
	}

	/*
	 *	Make sure everything is in memory
	 */
	for(i = 0; i < Last_Message; i++) cp = MESSAGE_HEADER(i+1);

	/*
	 *	Lock the message file
	 */
	if (Lock_MM_Mail_File() < 0) {
		printf("Couldn't lock mail file in order to expunge deleted messages\n");
		Super_Unlock();
		return;
	}

/*
**  Check to see if there is sufficient space on the disk itself.
*/
    	if (Free_Disk_Space(Current_Mail_File) < (Post_Expunge_Size+511)/512) {
    	    printf("Insufficient disk space to perform expunge operation.\n");
    	    UnLock_MM_Mail_File();
    	    Super_Unlock();
    	    return;
    	}

/*
**  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 < (Post_Expunge_Size+511)/512)) {
    	    	    printf("Insufficient diskquota to perform expunge operation.\n");
    	    	    UnLock_MM_Mail_File();
    	    	    Super_Unlock();
    	    	    return;
    	    	}
    	    }
    	}

	/*
	 *	Squish everything
	 */
	Msg = Messages;
	Msg1 = Messages;
	cp = Message_Text;
	cp1 = Message_Text;
	i = Last_Message;
	while(--i >= 0) {
		/*
		 *	Clear the update needed flag
		 */
		Msg->Flags &= ~MSG_NEEDS_UPDATING;
		/*
		 *	If deleted ignore
		 */
		if (Msg->Flags & MSG_DELETED) {
			/*
			 *	Decrement the file size and # of messages
			 */
			Message_File_Size -= Msg->Real_Size;
			Last_Message--;
			/*
			 *	Skip to the next message
			 */
			cp += Msg->Real_Size;
			Msg++;
			continue;
		}
		/*
		 *	Squish
		 */
		if (Msg == Msg1) {
			/*
			 *	Don't have to do anything - skip over text
			 */
			cp += Msg->Real_Size;
			cp1 += Msg->Real_Size;
		} else {
			/*
			 *	Copy the Msg structure
			 */
			*Msg1 = *Msg;
			/*
			 *	Change the header pointer in the msg
			 */
			Msg1->Header = cp1;
			/*
			 *	Copy the entire message text (with header)
			 */
			j = Msg->Real_Size;
			while(--j >= 0) *cp1++ = *cp++;
		}
		/*
		 *	Bump the message pointers
		 */
		Msg++;
		Msg1++;
	}
	/*
	 *	Update the flags/keywords number in the header
	 */
	for(i = 0; i < Last_Message; i++) {
		static char Digit[8] =	/* In bit reverse order */
			{'0','4','2','6','1','5','3','7'};
		/*
		 *	Point to the flags/keyword number
		 */
		Msg = &Messages[i];
		cp = MESSAGE_HEADER(i+1) + Messages[i].Header_Size - 13;
		/*
		 *	Convert to character string
		 */
		for(j = 0; j < 10; j++)
			*cp++ = Digit[(Msg->Keyword_Flags >> (3*j)) & 07];
		/*
		 *	Do the regular flag bits
		 */
		*cp++ = ((Msg->Flags >> 3) & 07) + '0';
		*cp   = (Msg->Flags & 07) + '0';
	}
	/*
	 *	Fix the current message pointer
	 */
	if (Current_Message > Last_Message) Current_Message = Last_Message;
	/*
	 *	Now the expunge has REALLY started
	 */
	printf(" Expunging deleted messages.\n");
	/*
	 *	Create the new mail.mm1 file
	 */
	umask(0);	/* Don't allow anyone to override the protection mode */
	New_Extension(Current_Mail_File,Local1,"mm1");
	New_Extension(Current_Mail_File,Local2,"mm2");
	link(Local1,Local2);
	unlink(Local1);
    	sys$setprv(1, exquota, 0, prvprv);
	fd = Xcreat(Local1,Text_File_Mode,0);
	if (fd < 0) {
    	    	if (!(prvprv[0] & PRV$M_EXQUOTA)) sys$setprv(0, exquota, 0, 0);
		link(Local2,Local1);
		unlink(Local2);
		printf("Couldn't re-create mail file for writing.\n");
		UnLock_MM_Mail_File();
		Super_Unlock();
		return;
	}
	if (write(fd,Message_Text,Message_File_Size) != Message_File_Size) {
		perror("I/O error writing new mail file");
		/*
		 *	Error: delete the partial mail file
		 */
		close(fd);
    	    	if (!(prvprv[0] & PRV$M_EXQUOTA)) sys$setprv(0, exquota, 0, 0);
		unlink(Local1);
		link(Local2,Local1);
		unlink(Local2);
		UnLock_MM_Mail_File();
		Super_Unlock();
		return;
	}
	/*
	 *	Now, get the new EOF, and re-open the text file in the
	 *	normal fashion
	 */
	close(fd);
    	if (!(prvprv[0] & PRV$M_EXQUOTA)) sys$setprv(0, exquota, 0, 0);
	if (Text_File_FD >= 0) close(Text_File_FD);
	stat(Local1,&s);
	Last_Message_File_EOF = s.st_size;
	Text_File_FD = Vax11_C_Shared_Open(Local1,0);
	/*
	 *	Kill the OLD mail.txt, now that the new one
    	 *  	has been written out.
	 */
	unlink(Local2);
	/*
	 *	Create the new 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;
	Header_File_Name(Current_Mail_File,Local1);
	unlink(Local1);
    	sys$setprv(1, exquota, 0, prvprv);
	Header_File_FD = Xcreat(Local1,Header_File_Mode,0);
	if (Header_File_FD >= 0) {
		write(Header_File_FD,&Header_File_Header,sizeof(Header_File_Header));
		write(Header_File_FD,Messages,Last_Message*sizeof(struct Msg));
		close(Header_File_FD);
		Header_File_FD = -1;
	}
    	if (!(prvprv[0] & PRV$M_EXQUOTA)) sys$setprv(0, exquota, 0, 0);
	/*
	 *	Unlock things
	 */
	UnLock_MM_Mail_File();

	/*
	 *	Possibly kill the mail file
	 */
	if (Message_File_Size == 0) {
		strcpy(Local1, Topsify_Filename(Current_Mail_File));
		strcpy(Local2, Topsify_Filename(Home_File(MM_MAIL_FILE)));
		if (strcmp(Local1, Local2) != 0) {
			printf(" All messages deleted, deleting file\n");
			unlink(Current_Mail_File);
			Header_File_Name(Current_Mail_File,Local1);
			unlink(Local1);
			Zero_Sizes();
		}
	}

	/*
	 *	Now release the "Super" lock
	 */
	Super_Unlock();
	return;
}

/*
 *	Message file's been deleted, so let's zero out the message pointers and
 *	lengths.
 */
Zero_Sizes()
{
	Text_File_FD = 0;
	Message_File_Modified = 0;
	Message_File_Size = 0;
	Last_Message_File_EOF = 0;
	Last_Message = 0;
	Max_Messages = 0;
	Current_Message = 0;
	Messages = 0;
	Current_Mail_File[0] = '\0';
}
