/*
     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.
*/
	 

/*
 * Change Log: $Log: qlib.c,v $
 * Revision 1.400  1992/11/28  09:54:55  root
 * second release
 *
 * Revision 1.310  1992/07/08  23:15:14  root
 * first release -- minor bug fix in read.c, qlib.c
 *
 * Revision 1.30   1992/07/05  15:36:19  root
 * first release of ATP
 *
 * Revision 1.2  1992/04/19  13:21:44  root 1st semifunctional UNIX version.
 *
 */


#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 <unistd.h>
#ifdef DJD
#include <dir.h>
#endif
#else
#include <alloc.h>
#include <io.h>
#include <dir.h> 
fcopy(const char *source, const char *destin) ;
#endif
#include "system.h"
#include "ansi.h"
#include "qlib.h"
#include "reader.h"
#include "readlib.h"
#define  call   goto

static struct MsgHeaderType Qmail;
static int    alias = FALSE ; /* if (UserName1 != UserName2 ) alias = TRUE */ 
/* Variables used to read Control.dat         */

int             LastConf = 0;	/* read from control.dat number of conf. field */
char            BoardName[76],
                UserName[50],
				UserName1[50],	/* user name taken from atprc       */ 
				UserName2[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(UserName2, 50, fp) == NULL)
		call            bad_end;

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

	/*--- 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() */

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

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

MFREE ( ConfName );
if ((ConfName = MALLOK ( (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], 15, fp) == NULL)
			call            bad_end;
		StripSpaceL(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 " */
	
	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, i ;
	long int        oldcount = 0;
	unsigned        Head = sizeof(struct MsgHeaderType), NbBlocs;
	unsigned long   MaxChars;
	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     */
	long            last, plast ;
	int             ffllgg = FALSE;
    unsigned    	NamLen, NamLen1 = 0  ;

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

	sprintf(Src, "%s%c%s", SrcDir,  SEP, CNTRL_FILE);
	sprintf(Dst, "%s%c%s", DestDir, SEP, CNTRL_FILE);
	unlink(Dst);
	if (link(Src, Dst)) {
		return (ERROR);
	} else
		unlink(Src);

	/* Update Newfiles.dat in bbs dir */

	sprintf(Src, "%s%c%s", SrcDir, SEP, NEWFILES);
	if (!access(Src, F_OK )) {
		sprintf(Dst, "%s%c%s", DestDir, SEP, NEWFILES);
		unlink(Dst);
		if (link(Src, Dst))
			return (ERROR);
		else
			unlink(Src);
	}
	/* Update Welcome file in bbs dir */

	sprintf(Src, "%s%c%s", SrcDir, SEP, WELCOME);	/* try ascii mode */
	if (!access(Src, F_OK )) {
		sprintf(Dst, "%s%c%s", DestDir, SEP, WELCOME);
		unlink(Dst);
		if (link(Src, Dst))
			return (ERROR);
		else
			unlink(Src);
	}
	sprintf(Src, "%s%c%s", SrcDir, SEP, WELCOMEG);	/* or ansi mode */
	if (!access(Src, F_OK )) {
		sprintf(Dst, "%s%c%s", DestDir, SEP, WELCOMEG);
		unlink(Dst);
		if (link(Src, Dst))
			return (ERROR);
		else
			unlink(Src);
	}
	/* Update News file in bbs dir */
	sprintf(Src, "%s%c%s", SrcDir, SEP, NEWS);	/* try ascii mode */
	if (!access(Src, F_OK )) {
		sprintf(Dst, "%s%c%s", DestDir, SEP, NEWS);
		unlink(Dst);
		if (link(Src, Dst))
			return (ERROR);

	}
	sprintf(Src, "%s%c%s", SrcDir, SEP, NEWSG);	/* or ansi mode */
	if (!access(Src, F_OK )) {
		sprintf(Dst, "%s%c%s", DestDir, SEP, NEWSG);
		unlink(Dst);
		if (link(Src, Dst))
			return (ERROR);
		else
			unlink(Src);
	}
	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, 1, Head, fs)) {
		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, 1, Head, fs))
			break;	/* End of file         */
		if ( Qmail.Status == 0 )
			break;	/* End of file  J mail ?  */

		/* Faudrait tester un peu mieux que a... enfin...           */

  		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(UserName1, (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); 
			}
		}
		WriteIndex(fx, count, MaxChars, last);	/* Write Index Structure */
		count++;
		if (persave) {
			WriteIndex(pfx, pcount, MaxChars, plast);	/* Write Personal Index */
			pcount++;
		}
		printf("%d : %ld\r", iconf, count - oldcount);
		fflush(stdout);
		if( MaxChars != 0 ){
		if (!fread((char *) (rbuf + Head), 128, NbBlocs, fs)) {	/* 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)) {    /* 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)) {   /* 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.
 */

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

	Yndex.LastRead = 0;
	Yndex.MaxMsg = 0 ; /* future use */
	Yndex.MsgNum = count;
	Yndex.Offset = Offset;
	Yndex.Size = Size;

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

 /*
  * strip space on right side
  */

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

 /*
  * strip space on left side
  */

void
StripSpaceL(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)
{
	extern int      ActvCnt;
	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

/* end of qlib.c */
