/*
     ATP QWK MAIL READER FOR READING AND REPLYING TO QWK MAIL PACKETS.
     Copyright (C) 1992  Thomas McWilliams 
     Copyright (C) 1990  Rene Cougnenc
  
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 1, or (at your option)
     any later version.
     
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
     
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
  
/*
qlib.c
*/

#include <stdio.h>
#include <time.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#ifdef unix
#include <utime.h>
#ifdef DJD
#include <dir.h>
#endif
#else
#ifdef WIN32
#include <malloc.h>
#include <direct.h> 
#else
#include <alloc.h>
#include <dir.h> 
#endif
#include <dos.h>
#include <io.h>
#endif
#include "system.h"
#include "ansi.h"
#include "qlib.h"
#include "reader.h"
#include "readlib.h"
#define  call   goto

static fcopy(const char *source, const char *destin) ;
static void Work2home( const char *DstDir, const char *SrcDir, const char *pilgrim ) ;
static struct MsgHeaderType Qmail;
static int    alias = FALSE ; /* if (User1Name != User2Name ) alias = TRUE */ 
/* Variables used to read Control.dat         */

int             LastConf = 0;	/* read from control.dat number of conf. field */
char            BoardName[76],
                UserName[50],
				User1Name[50],	/* user name taken from atprc       */ 
				User2Name[50];	/* user name taken from control.dat */

string16       *ConfName = NULL ;
int            *ConfNumbers = NULL ;
int            *ConfActive = NULL ;
/*
 * Reads the file CONTROL.DAT pointed by Pathname. This function is not finished
 * but works well enough.
 *
 */

int
ReadControl(const char *Path)
{
	FILE           *fp ;
	int             i, numconf;
	int             dd, mm, year, hh, mn, sec;
	char            tmp[MAXPATHS] ;

	sprintf(tmp, "%s%c%s", Path, SEP, CNTRL_FILE);

	if ((fp = fopen(tmp, "rb")) == NULL)
		call            bad_end;

	/*--- Get Board Name -----*/
	if (fget(BoardName, 74, fp) == NULL)
		call            bad_end;

    printf("\n %s\n\n", BoardName);
	for (i = 0; i < 5; i++)
		if (fget(tmp, 126, fp) == NULL)
			call            bad_end;

	/* Get time & date        */
	sscanf(tmp, "%d-%d-%d,%d:%d:%d", &mm, &dd, &year, &hh, &mn, &sec);

	/*--- Get User Name  -----*/
	if (fget(User2Name, 50, fp) == NULL)
		call            bad_end;

	if (stricmp(User2Name, User1Name)){ /* debug ?? if n.atprc != n.contrl > use n.contrl */
		alias = TRUE ;
		strcpy(UserName, User2Name);
	}else{
		alias = FALSE ;
		strcpy(UserName, User1Name);
	}

	/*--- Skip 3 lines...-----*/
	for (i = 0; i < 3; i++)
		if (fget(tmp, 126, fp) == NULL)
			call            bad_end;

	/*--- Get Last conf #-----*/
	if ((fget(tmp, 126, fp) == NULL))
		call            bad_end;

	LastConf = atoi(tmp);

	if ((LastConf < 0) || (LastConf > MAXCONF))
		call            bad_end;

/* Here is where we allocate memory for arrays with malloc() */

if( ConfActive != NULL ) free( ConfActive ) ;
if ((ConfActive = malloc( (sizeof(int) * (LastConf+3)) )) == NULL )
	     call           bad_end; 

if( ConfNumbers != NULL ) free( ConfNumbers );
if ((ConfNumbers = malloc( (sizeof(int) * (LastConf+3)) )) == NULL )
         call           bad_end;

if( ConfName != NULL )  free ( ConfName );
if ((ConfName = malloc ( (sizeof(string16) * (LastConf+3)) )) == NULL )
         call           bad_end;

	/*--- Get Conf Names -----*/
	for (i = 0; i <= LastConf; i++) {
		if (fget(tmp, 126, fp) == NULL)
			call            bad_end;
		numconf = atoi(tmp);
		if (numconf < 0)
			call            bad_end;
		if (fget(ConfName[i], 16, fp) == NULL)
			call            bad_end;
		StripLSpace(ConfName[i]);
		ConfNumbers[i] = numconf;
	}
	LastConf++;		/* add entry for personal mail */
	LastConf++;     /* add entry for reply mail    */
	ConfNumbers[ PCONF_IDX ] = PERS_CONF;
	ConfNumbers[ RCONF_IDX ] = REPL_CONF;
	strcpy(ConfName[ PCONF_IDX ], txt[103]);	/* "personal" */
    strcpy(ConfName[ RCONF_IDX ], txt[104]);    /* "replies " */
	
	/*--- Get Welcome, News, Goodbye Names -----*/
	strcpy( Welcome, "welcome");
	strcpy( News, "news");
	strcpy( GoodBye, "script0");
	if (fget(tmp, 15, fp) == NULL) goto skippy ;
	tmp[15] = 0 ; strlwr(tmp); strcpy( Welcome, tmp );
	if (fget(tmp, 15, fp) == NULL) goto skippy ;
	tmp[15] = 0 ; strlwr(tmp); strcpy( News, tmp );
	if (fget(tmp, 15, fp) == NULL) goto skippy ;
	tmp[15] = 0 ; strlwr(tmp); strcpy( GoodBye, tmp );

skippy:	
	fclose(fp);
	return (OK);
bad_end:
	fclose(fp);
	return (ERROR);
}

/* find actual index into array so as to locate conference name */

int
findCindex(const int n)
{
	int             i;
	for (i = 0; i <= LastConf; i++) {
		if (ConfNumbers[i] == n)
			return (i);
	}
	return (-1);		/* return -1 if conference doesn't exist */
}


/*
 * Scans the file MESSAGE.DAT, and calls WriteIndex for writing index files.
 */

int
MkIndex(const char *SrcDir, const char *DestDir)
{
	FILE           *fs = NULL, *fd = NULL, *fx = NULL, *pfd = NULL, *pfx = NULL;
	unsigned char  *ptr;
    volatile int    conf = -1;
	int             iconf = 0, persmail = FALSE, persave = FALSE;
	long int        count = 0, pcount = 0 ;
	long int        oldcount = 0;
	unsigned        Head = sizeof(struct MsgHeaderType), NbBlocs;
	unsigned long   MaxChars, i ;
	char            Src[MAXPATHS];	/* Source file Message.DAT */
	char            Dst[MAXPATHS];	/* Conference data  file   */
	char            Idx[MAXPATHS];	/* Conference index file   */
	char            Pdst[MAXPATHS];	/* Personal conference file */
	char            Pidx[MAXPATHS];	/* Personal index file     */
	char			**av ;
	long            last, plast ;
	int             ffllgg = FALSE;
    unsigned    	NamLen, NamLen1 = 0  ;
	SIZE_T			bct, j ;

	if (ReadControl(SrcDir) != OK ) {	/* Load new CONTROL.DAT file. */
		printf("%s !\n", txt[14]);	/* "error in control.dat" */
		return (ERROR);
	}
	NamLen = strlen(UserName);
	if (alias) NamLen1 = strlen(User1Name);
	
	/* Update Control.dat in bbs dir */
	Work2home( DestDir, SrcDir, CNTRL_FILE );

	/* Update Newfiles.dat in bbs dir */
	Work2home( DestDir, SrcDir, NEWFILES );
	
	/* Update Welcome file in bbs dir */
	Work2home( DestDir, SrcDir, Welcome );

	/* Update GoodBye file in bbs dir */
	Work2home( DestDir, SrcDir, GoodBye );

	/* Update News file in bbs dir */
	Work2home( DestDir, SrcDir, News );
	
	/* Update GoodBye file in bbs dir */
	Work2home( DestDir, SrcDir, DOORID );

#ifndef SIMP	
	/* Move bulletins to bbs dir */
#ifdef USEDIRENT	
	bct = FindMatches( (char *) SrcDir, "blt-" , &av ) ;
#else  /* msdog and windoze */ 
	sprintf(Src, "%s%c%s", SrcDir, SEP, "blt-" );
	bct = FindMatches( (char *) Src, &av ) ;
#endif
	for( j = 0 ; j < bct ; j++ ){
		Work2home( DestDir, SrcDir, *av ) ;
		free( *av ) ; av++ ;
	}
#endif	

	yellow();
	printf("%s...\n", txt[71]);	/* "adding msgs and creating indexes" */
	cyan();
	sprintf(Src, "%s%c%s", SrcDir, SEP, MSG_FILE);	/* Open MESSAGES.DAT */

	printf("Open %s\n", Src);
	if ((fs = fopen(Src, "rb")) == NULL)
	{
		printf("%s %s...\n", txt[51], Src);	/* "unable to open file" */
		return (ERROR);
	}

	/* Skip First Header      */
	if ( fread((char *) &Qmail.Status, Head, 1, fs) != (size_t) 1 ) {
		printf("%s %s\n", txt[58], Src);	/* "error reading file" */
		fclose(fs);
		return (ERROR);
	}
/*
      This is where we read messages.dat the main message file from
	  the BBS. The file is parsed into new seperate .cnf files for each
	  conference, each with its own index. While scanning the main file
	  we also look for a match between UserName (global variable) and
	  Qmail.ForWhom field in each message header. When a match is found
	  the message is also saved to a personal conference file with its
	  own index, and a flag "persmail" is set which shows that personal
	  mail has been received. Note that the index files sent in the QWK
	  packet are ignored and never used by this program.
*/

	/* Open personal conference and index files  */
	sprintf(Pdst, "%s%c%d.cnf", DestDir, SEP, PERS_CONF);	/*--< Open Conference file >--*/

	if (OpenCon(&pfd, &fs, Pdst) == ERROR)
		return (ERROR);

	fseek(pfd, 0L, SEEK_END);	/* Go to end of personal idx file... */
	plast = ftell(pfd);	/* Take size of existing messages */
	sprintf(Pidx, "%s%c%d.idx", DestDir, SEP, PERS_CONF);	/*--< Open Conference Index >--*/

	if (OpenCon(&pfx, &fs, Pidx) == ERROR)
		return (ERROR);
	fseek(pfx, 0L, SEEK_END);
	pcount =  (ftell(pfx) / sizeof(struct MyIndex));

	while (1) {
		if ( fread(&Qmail.Status, Head, 1, fs) != (size_t) 1 )
			break;	/* End of file         */
		if ( Qmail.Status == 0 )
			break;	/* End of file  J mail ?  */

  		memcpy((char *) rbuf, (char *) &Qmail.Status, (unsigned) Head);
		if ( ( readCnum((byte *) &(Qmail.BinConfN)) ) != conf) {	/* Another conference ? */
			if (ffllgg) {	/* after first pass, close file pointers */
				fclose(fx);
				fclose(fd);
			}
			ffllgg = TRUE;	/* first pass flag set true */
			if (conf >= 0) {  /* close up old before starting new */
				printf("Conference %d \t[ %-15s ]%c  %3ld New %s %s\n",
				       ConfNumbers[iconf], ConfName[iconf],
				       persmail ? '*' : ' ', count - oldcount,
				       (count - oldcount) > 1 ? "Messages" : "Message",
				       persmail ? txt[103] : " ");
			}
			persmail = FALSE;
			count = 0;
			oldcount = 0;
			conf = readCnum((byte *) & (Qmail.BinConfN));
			
			/* here is where we do bounds checking */
			for (iconf = 0; iconf <= LastConf ; iconf++)	
				if ( conf == ConfNumbers[iconf])
					break ; 
			if ( iconf > LastConf) {
				Qmail.BinConfN[1] = (byte) 0 ; /* this unmungs the number */
  				memcpy((char *) rbuf , (char *) &(Qmail.Status), (unsigned) Head);
				conf = readCnum((byte *) & (Qmail.BinConfN));
				for (iconf = 0; iconf <= LastConf ; iconf++)	
					if ( conf == ConfNumbers[iconf])
						break ; 
				if (iconf > LastConf){ /* last resort force valid conf num. */
					iconf = 0 ; 
					conf = ConfNumbers[0] ;
					Qmail.BinConfN[0]  = (byte) (conf & 0x00ff);
					Qmail.BinConfN[1]  = (byte) ((conf & 0xff00) >> 8);
  					memcpy((char *) rbuf , (char *) &(Qmail.Status), (unsigned) Head);
				}
			}
			/* Open new files  */
			sprintf(Dst, "%s%c%d.cnf", DestDir, SEP, conf);	/*--< Open Conference file >--*/
			if (OpenCon(&fd, &fs, Dst) == ERROR) {
				fclose(pfx);
				fclose(pfd);
				return (ERROR);
			}
			fseek(fd, 0L, SEEK_END);	/* Go to end of file... */
			last = ftell(fd);	/* Take size of existing messages */

			sprintf(Idx, "%s%c%d.idx", DestDir, SEP, conf);	/*--< Open Conference Index >--*/
			if (OpenCon(&fx, &fs, Idx) == ERROR) {
				fclose(pfx);
				fclose(pfd);
				return (ERROR);
			}
			fseek(fx, 0L, SEEK_END);
			count = (ftell(fx) / sizeof(struct MyIndex));
			oldcount = count;
			printf("%d : %ld\r", iconf, count - oldcount);	
			fflush(stdout);
		}
		persave = FALSE;
		if (!strnicmp(UserName, (char *) Qmail.ForWhom, NamLen)) {
			pmail = TRUE;	/* set global personal mail flag true */
			persmail = TRUE;	/* set local personal mail flag true  */
			persave = TRUE;
		}
		if (alias){
			if (!strnicmp(User1Name, (char *) Qmail.ForWhom, NamLen1)) {
				pmail = TRUE;	/* set global personal mail flag true */
				persmail = TRUE;	/* set local personal mail flag true  */
				persave = TRUE;
			}
		}
		NbBlocs = atoi((char *) Qmail.SizeMsg) - 1;
		MaxChars = NbBlocs * 128L ;
		/* check buffer size and re-allocate if needed */
		if ( MaxChars > (unsigned long) RbufSize-128 ){
			if( MaxChars <= MAXBUF-128 )
			{
				if(!reup( MaxChars+128) )
					MaxChars = RbufSize-128 ;
			}
			else    {
				printf ("%d = NbBlocs\n", NbBlocs );
				printf("%s [ > %lu bytes ]\n", txt[72], MAXBUF );	/* "msg too big" */
				fclose(fs);
				fclose(fd);
				fclose(pfd);
				fclose(fx);
				fclose(pfx);
				return (ERROR); 
			}
		}
		if ( WriteIndex(fx, count, MaxChars, last) == ERROR ){	/* Write Index Structure */
				printf("Error writing fidx MkIndex()\n" );	/* "msg too big" */
				fclose(fs);
				fclose(fd);
				fclose(pfd);
				fclose(fx);
				fclose(pfx);
				return (ERROR); 
		}
		count++;
		if (persave) {
			if ( WriteIndex(pfx, pcount, MaxChars, plast) == ERROR ){ 	/* Write Personal Index */
				printf("Error writing pidx MkIndex()\n" );	/* "msg too big" */
				fclose(fs);
				fclose(fd);
				fclose(pfd);
				fclose(fx);
				fclose(pfx);
				return (ERROR); 
			}
			pcount++;
		}
		printf("%d : %ld\r", iconf, count - oldcount);
		fflush(stdout);
		if( MaxChars != 0 ){
		if ( fread((char *) (rbuf + Head), 128, NbBlocs, fs) != NbBlocs ) {	/* Read the message..... */
			printf("max char %ld\n", MaxChars);
			printf("%s %s\n", txt[58], Src);	/* "error reading file " */
			fclose(fs);
			fclose(fd);
			fclose(pfd);
			fclose(fx);
			fclose(pfx);
			return (ERROR);
		}
		}
		ptr = (unsigned char *) (rbuf + Head);
		for (i = 0; i < MaxChars; i++) {	/* Translate to normal Line Feed */
			if (*ptr == 227)
				*ptr = '\n';
			ptr++;
		}
		while (*(--ptr) == 0x20)
			*ptr = 0;	/* delete padding spaces */
        if ( fwrite(rbuf, 128, NbBlocs+1 , fd) != NbBlocs+1 ) {    /* And copy it.         */
			printf("%s %s\n", txt[73], Dst);	/* "error writing file " */
			fclose(fs);
			fclose(fd);
			fclose(pfd);
			fclose(fx);
			fclose(pfx);
			return (ERROR);
		}
		if (persave) {
            if ( fwrite(rbuf, 128, NbBlocs+1, pfd) != NbBlocs+1 ) {   /* And copy it.         */
				printf("%s %s\n", txt[73], Dst);	/* "error writing file " */
				fclose(fs);
				fclose(fd);
				fclose(pfd);
				fclose(fx);
				fclose(pfx);
				return (ERROR);
			}
			plast += (long) MaxChars + Head;
		}
		last += (long) MaxChars + Head;	/* INC counter... */
	}			/* end of while(1) */

	if (conf >= 0) {
		printf("Conference %d \t[ %-15s ]%c  %3ld New %s %s \n",
		       ConfNumbers[iconf], ConfName[iconf],
		       persmail ? '*' : ' ', count - oldcount,
		       (count - oldcount) > 1 ? "Messages" : "Message",
		       persmail ? txt[103] : " ");
	}
	printf("\n");
	fclose(fs);
	fclose(fd);
	fclose(pfd);
	fclose(fx);
	fclose(pfx);

	if (!plast) {
		unlink(Pidx);	/* if no personal messages, delete personal cnf. */
		unlink(Pdst);
	}
	ActvConf();		/* update array of active conferences */
    return(OK);
}


/* *************************** end of MkIndex ******************************/

/*
 * Add an index struct to the index file.
 */

int
WriteIndex( FILE * fx, const long count, const unsigned long Size, const long Offset)
{
	struct MyIndex Yndex ;

	Yndex.LastRead =  count ? 0L : VIRGINDX ;  /* no count implies new index */
	Yndex.MaxMsg = 0L ; /* future use */
	Yndex.MsgNum = count;
	Yndex.Offset = Offset;
	Yndex.Size = Size;

	if (fwrite (&Yndex, sizeof(struct MyIndex), 1, fx) != (size_t) 1 ) {
		printf("%s !\n", txt[74]);	/* "error writing index file " */
		return( ERROR );
	}
	return( OK );
}

 /*
  * strip space on right side
  */

void
StripRSpace(char *ptr)
{
	while (*ptr == '\040') {
		*ptr = '\0';
		 ptr-- ;
	}
}

 /*
  * strip space on left side
  */

void
StripLSpace(char *ptr)
{
	while (*ptr == ' ')
		ShiftLeft(ptr, 1);
}


 /*
  * Open a .cnf or idx file for read/write
  * 
  */
int 
OpenCon(FILE ** pf, FILE ** fs, const char *dspath)
{
	if (access(dspath, F_OK )) {/* Create if not exist */
		*pf = fopen(dspath, "wb");
		fclose(*pf);
	}
    if ((*pf = fopen(dspath, "r+b")) == NULL)
	{
		printf("%s %s...\n", txt[51], dspath);	/* "unable to open file" */
		if( *fs != NULL )
			fclose(*fs);
		return (ERROR);
	}
	return(OK);
}

 /*
  * Convert 13 bit binary integer in header to ordinary integer
  */

int
readCnum(const byte * ptr)
{
	unsigned char   p, q;
	unsigned int    j;

	p = *ptr;
	ptr++;
	q = *ptr;
	q = q & 0x1f;
	j = (unsigned int) q;
	j <<= 8;
	j += (unsigned int) p;

	return (j);
}



/*
 * Update array tracking which conferences are active
 *
 */

void
ActvConf(void)
{
	int             i ;
	char            tmp[MAXPATHS];
#ifdef __MSDOS__
    struct ffblk    merv ;
    ActvCnt = 0;

    for (i = 0; i <= LastConf; i++) ConfActive[i] = FALSE ;
    sprintf(tmp,"%s%s%c*.idx", HomePath, CurBoard, SEP );

    if(!findfirst( (char *) tmp, &merv, 0 )){
        i =  findCindex( atoi(merv.ff_name) ) ;
        if( i >= 0 ) {
            ConfActive[i] = TRUE ;
            ActvCnt = 1 ;
        }
        while(!findnext( &merv )){
            i =  findCindex( atoi(merv.ff_name) ) ;
            if( i >= 0 ) {
                ConfActive[i] = TRUE ;
                ActvCnt++ ;
            }
        }
    }
}
#else /* unix et al */
    ActvCnt = 0 ;

    for (i = 0; i <= LastConf; i++) {
		sprintf(tmp, "%s%s%c%d.cnf", HomePath, CurBoard, SEP, ConfNumbers[i]);
		if (!access(tmp, F_OK )) {
			ConfActive[i] = TRUE;
			ActvCnt++;
		} else {
			ConfActive[i] = FALSE;
		}
	}

}
#endif

static void
Work2home( const char *DstDir, const char *SrDir, const char *pilgrim ) {

	char            Src[MAXPATHS];	/* Source file Message.DAT */
	char            Dst[MAXPATHS];	/* Conference data  file   */

	if( *pilgrim == 0 || *pilgrim == 0x20 ) 
		return ;
	sprintf(Src, "%s%c%s", SrDir,  SEP, pilgrim);

	if ( access(Src, F_OK ) == 0 ) {
		sprintf(Dst, "%s%c%s", DstDir, SEP, pilgrim );
		if( access(Dst, F_OK ) == 0 )
			if( unlink(Dst) != 0 ) {
				printf("Can't remove destination %s %c\n", Dst, BELL );
				sleep(3);
			}
		if ( link(Src, Dst) != 0 )  /* try linking first */
#ifdef HAVE_LINK
			if( fcopy( Src, Dst) != 0 ) /* unix will try copying too */
#endif			
		{
			printf("Can't link source %s\n", Src );
			printf("Check destination permissions %s\n", Dst );
			exit(0);
		}
		unlink(Src);
	}
}

/*
 * FCOPY : fast copy of a file. return :  0 on success 1 on error
 * it is helpful to be able to duplicate the source file's time stamp
 * too. While not absolutely needed for ATP, there are times when
 * it is useful when dealing with mail packets from systems whose time
 * clock is out of synch with yours. This is a fine point and if you
 * are porting ATP to a new system, you may comment out the time functions
 * in fcopy() to ease initial porting with no serious side effects. 
 */
static int
fcopy(const char *source, const char *destin)
{
	int             src, dst;
	int             acc = ( O_RDWR | O_CREAT | O_TRUNC ) ;
	int 			nb;
	char           *buf;
#ifdef UNIXCMDS
	mode_t 			perms = 0666 ;
#else
    int				perms = ( S_IREAD | S_IWRITE ) ;
#endif	
#if defined(HAVE_UTIME)
	struct stat		srctime ;
	struct utimbuf  restime ;
	if( stat( source, &srctime ) == ERROR ) {
		printf("%s %s\n", txt[49], source);	/* "unable to read file" */
		return (ERROR);
	}
	restime.actime  = srctime.st_atime ;
	restime.modtime = srctime.st_mtime ;
#elif defined(HAVE_FTIME)
	struct ftime	dsrctime ;
#endif	
	if ((buf = malloc(4096)) == NULL) {
		printf("%s", txt[1]);
		return (ERROR);
	}
	if ((src = open(source, O_RDONLY)) == ERROR )
	{
		printf("%s %s\n", txt[49], source);	/* "unable to read file" */
		free(buf);
		return (ERROR);
	}
#ifdef HAVE_FTIME
	getftime( src, &dsrctime ); /* no utime for DOS, WIN etc???? */
#endif		
	if ((dst = open(destin, acc, perms )) == ERROR ) {
		printf("%s %s\n", txt[50], source);	/* "unable to create file" */
		free(buf);
		return (ERROR);
	}
	while ( (nb = read(src, buf, 4096)) > 0 ) {
		write(dst, buf, (unsigned) nb);
	}
#ifdef HAVE_FTIME  /* msdos Borland Turbo C++, what about WIN and MSC?? */
	setftime( dst, &dsrctime ) ;
#endif
	close(src);
	close(dst);
#ifdef HAVE_UTIME 
	utime( destin, &restime ) ;
#endif	
	free(buf);
	return (0);
}
/* end of qlib.c */
