/*****************************************************************************
 *
 * $Id$
 * Purpose ...............: .TIC files magic processing. 
 *
 *****************************************************************************
 * 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 "../lib/libs.h"
#include "../lib/structs.h"
#include "../lib/users.h"
#include "../lib/records.h"
#include "../lib/common.h"
#include "../lib/clcomm.h"
#include "../lib/dbtic.h"
#include "tic.h"
#include "utic.h"
#include "magic.h"


long	MagicNr;		/* Current magic record number		    */
int	Magics = 0;		/* Processed magics			    */



char *Magic_Macro(int);
char *Magic_Macro(int C)
{
	static char	buf[PATH_MAX];

	buf[0] = '\0';
	switch(toupper(C)) {
		case 'F':
			sprintf(buf, "%s/%s", TIC.BBSpath, TIC.NewName);
			break;

		case 'P':
			sprintf(buf, "%s", TIC.BBSpath);
			break;

		case 'N':
			sprintf(buf, "%s", strtok(strdup(TIC.NewName), "."));
			break;

		case 'E':
			sprintf(buf, "%s", strrchr(TIC.NewName, '.'));
			break;

		case 'L':
			sprintf(buf, "%s", strrchr(TIC.NewName, '.'));
			buf[0] = buf[1];
			buf[1] = buf[2];
			buf[2] = '\0';
			break;

		case 'D':
			sprintf(buf, "%03d", Day_Of_Year());
			break;

		case 'C':
			sprintf(buf, "%03d", Day_Of_Year());
			buf[0] = buf[1];
			buf[1] = buf[2];
			buf[2] = '\0';
			break;

		case 'A':
			sprintf(buf, "%s", TIC.TicIn.Area);
			break;
	}

	Syslog('f', "Mgc Macro(%c): \"%s\"", C, buf);
	return buf;
}




int GetMagicRec(int Typ, int First)
{
    int	    Eof = FALSE;
    char    *temp, *p, *q, mask[256];
    FILE    *FeM;

    if (First)
	MagicNr = 0;

    temp = calloc(PATH_MAX, sizeof(char));
    sprintf(temp, "%s/etc/magic.data", getenv("MBSE_ROOT"));
    if ((FeM = fopen(temp, "r")) == NULL) {
	Syslog('+', "Huh? No magic file? (%s)", temp);
	free(temp);
	return FALSE;
    }

    fread(&magichdr, sizeof(magichdr), 1, FeM);

    do {
	if (fseek(FeM, magichdr.hdrsize + (MagicNr * magichdr.recsize), SEEK_SET) != 0) {
	    WriteError("$Can't seek record %ld in %s", MagicNr, temp);
	    free(temp);
	    fclose(FeM);
	    return FALSE;
	}

	MagicNr++;

	if (fread(&magic, magichdr.recsize, 1, FeM) == 1) {

	    if ((magic.Active) && (magic.Attrib == Typ) && (strcasecmp(magic.From, TIC.TicIn.Area) == 0)) {

		memset(&mask, 0, sizeof(mask));
		p = magic.Mask;
		q = mask;
		*q++ = '^';
		while ((*p) && (q < (mask + sizeof(mask) - 4))) {
		    switch(*p) {
			case '\\':  *q++ = '\\'; *q++ = '\\'; break;
			case '?':   *q++ = '.'; break;
			case '.':   *q++ = '\\'; *q++ = '.'; break;
			case '+':   *q++ = '\\'; *q++ = '+'; break;
			case '*':   *q++ = '.'; *q++ = '*'; break;
			case '@':   sprintf(q, "[A-Za-z]"); while (*q) q++; break;
			case '#':   sprintf(q, "[0-9]"); while (*q) q++; break;
			default:    *q++ = *p; break;
		    }
		    p++;
		}
		*q++ = '$';
		*q = '\0';

		if ((re_comp(mask)) == NULL) {
		    if (re_exec(TIC.NewName)) {
			fclose(FeM);
			free(temp);
			return TRUE;
		    }
		} else {
		    WriteError("Magic: re_comp(%s) failed", mask);
		}
	    }

	} else {
	    Eof = TRUE;
	}

    } while (!Eof);

    free(temp);
    fclose(FeM);
    return FALSE;
}



void MagicResult(char *format, ...)
{
	char	*outputstr;
	va_list	va_ptr;

	outputstr = calloc(1024, sizeof(char));

	va_start(va_ptr, format);
	vsprintf(outputstr, format, va_ptr);
	va_end(va_ptr);

	Syslog('+', "Magic: %s", outputstr);
	free(outputstr);
	Magics++;
}



void Magic_CheckCompile(void);
void Magic_CheckCompile(void)
{
	if (magic.Compile) {
		CompileNL = TRUE;
		Syslog('+', "Magic: Trigger Compile Nodelists");
	}
}



int Magic_MoveFile(void)
{
	if (GetMagicRec(MG_MOVE, TRUE)) {
		strcpy(TIC.TicIn.Area, magic.ToArea);
		MagicResult((char *)"Move %s to Area %s", TIC.RealName, TIC.TicIn.Area);
		return TRUE;	
	} else
		return FALSE;
}



void Magic_ExecCommand(void)
{
	int	i, j, k, Err, First = TRUE;
	char	Line[256];
	char	Temp[PATH_MAX];
	char	*cmd, *opts;

	while (GetMagicRec(MG_EXEC, First)) {
		First = FALSE;
		j = 0;
		memset(&Line, 0, sizeof(Line));
		for (i = 0; i < strlen(magic.Cmd); i++) {
			if (magic.Cmd[i] != '%') {
				Line[j] = magic.Cmd[i];
				j++;
			} else {
				i++;
				if (magic.Cmd[i] == '%') {
					Line[j] = '%';
					j++;
				} else {
					Temp[0] = '\0';
					sprintf(Temp, "%s", Magic_Macro(magic.Cmd[i]));
					for (k = 0; k < strlen(Temp); k++) {
						Line[j] = Temp[k];
						j++;
					}
				}
			}
		}

		cmd = xstrcpy(getenv("MBSE_ROOT"));
		cmd = xstrcat(cmd, (char *)"/bin/");
		cmd = xstrcat(cmd, strtok(Line, " "));
		opts = strtok(NULL, "\0");
		MagicResult((char *)"Exec: \"%s %s\"", cmd, opts);
	
		if ((Err = execute(cmd, opts, NULL, (char *)"/dev/null", (char *)"/dev/null", (char *)"/dev/null")) == 0) {
			Magics++;
		} else
			Syslog('!', "Mgc Exec: (%s %s) returns %d", cmd, opts, Err);

		Magic_CheckCompile();
	}
}



void Magic_CopyFile(void)
{
	int	First = TRUE, rc;
	char	*From, *To;

	From = calloc(PATH_MAX, sizeof(char));
	To   = calloc(PATH_MAX, sizeof(char));

	while (GetMagicRec(MG_COPY, First)) {
		First = FALSE;
		sprintf(From, "%s/%s", TIC.BBSpath, TIC.NewName);
		sprintf(To, "%s/%s", magic.Path, TIC.NewName);

		if ((rc = file_cp(From, To) == 0)) {
			MagicResult((char *)"%s copied to %s", From, To);
			Magic_CheckCompile();
		} else
			WriteError("Magic: copy: %s to %s failed, %s", strerror(rc));
	}

	free(From);
	free(To);
}



void Magic_UnpackFile(void)
{
    int	    rc, First = TRUE;
    char    *Fn, *buf = NULL, *unarc = NULL, *cmd = NULL;

    Fn = calloc(PATH_MAX, sizeof(char));
    while (GetMagicRec(MG_UNPACK, First)) {
	First = FALSE;
	buf = calloc(PATH_MAX, sizeof(char));
	getcwd(buf, 128);

	if (chdir(magic.Path) == 0) {
	    sprintf(Fn, "%s/%s", TIC.BBSpath, TIC.NewName);
	    if ((unarc = unpacker(Fn)) != NULL) {
		if (getarchiver(unarc)) {
		    cmd = xstrcpy(archiver.funarc);
		    if (strlen(cmd)) {
			rc = execute(cmd, Fn, (char *)NULL, (char *)"/dev/null", (char *)"/dev/null", (char *)"/dev/null");
			sync();
			if (rc)
			    WriteError("$Magic: unpack in %s, error %d", magic.Path, rc);
			else
			    MagicResult((char *)"unpacked in %s", magic.Path);
		    }
		    free(cmd);
		}
	    }

	    chdir(buf);
	    Magic_CheckCompile();
	} else
	    WriteError("$Magic: unpack: can't chdir \"%s\"", magic.Path);

	free(buf);
    }
    free(Fn);
}



void Magic_Keepnum(void)
{
	if (GetMagicRec(MG_KEEPNUM, TRUE)) {
		TIC.KeepNum = magic.KeepNum;
		MagicResult((char *)"Keep %d files", TIC.KeepNum);
	}
}



void Magic_UpDateAlias(void)
{
	if (GetMagicRec(MG_UPDALIAS, TRUE)) {
		UpDateAlias(TIC.TicIn.Area);
		MagicResult((char *)"Update Alias");
	}
}



void Magic_AdoptFile(void)
{
	int	First = TRUE;
	char	*temp;
	FILE	*Tf;

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

	while (GetMagicRec(MG_ADOPT, First)) {
		First = FALSE;

		if (SearchTic(magic.ToArea)) {
			MagicResult((char *)"Adoptfile in %s", magic.ToArea);

			sprintf(temp, "%s/%s", TIC.Inbound, MakeTicName());
			if ((Tf = fopen(temp, "a+")) == NULL)
				WriteError("$Can't create %s", temp);
			else {
				fprintf(Tf, "Hatch\r\n");
				fprintf(Tf, "NoMove\r\n");
				fprintf(Tf, "Created MBSE BBS v%s, %s\r\n", VERSION, SHORTRIGHT);
				fprintf(Tf, "Area %s\r\n", magic.ToArea);
				fprintf(Tf, "Origin %s\r\n", aka2str(tic.Aka));
				fprintf(Tf, "From %s\r\n", aka2str(tic.Aka));
				if (strlen(TIC.TicIn.Replace))
					fprintf(Tf, "Replaces %s\r\n", TIC.TicIn.Replace);
				if (strlen(TIC.TicIn.Magic))
					fprintf(Tf, "Magic %s\r\n", TIC.TicIn.Magic);
				fprintf(Tf, "File %s\r\n", TIC.NewName);
				fprintf(Tf, "Pth %s\r\n", TIC.BBSpath);
				fprintf(Tf, "Desc %s\r\n", TIC.TicIn.Desc);
				fprintf(Tf, "Crc %s\r\n", TIC.TicIn.Crc);
				fprintf(Tf, "Pw %s\r\n", CFG.hatchpasswd);
				fclose(Tf);
			}

			SearchTic(TIC.TicIn.Area);
		} else
			WriteError("Mgc Adopt: Area \"%s\" not found");
	}

	free(temp);
}



int Magic_DeleteFile(void)
{
	int	Result;

	if ((Result = GetMagicRec(MG_DELETE, TRUE)))
		MagicResult((char *)"Delete file");

	return Result;
}