/*****************************************************************************
 *
 * $Id$
 * Purpose ...............: Add TIC file to the BBS
 *
 *****************************************************************************
 * Copyright (C) 1997-2005
 *   
 * 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, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/

#include "../config.h"
#include "../lib/mbselib.h"
#include "../lib/users.h"
#include "../lib/mbsedb.h"
#include "orphans.h"
#include "tic.h"
#include "fsort.h"
#include "qualify.h"
#include "addbbs.h"


extern	int	tic_imp;


/*
 *  Add file to the BBS file database and place it in the download
 *  directory. If it is replacing a file, a file with a matching name
 *  will be deleted. If there is a limit on the number of files with
 *  the  same name pattern, the oldest files will be deleted. The
 *  files database will be packed if necessary. All modifications are
 *  done on temp files first.
 */
int Add_BBS(qualify **qal)
{
    struct FILE_record	    frec;
    int			    rc, i, Found = FALSE, Keep = 0, DidDelete = FALSE;
    char		    temp1[PATH_MAX], temp2[PATH_MAX], *fname, *lname, *p;
    fd_list		    *fdl = NULL;
    struct _fdbarea	    *fdb_area = NULL;
    qualify		    *tmpq;
    faddr		    *taka;

    /*
     * First check for an existing record with the same filename,
     * if it exists, update the record and we are ready. This will
     * prevent for example allfiles.zip to get a new record everytime
     * and thus the download counters will be reset after a new update.
     */
    if ((fdb_area = mbsedb_OpenFDB(tic.FileArea, 30))) {
	while (fread(&frec, fdbhdr.recsize, 1, fdb_area->fp) == 1) {
	    if (strcmp(frec.Name, TIC.NewFile) == 0) {
		snprintf(temp1, PATH_MAX, "%s/%s", TIC.Inbound, TIC.NewFile);
		snprintf(temp2, PATH_MAX, "%s/%s", TIC.BBSpath, TIC.NewFile);
		mkdirs(temp2, 0755);
		if ((rc = file_cp(temp1, temp2))) {
		    WriteError("Copy to %s failed: %s", temp2, strerror(rc));
		    mbsedb_CloseFDB(fdb_area);
		    return FALSE;
		}
		chmod(temp2, 0644);
		strncpy(frec.TicArea, TIC.TicIn.Area, sizeof(frec.TicArea) -1);
		frec.Size = TIC.FileSize;
		frec.Crc32 = TIC.Crc_Int;
		frec.Announced = TRUE;
		frec.FileDate = TIC.FileDate;
		frec.UploadDate = time(NULL);
		for (i = 0; i <= TIC.File_Id_Ct; i++) {
		    strcpy(frec.Desc[i], TIC.File_Id[i]);
		    if (i == 24)
			break;
		}
		if (mbsedb_LockFDB(fdb_area, 30)) {
		    fseek(fdb_area->fp, 0 - fdbhdr.recsize, SEEK_CUR);
		    fwrite(&frec, fdbhdr.recsize, 1, fdb_area->fp);
		    mbsedb_UnlockFDB(fdb_area);
		}
		mbsedb_CloseFDB(fdb_area);
		tic_imp++;
		if ((i = file_rm(temp1)))
		    WriteError("file_rm(%s): %s", temp1, strerror(i));
		return TRUE;
	    }
	}
	mbsedb_CloseFDB(fdb_area);
    }


    /*
     * Create filedatabase record.
     */
    memset(&frec, 0, sizeof(frec));
    strncpy(frec.Name, TIC.NewFile, sizeof(frec.Name) -1);
    if (strlen(TIC.NewFullName)) {
	strncpy(frec.LName, TIC.NewFullName, sizeof(frec.LName) -1);
    } else {
	/*
	 * No LFN, fake it with a lowercase copy of the 8.3 filename.
	 */
	strncpy(frec.LName, TIC.NewFile, sizeof(frec.LName) -1);
	for (i = 0; i < strlen(frec.LName); i++)
	    frec.LName[i] = tolower(frec.LName[i]);
    }
    strncpy(frec.TicArea, TIC.TicIn.Area, 20);
    frec.Size = TIC.FileSize;
    frec.Crc32 = TIC.Crc_Int;
    frec.Announced = TRUE;
    snprintf(frec.Uploader, 36, "Filemgr");
    frec.UploadDate = time(NULL);
    frec.FileDate = TIC.FileDate;
    for (i = 0; i <= TIC.File_Id_Ct; i++) {
	strcpy(frec.Desc[i], TIC.File_Id[i]);
	if (i == 24)
	    break;
    }
    if (strlen(TIC.TicIn.Magic)) {
	strncpy(frec.Magic, TIC.TicIn.Magic, sizeof(frec.Magic) -1);
    }

    snprintf(temp1, PATH_MAX, "%s/%s", TIC.Inbound, TIC.NewFile);
    snprintf(temp2, PATH_MAX, "%s/%s", TIC.BBSpath, frec.Name);
    mkdirs(temp2, 0755);

    if ((rc = file_cp(temp1, temp2))) {
	WriteError("Copy to %s failed: %s", temp2, strerror(rc));
	return FALSE;
    }
    chmod(temp2, 0644);

    /*
     * If LFN = 8.3 name and is DOS 8.3 format, change the LFN to lowercase.
     */
    if (strcmp(frec.Name, frec.LName) == 0) {
	p = frec.LName;
	while (*p) {
	    if (islower(*p))
		Found = TRUE;
	    p++;
	}
	if (!Found) {
	    /*
	     * All uppercase, change to lowercase.
	     */
	    tl(frec.LName);
	    Syslog('f', "Converted LFN to lowercase: \"%s\"", frec.LName);
	}
    }
    Found = FALSE;
    lname = calloc(PATH_MAX, sizeof(char));
    snprintf(lname, PATH_MAX, "%s/%s", TIC.BBSpath, frec.LName);
    if (symlink(temp2, lname)) {
	WriteError("$Create link %s to %s failed", temp2, lname);
    }
    free(lname);

    if ((fdb_area = mbsedb_OpenFDB(tic.FileArea, 30)) == NULL)
	return FALSE;
    mbsedb_InsertFDB(fdb_area, frec, area.AddAlpha);
    mbsedb_CloseFDB(fdb_area);

    /*
     * Delete file from the inbound
     */
    if ((i = file_rm(temp1)))
	WriteError("file_rm(%s): %s", temp1, strerror(i));

    /*
     * Handle the replace option.
     */
    if ((strlen(TIC.TicIn.Replace)) && (tic.Replace)) {
	Syslog('f', "Must Replace: %s", TIC.TicIn.Replace);

	if ((fdb_area = mbsedb_OpenFDB(tic.FileArea, 30))) {
	    while (fread(&fdb, fdbhdr.recsize, 1, fdb_area->fp) == 1) {
		if (strlen(fdb.LName) == strlen(frec.LName)) {
		    // FIXME: Search must be based on a reg_exp search
		    if (strcasecmp(fdb.LName, frec.LName) != 0) {
			Found = TRUE;
			for (i = 0; i < strlen(frec.LName); i++) {
			    if ((TIC.TicIn.Replace[i] != '?') && (toupper(TIC.TicIn.Replace[i]) != toupper(fdb.LName[i])))
				Found = FALSE;
			}
			if (Found) {
			    Syslog('+', "Replace: Deleting: %s", fdb.LName);
			    fdb.Deleted = TRUE;
			    if (mbsedb_LockFDB(fdb_area, 30)) {
				fseek(fdb_area->fp , - fdbhdr.recsize, SEEK_CUR);
				fwrite(&fdb, fdbhdr.recsize, 1, fdb_area->fp);
				mbsedb_UnlockFDB(fdb_area);
			    }
			    DidDelete = TRUE;
			}
		    }
		}
	    }
	    mbsedb_CloseFDB(fdb_area);
	}
    }

    /*
     * Handle the Keep number of files option
     */
    if (TIC.KeepNum) {
	if ((fdb_area = mbsedb_OpenFDB(tic.FileArea, 30))) {
	    while (fread(&fdb, fdbhdr.recsize, 1, fdb_area->fp) == 1) {
		if ((strlen(fdb.LName) == strlen(frec.LName)) && (!fdb.Deleted)) {
		    Found = TRUE;

		    for (i = 0; i < strlen(fdb.LName); i++) {
			if ((frec.LName[i] < '0') || (frec.LName[i] > '9')) {
			    if (frec.LName[i] != fdb.LName[i]) {
				Found = FALSE;
			    }
			}
		    }
		    if (Found) {
			Keep++;
			fill_fdlist(&fdl, fdb.LName, fdb.UploadDate);
		    }
		}
	    }
	    mbsedb_CloseFDB(fdb_area);
	}

	/*
	 * If there are files to delete, mark them.
	 */
	if (Keep > TIC.KeepNum) {
	    sort_fdlist(&fdl);

	    if ((fdb_area = mbsedb_OpenFDB(tic.FileArea, 30))) {
		for (i = 0; i < (Keep - TIC.KeepNum); i++) {
		    fname = pull_fdlist(&fdl);
		    fseek(fdb_area->fp, fdbhdr.hdrsize, SEEK_SET);
		    while (fread(&fdb, fdbhdr.recsize, 1, fdb_area->fp) == 1) {
			if (strcmp(fdb.LName, fname) == 0) {
			    Syslog('+', "Keep %d files, deleting: %s", TIC.KeepNum, fdb.LName);
			    fdb.Deleted = TRUE;
			    if (mbsedb_LockFDB(fdb_area, 30)) {
				fseek(fdb_area->fp , - fdbhdr.recsize, SEEK_CUR);
				fwrite(&fdb, fdbhdr.recsize, 1, fdb_area->fp);
				mbsedb_UnlockFDB(fdb_area);
			    }
			    DidDelete = TRUE;
			}
		    }
		}
		mbsedb_CloseFDB(fdb_area);
    		}
	}
	tidy_fdlist(&fdl);
    }

    /*
     *  Now realy delete the marked files and clean the file
     *  database.
     */
    if (DidDelete) {
	if ((fdb_area = mbsedb_OpenFDB(tic.FileArea, 30))) {
	    while (fread(&fdb, fdbhdr.recsize, 1, fdb_area->fp) == 1) {
		if (fdb.Deleted) {
		    snprintf(temp2, PATH_MAX, "%s/%s", area.Path, fdb.LName);
		    if (unlink(temp2) != 0)
		        WriteError("$Can't unlink file %s", temp2);
		    snprintf(temp2, PATH_MAX, "%s/%s", area.Path, fdb.Name);

		    /*
		     * With the path to the 8.3 name, we can check if this file
		     * is attached for any possible downlink. We use the qualify
		     * list created by the ptic function to check connected nodes
		     * only.
		     */
		    for (tmpq = *qal; tmpq; tmpq = tmpq->next) {
			if (tmpq->send) {
			    taka = fido2faddr(tmpq->aka);
			    un_attach(taka, temp2);
			    tidy_faddr(taka);
			}
		    }

		    if (unlink(temp2) != 0)
		        WriteError("$Can't unlink file %s", temp2);
		    snprintf(temp2, PATH_MAX, "%s/.%s", area.Path, fdb.Name);
		    unlink(temp2); /* Thumbnail, no logging if there is an error */
		}
	    }
	    mbsedb_PackFDB(fdb_area);
	    mbsedb_CloseFDB(fdb_area);
	    DidDelete = FALSE;
	}
    }

    tic_imp++;
    return TRUE;
}