/*****************************************************************************
 *
 * $Id$
 * Purpose: File Database Maintenance - utilities
 *
 *****************************************************************************
 * Copyright (C) 1997-2002
 *   
 * Michiel Broek		FIDO:		2:280/2802
 * Beekmansbos 10
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS 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 2, or (at your option) any
 * later version.
 *
 * MBSE BBS 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 MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "../config.h"
#include "../lib/libs.h"
#include "../lib/memwatch.h"
#include "../lib/structs.h"
#include "../lib/users.h"
#include "../lib/records.h"
#include "../lib/common.h"
#include "../lib/clcomm.h"
#include "../lib/dbcfg.h"
#include "../lib/mberrors.h"
#include "mbfutil.h"
#include "mbfile.h"



extern int	do_quiet;		/* Suppress screen output	    */
extern	int	e_pid;			/* Pid of external process	    */
extern time_t	t_start;		/* Start time			    */
extern time_t	t_end;			/* End time			    */
int		marker = 0;		/* Marker counter		    */



void ProgName(void)
{
	if (do_quiet)
		return;

	colour(WHITE, BLACK);
	printf("\nMBFILE: MBSE BBS %s File maintenance utility\n", VERSION);
	colour(YELLOW, BLACK);
	printf("        %s\n", COPYRIGHT);
}



void die(int onsig)
{
	/*
	 * First check if a child is running, if so, kill it.
	 */
	if (e_pid) {
		if ((kill(e_pid, SIGTERM)) == 0)
			Syslog('+', "SIGTERM to pid %d succeeded", e_pid);
		else {
			if ((kill(e_pid, SIGKILL)) == 0)
				Syslog('+', "SIGKILL to pid %d succeded", e_pid);
			else
				WriteError("$Failed to kill pid %d", e_pid);
		}

		/*
		 * In case the child had the tty in raw mode...
		 */
		if (!do_quiet)
			system("stty sane");
	}

	signal(onsig, SIG_IGN);

	if (onsig) {
		if (onsig <= NSIG)
			WriteError("Terminated on signal %d (%s)", onsig, SigName[onsig]);
		else
			WriteError("Terminated with error %d", onsig);
	}

	t_end = time(NULL);
	Syslog(' ', "MBFILE finished in %s", t_elapsed(t_start, t_end));

	if (!do_quiet) {
		colour(LIGHTGRAY, BLACK);
		fflush(stdout);
	}
	ExitClient(onsig);
}



void Help(void)
{
	do_quiet = FALSE;
	ProgName();

	colour(LIGHTCYAN, BLACK);
	printf("\nUsage:	mbfile [command] <options>\n\n");
	colour(LIGHTBLUE, BLACK);
	printf("	Commands are:\n\n");
	colour(CYAN, BLACK);
	printf("	a  adopt <area> <file> [desc]	Adopt file to area\n");
	printf("	c  check			Check filebase\n");
	printf("	d  delete <area> <file>		Mark file in area for deletion\n");
	printf("        im import <area>		Import files in current dir to area\n");
	printf("	in index			Create filerequest index\n");
	printf("        k  kill				Kill/move old files\n");
	printf("	l  list	[area]			List file areas or one area\n");
	printf("	m  move <from> <to> <file>	Move file from to area\n");
	printf("	p  pack				Pack filebase\n");
//	printf("	r  rearc <area> [file] [arc]	Rearc file(s) in area\n");
	printf("	t  toberep			Show toberep database\n");
	printf("	u  undelete <area> <file>	Mark file in area for undeletion\n");
	colour(LIGHTBLUE, BLACK);
	printf("\n	Options are:\n\n");
	colour(CYAN, BLACK);
	printf("	-a -announce			Suppress announce added files\n");
	printf("	-q -quiet			Quiet mode\n");
	printf("	-v -virus			Suppress virus scanning, use with care\n");
	die(MBERR_COMMANDLINE);
}



void Marker(void)
{
	/*
	 * Keep the connection with the server alive
	 */
	Nopper();

	/*
	 * Release system resources when running in the background
	 */
	if (CFG.slow_util && do_quiet)
		usleep(1);

	if (do_quiet)
		return;

	switch (marker) {
		case 0:	printf(">---");
			break;

		case 1:	printf(">>--");
			break;

		case 2:	printf(">>>-");
			break;

		case 3:	printf(">>>>");
			break;

		case 4:	printf("<>>>");
			break;

		case 5:	printf("<<>>");
			break;

		case 6:	printf("<<<>");
			break;

		case 7:	printf("<<<<");
			break;

		case 8: printf("-<<<");
			break;

		case 9: printf("--<<");
			break;

		case 10:printf("---<");
			break;

		case 11:printf("----");
			break;
	}
	printf("\b\b\b\b");
	fflush(stdout);

	if (marker < 11)
		marker++;
	else
		marker = 0;
}



void DeleteVirusWork()
{
        char    *buf, *temp;

        buf  = calloc(PATH_MAX, sizeof(char));
        temp = calloc(PATH_MAX, sizeof(char));
        getcwd(buf, PATH_MAX);
        sprintf(temp, "%s/tmp", getenv("MBSE_ROOT"));

        if (chdir(temp) == 0) {
                Syslog('f', "DeleteVirusWork %s/arc", temp);
                system("rm -r -f arc");
                system("mkdir arc");
        } else
                WriteError("$Can't chdir to %s", temp);

        chdir(buf);
        free(temp);
        free(buf);
}



int UnpackFile(char *File)
{
    char    *temp, *pwd, *unarc, *cmd;

    Syslog('f', "UnpackFile(%s)", File);

    if ((unarc = unpacker(File)) == NULL) {
	Syslog('f', "Unknown archive format %s", File);
	return FALSE;
    }

    temp = calloc(PATH_MAX, sizeof(char));
    pwd  = calloc(PATH_MAX, sizeof(char));
    getcwd(pwd, PATH_MAX);

    /*
     * Check if there is a temp directory to unpack the archive.
     */
    sprintf(temp, "%s/tmp/arc", getenv("MBSE_ROOT"));
    if ((access(temp, R_OK)) != 0) {
	if (mkdir(temp, 0777)) {
	    WriteError("$Can't create %s", temp);
	    if (!do_quiet)
		printf("Can't create %s\n", temp);
	    die(MBERR_GENERAL);
	}
    }

    /*
     * Check for stale FILE_ID.DIZ files
     */
    sprintf(temp, "%s/tmp/arc/FILE_ID.DIZ", getenv("MBSE_ROOT"));
    if (!unlink(temp))
	Syslog('+', "Removed stale %s", temp);
    sprintf(temp, "%s/tmp/arc/file_id.diz", getenv("MBSE_ROOT"));
    if (!unlink(temp))
	Syslog('+', "Removed stale %s", temp);

    if (!getarchiver(unarc)) {
	WriteError("No archiver available for %s", File);
	if (!do_quiet)
	    printf("No archiver available for %s\n", File);
	return FALSE;
    }

    cmd = xstrcpy(archiver.funarc);
    if ((cmd == NULL) || (cmd == "")) {
	WriteError("No unarc command available");
	if (!do_quiet)
	    printf("No unarc command available\n");
	return FALSE;
    }

    sprintf(temp, "%s/tmp/arc", getenv("MBSE_ROOT"));
    if (chdir(temp) != 0) {
	WriteError("$Can't change to %s", temp);
	die(MBERR_GENERAL);
    }

    if (execute(cmd, File, (char *)NULL, (char *)"/dev/null", (char *)"/dev/null", (char *)"/dev/null") == 0) {
	chdir(pwd);
	free(temp);
	free(pwd);
	free(cmd);
	sync();
	return TRUE;
    } else {
	chdir(pwd);
	WriteError("Unpack error, file may be corrupt");
	DeleteVirusWork();
    }
    sync();
    return FALSE;
}



/*
 * Add file to the BBS. The file is in the current
 * directory. The fdb record already has all needed
 * information.
 */
int AddFile(struct FILERecord fdb, int Area, char *DestPath, char *FromPath, char *LinkPath)
{
    char    *temp1, *temp2;
    FILE    *fp1, *fp2;
    int	    i, rc, Insert, Done = FALSE, Found = FALSE;

    /*
     * Copy file to the final destination and make a hard link with the
     * 8.3 filename to the long filename.
     */
    mkdirs(DestPath, 0775);
    if ((rc = file_cp(FromPath, DestPath))) {
	WriteError("Can't copy file in place");
	if (!do_quiet)
	    printf("Can't copy file to %s, %s\n", DestPath, strerror(rc));
	return FALSE;
    }
    chmod(DestPath, 0644);
    if (LinkPath) {
	if ((rc = symlink(DestPath, LinkPath))) {
	    WriteError("Can't create symbolic link %s", LinkPath);
	    if (!do_quiet)
		printf("Can't create symbolic link %s, %s\n", LinkPath, strerror(rc));
	    unlink(DestPath);
	    return FALSE;
	}
    }

    temp1 = calloc(PATH_MAX, sizeof(char));
    temp2 = calloc(PATH_MAX, sizeof(char));
    sprintf(temp1, "%s/fdb/fdb%d.data", getenv("MBSE_ROOT"), Area);
    sprintf(temp2, "%s/fdb/fdb%d.temp", getenv("MBSE_ROOT"), Area);

    fp1 = fopen(temp1, "r+");
    fseek(fp1, 0, SEEK_END);
    if (ftell(fp1) == 0) {
	/*
	 * No records yet
	 */
	fwrite(&fdb, sizeof(fdb), 1, fp1);
	fclose(fp1);
    } else {
	/*
	 * Files are already there. Find the right spot.
	 */
	fseek(fp1, 0, SEEK_SET);

	Insert = 0;
	do {
	    if (fread(&file, sizeof(file), 1, fp1) != 1)
		Done = TRUE;
	    if (!Done) {
		if (strcmp(fdb.LName, file.LName) == 0) {
		    Found = TRUE;
		    Insert++;
		} else {
		    if (strcmp(fdb.LName, file.LName) < 0)
			Found = TRUE;
		    else
			Insert++;
		}
	    }
	} while ((!Found) && (!Done));

	if (Found) {
	    if ((fp2 = fopen(temp2, "a+")) == NULL) {
		WriteError("Can't create %s", temp2);
		return FALSE;
	    }

	    fseek(fp1, 0, SEEK_SET);
	    /*
	     * Copy until the insert point
	     */
	    for (i = 0; i < Insert; i++) {
		fread(&file, sizeof(file), 1, fp1);
		/*
		 * If we are importing a file with the same name,
		 * skip the original record and put the new one in place.
		 */
		if (strcmp(file.LName, fdb.LName) != 0)
		    fwrite(&file, sizeof(file), 1, fp2);
	    }

	    if (area.AddAlpha)
		fwrite(&fdb, sizeof(fdb), 1, fp2);

	    /*
	     * Append the rest of the records
	     */
	    while (fread(&file, sizeof(file), 1, fp1) == 1) {
		if (strcmp(file.LName, fdb.LName) != 0)
		    fwrite(&file, sizeof(file), 1, fp2);
	    }
	    if (!area.AddAlpha)
		fwrite(&fdb, sizeof(fdb), 1, fp2);
	    fclose(fp1);
	    fclose(fp2);

	    if (unlink(temp1) == 0) {
		rename(temp2, temp1);
		chmod(temp1, 0660);
	    } else {
		WriteError("$Can't unlink %s", temp1);
		unlink(temp2);
		return FALSE;
	    }
	} else { /* if (Found) */
	    /*
	     * Append file record
	     */
	    fseek(fp1, 0, SEEK_END);
	    fwrite(&fdb, sizeof(fdb), 1, fp1);
	    fclose(fp1);
	}
    }

    free(temp1);
    free(temp2);
    return TRUE;
}



int CheckFDB(int Area, char *Path)
{
    char    *temp;
    FILE    *fp;
    int	    rc = FALSE;

    temp = calloc(PATH_MAX, sizeof(char));
    sprintf(temp, "%s/fdb/fdb%d.data", getenv("MBSE_ROOT"), Area);

    /*
     * Open the file database, create new one if it doesn't excist.
     */
    if ((fp = fopen(temp, "r+")) == NULL) {
	Syslog('!', "Creating new %s", temp);
	if ((fp = fopen(temp, "a+")) == NULL) {
	    WriteError("$Can't create %s", temp);
	    rc = TRUE;
	} else {
	    fclose(fp);
	}
    } else {
	fclose(fp);
    }

    /*
     * Set the right attributes
     */
    chmod(temp, 0660);

    /*
     * Now check the download directory
     */
    if (access(Path, W_OK) == -1) {
	sprintf(temp, "%s/foobar", Path);
	if (mkdirs(temp, 0755))
	    Syslog('+', "Created directory %s", Path);
    }

    free(temp);

    return rc;
}



/*
 * Load Area Record
 */
int LoadAreaRec(int Area)
{
    FILE    *pAreas;
    char    *sAreas;

    sAreas = calloc(PATH_MAX, sizeof(char));

    sprintf(sAreas, "%s/etc/fareas.data", getenv("MBSE_ROOT"));
    if ((pAreas = fopen (sAreas, "r")) == NULL) {
        WriteError("$Can't open %s", sAreas);
        if (!do_quiet)
            printf("Can't open %s\n", sAreas);
        return FALSE;
    }

    fread(&areahdr, sizeof(areahdr), 1, pAreas);
    if (fseek(pAreas, ((Area - 1) * areahdr.recsize) + areahdr.hdrsize, SEEK_SET)) {
        WriteError("$Can't seek record %d in %s", Area, sAreas);
        if (!do_quiet)
            printf("Can't seek record %d in %s\n", Area, sAreas);
        fclose(pAreas);
        free(sAreas);
        return FALSE;
    }

    if (fread(&area, areahdr.recsize, 1, pAreas) != 1) {
        WriteError("$Can't read record %d in %s", Area, sAreas);
        if (!do_quiet)
            printf("Can't read record %d in %s\n", Area, sAreas);
        fclose(pAreas);
        free(sAreas);
        return FALSE;
    }

    fclose(pAreas);
    free(sAreas);
    return TRUE;
}