/*
 *	Copyright (C) 1984, 1985  SRI International
 *	Copyright (C) 1988, 1992  TGV, Incorporated
 *
 *	Convert host mail file to MM message format
 *
 */
#include "vax-mm.h"
#include <stat.h>
#include "date.h"
#include <stdio.h>

#ifndef UNIX

/*
 *	Format of VMS "ISAM" mail file
 */
struct v4_mail_record_header {
	unsigned long	datim[2];		/* Time msg recv'd */
	unsigned char	folder_name_size;	/* size of folder name */
	unsigned char	folder_name[39];	/* Name of folder */
	};
struct v4_mail_record {
	struct v4_mail_record_header header;	/* header */
	unsigned short		flags;		/* Flags  */
#define NEW_MESSAGE		(1<<0)			/* Message is new */
#define REPLIED			(1<<1)			/* Reply done */
#define MSG_IN_EXTERNAL_FILE	(1<<3)			/* In External file */
#define MSG_IN_SYSTEM_FILE	(1<<4)			/* In System file */
	unsigned char		flagstring_size;/* Flagstring size */
	unsigned char		flagval[4];	/* Flagval ?? */
	unsigned char		dummy;		/* Alignment char */
	unsigned long		data_id[2];	/* ISAM key for msg data */
	char			data[1];	/* Variable length data */
/* Field types */
#define FROM	0
#define TO	1
#define SUBJECT 2
#define CC	3
#define MW	4
#define NREC	5
#define EXTMSG	6
};
struct v4_mail {
	long int flink_offset;
	long int size;
	struct v4_mail_record mail_record;
};

/*
 *	Convert mail to "MM" format (VMS version)
 */
char *Convert_Mail(Filename,Size_Ptr)
char *Filename;
int *Size_Ptr;
{
	register struct v4_mail_record *p;
	register char *cp;
	register int i;
	char *cp1;
	int j;
	int fd;
	int space_used,space_left;
	int last_message_offset = -1;
	char From[128],To[128],Cc[128],Subject[256],Date[64],Mail_Time[64];
	char *New_Text = 0;
	int New_Text_Size = 0;
	struct {int size; char *ptr;} descr;
	int Time[2];
	char Header_Buffer[1024];
	char data[20000];
	char *Allocated_Data = 0;

	/*
	 *	Open the Mail file
	 */
	if ((fd = open(Filename,0)) < 0) return(0);
	/*
	 *	Read in the V4 mail file records and save a list of them
	 */
	cp = data;
	space_left = sizeof(data);
	space_used = 0;
	while(1) {
		char buf[2500];
		int j;

		i = read(fd,buf,sizeof(buf));
		if (i == 0) break;
		if (i < 0) {
			perror("read:");
			exit(0);
		}
		j = (i + (sizeof(long int)-1)) & ~(sizeof(long int)-1);
		if ((j + sizeof(long int)) > space_left) {
			if (cp == data) {
				space_left += 10*1024;
				cp = (char *)mm_malloc(sizeof(data)+(10*1024));
				if (cp == 0) {
					printf("Out of memory\n");
					return(0);
				}
				bcopy(data,cp,space_used);
			} else {
				space_left += 10*1024;
				cp = (char *)mm_realloc(cp,
						     space_left+space_used);
				if (cp == 0) {
					printf("Out of memory\n");
					return(0);
				}
			}
			Allocated_Data = cp;	/* Remember the allocation */
		}
		if (last_message_offset == -1) {
			last_message_offset = 0;
		} else {
			((struct v4_mail *)(cp + last_message_offset))->
					flink_offset = space_used;
		}
		((struct v4_mail *)(cp + space_used))->flink_offset = 0;
		((struct v4_mail *)(cp + space_used))->size = i;
		bcopy(buf,cp + space_used + (2*sizeof(long int)), i);
		last_message_offset = space_used;
		space_used += i + (2*sizeof(long int));
		space_left -= i + (2*sizeof(long int));
	}
	close(fd);
	/*
	 *	Now look at the mail records
	 */
	i = 0;
	space_left = 0;
	while(1) {
		struct v4_mail_record *p,*p1;
		int p1_size;
		int size;
		int tops_size;
		int real_size;
		int empty_message;
		char *cp1;
		char Filename[128];

		p = (struct v4_mail_record *)(cp + i + (2*sizeof(long int)));
		size = ((struct v4_mail *)(cp + i))->size;
		/* Ignore records without folder names */
		if (size <= 8) goto next;
		if (p->header.folder_name_size == 0)
				goto next;
		cp1 = p->data;
		size -= (int)((struct v4_mail_record *)0)->data;
		From[0] = 0; To[0] = 0; Cc[0] = 0; Subject[0] = 0;
		while(size > sizeof(short)) {
			int i,j;
			char *cp;
			switch(*((short int *)cp1)) {
				case NREC:
					cp1 += 3*sizeof(short);
					size -= 3*sizeof(short);
					break;
				case FROM:
					cp = From;
					i = sizeof(From)-1;
					goto Common;
				case TO:
					cp = To;
					i = sizeof(To)-1;
					goto Common;
				case CC:
					cp = Cc;
					i = sizeof(Cc)-1;
					goto Common;
				case SUBJECT:
					cp = Subject;
					i = sizeof(Subject)-1;
			Common:		cp1 += sizeof(short);
					size -= sizeof(short);
					j = *((unsigned short int *)cp1);
					cp1 += sizeof(unsigned short);
					size -= sizeof(unsigned short);
					strncpy(cp,cp1,j > i ? i : j);
					cp[j > i ? i : j] = 0;
					cp1 += j;
					size -= j;
					break;
				default:
					cp1 += sizeof(short);
					size -= sizeof(short);
					i = *((unsigned short int *)cp1);
					cp1 += sizeof(unsigned short);
					size -= sizeof(unsigned short);
					cp1 += i;
					size -= i;
					break;
			}
		}
		/*
		 *	Set the Date string
		 */
		descr.size = 20;
		descr.ptr  = Date;
		sys$asctim(&descr.size,&descr,p->header.datim,0);
		Date[descr.size] = 0;
		/*
		 *	Convert to TOPS-20 Date string
		 */
		idtim_jsys(Date,0,Time);
		odtim_jsys(Time,
			   ODT_DAY_OF_WEEK |
			   ODT_DATE_WITH_SPACES |
			   ODT_DO_TIMEZONE |
			   ODT_NO_COLUMNATION,
			   Date);
		/*
		 *	Fix up the "from" field
		 */
		cp1 = From + strlen(From);
		while(1) {
			cp1--;
			if ((*cp1 != ' ') && (*cp1 != '\t')) break;
			*cp1 = 0;
		}
		cp1 = From;
		while(*cp1 && (*cp1 != ' ') && (*cp1 != '\t')) cp1++;
		if ((*cp1 == ' ') || (*cp1 == '\t')) {
			char tmp[128];

			*cp1++ = 0;
			while((*cp1 == ' ') || (*cp1 == '\t')) *cp1++ = 0;
			if (*cp1 == '\"') *cp1++ = 0;
			while(*cp1 && (*cp1 != '\"')) cp1++;
			*cp1 = 0;
			cp1--;
			while(*cp1) cp1--;
			cp1++;
			sprintf(tmp,"%s <%s>",cp1,From);
			strcpy(From,tmp);
		}
		/*
		 *	Convert to MM style mail text:
		 */
		if (p->flags & MSG_IN_EXTERNAL_FILE) {
			struct stat s;

			/*
			 *	Open the external mail file and get its size
			 */
			sprintf(Filename,"SYS$LOGIN:MAIL$%08.8X%08.8X.MAI",
					p->data_id[1],p->data_id[0]);
			fd = open(Filename,0);
			if (fd < 0) {
			   sprintf(Filename,"SYS$LOGIN:MAIL$%08.8X%08.8X.MAI",
					p->data_id[0],p->data_id[1]);
			   fd = open(Filename,0);
			   if (fd < 0) {
				sprintf(Filename,"SYS$LOGIN:MAIL$%08.8X%08.8X.MAI",
					p->data_id[1],p->data_id[0]);
				printf("Error integrating new mail: Couldn't open EXTERNAL msg file %s\n",
					Filename);
				goto next;
			   }
			}
			if (fstat(fd,&s) < 0) {
				printf("Error integrating new mail: Couldn't fstat EXTERNAL msg file %s\n",
					Filename);
				goto next;
			}
			size = s.st_size;
			empty_message = 0;
		} else {
			int i = 0;
			int j;

			empty_message = 0;
			while(1) {
				p1 = (struct v4_mail_record *)(cp + i + (2*sizeof(long int)));
				if ((p1->header.datim[0] == p->data_id[0]) &&
				    (p1->header.datim[1] == p->data_id[1]))
								break;
				i = ((struct v4_mail *)(cp + i))->flink_offset;
				if (i == 0) {
					empty_message = 1;
					size = 0;
					break;
				}
			}
			if (!empty_message) {
				j = ((struct v4_mail *)(cp + i))->size;
				p1_size = j;
				j -= sizeof(struct v4_mail_record_header);
				cp1 = ((char *)p1) + sizeof(struct v4_mail_record_header);
				size = 0;
				while(j > sizeof(short)) {
					int i;
					i = *((short *)cp1);
					cp1 += sizeof(short);
					j -= sizeof(short);
					size += i + 1; /* Line + "\n" */
					cp1 += i;
					j -= i;
				}
			}
		}
		/*
		 *	Make sure we have enough room (allocate more)
		 */
		if (space_left < (size + 1024)) {
			space_left += (size + 1024 + 1023) & ~1023;
			if (New_Text == 0) {
				New_Text = (char *)mm_malloc(space_left);
			} else {
				New_Text = (char *)
					mm_realloc(New_Text,
						New_Text_Size + space_left);
			}
			if (New_Text == 0) {
				printf("?Out of memory\n");
				if (Allocated_Data) mm_free(Allocated_Data);
				return(0);
			}
		}
		/*
		 *	Format the text of the message at the top of
		 *	free space (we will copy it later)
		 */
		cp1 = New_Text + New_Text_Size + space_left - size;
		if (p->flags & MSG_IN_EXTERNAL_FILE) {
			/*
			 *	Read it in and get its REAL size
			 */
			real_size = read(fd,cp1,size);
			close(fd);
			if (unlink(Filename) < 0) {
				chmod(Filename,0700);
				if (unlink(Filename) < 0)
					printf("WARNING: %s could not be deleted, please report this!\n",Filename);
			}
		} else {
			int i = 0;
			int j;
			char *cp2;

			if (!empty_message) {
				j = p1_size - sizeof(struct v4_mail_record_header);
				cp2 = ((char *)p1) + sizeof(struct v4_mail_record_header);
				while(j > sizeof(short)) {
					int i;
					i = *((short *)cp2);
					cp2 += sizeof(short);
					j -= sizeof(short);
					bcopy(cp2,cp1,j);
					cp1 += i;
					*cp1++ = '\n';
					cp2 += i;
					j -= i;
				}
				real_size = size;
			} else {
				real_size = 0;
			}
		}
		/*
		 *	Calculate the TOPS-20 size of the message text
		 */
		cp1 = New_Text + New_Text_Size + space_left - size;
		tops_size = 0;
		j = real_size;
		while(--j >= 0) {
			if (*cp1++ == '\n') tops_size++; /* Counts as 2 */
			tops_size++;
		}
		/*
		 *	If this is not a [RESENT MM-Mail] item
		 *	or a MM:<Subject> item do the header
		 */
		if ((strcmp(Subject,"[RESENT MM-Mail]") != 0) &&
		    (strcmpn(Subject,"MM:<", 4) != 0)) {
			/*
			 *	Account for the header
			 */
			tops_size += 8 + strlen(Date);		/* Date:    */
			tops_size += 8 + strlen(From);		/* From:    */
			tops_size += 11 + strlen(Subject);	/* Subject: */
			tops_size += 6 + strlen(To);		/* To:	    */
			tops_size += 2;				/* Separator*/
			/*
			 *	Format the header
			 */
			odtim_jsys(Time,ODT_DO_TIMEZONE,Mail_Time);
			cp1 = New_Text + New_Text_Size;
			sprintf(cp1,
				"%s,%d;000000000000\nDate: %s\nFrom: %s\nSubject: %s\nTo: %s\n\n",
				Mail_Time,tops_size,Date,From,Subject,To);
		} else {
			/*
			 *	Format the header
			 */
			odtim_jsys(Time,ODT_DO_TIMEZONE,Mail_Time);
			cp1 = New_Text + New_Text_Size;
			sprintf(cp1,
				"%s,%d;000000000000\n",
				Mail_Time,tops_size);
		}
		/*
		 *	Append the message text
		 */
		j = strlen(cp1);
		bcopy(New_Text + New_Text_Size + space_left - size,cp1+j,real_size);
		j += real_size;
		New_Text_Size += j;
		space_left -= j;
		/*
		 *	Next record
		 */
next:		i = ((struct v4_mail *)(cp + i))->flink_offset;
		if (i == 0) break;
	}
	/*
	 *	Free any allocated data
	 */
	if (Allocated_Data) mm_free(Allocated_Data);
	/*
	 *	Return the new mail text
	 */
	*Size_Ptr = New_Text_Size;
	return(New_Text);
}
