/*****************************************************************************
 *
 * File ..................: mbcico/openfile.c
 * Purpose ...............: Fidonet mailer
 * Last modification date : 04-Oct-1999
 *
 *****************************************************************************
 * Copyright (C) 1997-1999
 *   
 * Michiel Broek		FIDO:		2:2801/16
 * Beekmansbos 10		Internet:	mbroek@ux123.pttnwb.nl
 * 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 "../include/libs.h"
#include "../include/structs.h"
#include "../common/common.h"
#include "config.h"
#include "lutil.h"
#include "openfile.h"


static FILE	*infp=NULL;
static char	*infpath=NULL;
static time_t	intime;
static int	isfreq;
char		*freqname=NULL;
int		gotfiles = FALSE;


/*
 * Try to find present (probably incomplete) file with the same timestamp
 * (it might be renamed), open it for append and store resync offset.
 * Store 0 in resync offset if the file is new. Return FILE* or NULL.
 * resync() must accept offset in bytes and return 0 on success, nonzero
 * otherwise (and then the file will be open for write).  For Zmodem,
 * resync is always possible, but for SEAlink it is not.  Do not try
 * any resyncs if remsize == 0.
 */
FILE *openfile(char *fname, time_t remtime, off_t remsize, off_t *resofs, int(*resync)(off_t))
{
	char		*opentype;
	char		*p, x;
	char		ctt[32];
	int		rc, ncount;
	struct stat	st;
	struct flock	fl;
	char		tmpfname[16];

	fl.l_type   = F_WRLCK;
	fl.l_whence = 0;
	fl.l_start  = 0L;
	fl.l_len    = 0L;
	strcpy(ctt,date(remtime));

#ifdef DEBUGO
	Syslog('s', "openfile(\"%s\",%s,%lu,...)",
		S(fname),S(ctt),(unsigned long)remsize);
#endif

	if ((fname == NULL) || (fname[0] == '\0')) {
		sprintf(tmpfname,"%08lx.pkt",(unsigned long)sequencer());
		fname=tmpfname;
	}

	if ((strlen(fname) == 12) &&
	    (strspn(fname,"0123456789abcdefABCDEF") == 8) &&
	    (strcasecmp(fname+8,".req") == 0)) {
		Syslog('s', "received wazoo freq file");
		isfreq = TRUE;
	} else 
		isfreq = FALSE;

	/*
	 * First check if the file is already in the real inbound directory.
	 * If it's there, resoffs will be set equal to remsize to signal the
	 * receiving protocol to skip the file.
	 */
	if (infpath)
		free(infpath);
	infpath = xstrcpy(inbound);
	infpath = xstrcat(infpath, (char *)"/");
	infpath = xstrcat(infpath, fname);
	if (stat(infpath, &st) == 0) {
#ifdef DEBUGO
		Syslog('s', "remtine=%ld, st_time=%ld, remsize=%ld, st_size=%ld", remtime, st.st_mtime, remsize, st.st_size);
#endif

		if ((remtime == st.st_mtime) && (remsize == st.st_size)) {
			Syslog('+', "File %s is already here", fname);
			*resofs = st.st_size;
			free(infpath);
			infpath = NULL;
			return NULL;
		} 
	}

	/*
	 * If the file is not already in the inbound, but there is a file
	 * with the same name, the new file will be renamed.
	 *
	 * Renaming algorythm is as follows: start with the present name,
	 * increase the last character of the file name, jumping from 
	 * '9' to 'a', from 'z' to 'A', from 'Z' to '0'. If _all_ these 
	 * names are occupied, 
	 */
	if (infpath)
		free(infpath);
	infpath = xstrcpy(inbound);
	infpath = xstrcat(infpath, (char *)"/tmp/");
	infpath = xstrcat(infpath, fname);

	p = infpath + strlen(infpath) -1;
	x = *p;
	ncount = 0;
	while (((rc = stat(infpath, &st)) == 0) &&
	       (remtime != st.st_mtime) &&
	       (ncount++ < 62)) {
		if (x == '9') 
			x = 'a';
		else if (x == 'z') 
			x = 'A';
		else if (x == 'Z') 
			x = '0';
		else 
			x++;
		*p = x;
	}

	if (isfreq || (ncount >= 62)) { /* names exhausted */
		rc = 1;
		p = strrchr(infpath,'/');
		*p = '\0';
		p = tempnam(infpath,",");
		free(infpath);
		infpath = xstrcpy(p); /* tempnam() returns pointer to a static
			 		 buffer, right? */
	}

	*resofs = 0L;
	opentype = (char *)"w";
	if ((rc == 0) && (remsize != 0)) {
		Syslog('+', "Resyncing at offset %lu of \"%s\"",
			(unsigned long)st.st_size, infpath);
		if (resync(st.st_size) == 0) {
			opentype = (char *)"a";
			*resofs = st.st_size;
		}
	}

#ifdef DEBUGO
	Syslog('s', "try fopen(\"%s\",\"%s\")",infpath,opentype);
#endif

	/*
	 * If first attempt doesn't succeed, create tmp directory
	 * and try again.
	 */
	if ((infp = fopen(infpath,opentype)) == NULL) {
		mkdirs(infpath);
		if ((infp = fopen(infpath, opentype)) == NULL) {
			WriteError("$Cannot open local file \"%s\" for \"%s\"",
				infpath,opentype);
			free(infpath);
			infpath=NULL;
			return NULL;
		}
	}

	fl.l_pid = getpid();
	if (fcntl(fileno(infp),F_SETLK,&fl) != 0) {
		Syslog('+', "$cannot lock local file \"%s\"",infpath);
		fclose(infp);
		infp = NULL;
		free(infpath);
		infpath = NULL;
		return NULL;
	}
	intime=remtime;

	if (isfreq) {
		if (freqname) 
			free(freqname);
		freqname = xstrcpy(infpath);
	}

#ifdef DEBUGO
	Syslog('s', "opened file \"%s\" for \"%s\", restart at %lu",
		infpath,opentype,(unsigned long)*resofs);
#endif

	return infp;
}



/*
 *  close file and if (success) { move it to the final location }
 */
int closefile(int success)
{
	char *newpath,*p;
	int rc=0,ncount;
	char x;
	struct stat st;
	struct utimbuf ut;

#ifdef DEBUGO
	Syslog('s', "closefile(%d), for file \"%s\"",success,S(infpath));
#endif

	if ((infp == NULL) || (infpath == NULL)) {
		WriteError("Internal error: try close unopened file!");
		return 1;
	}

	rc = fclose(infp);
	infp = NULL;

	if (rc == 0) {
		ut.actime = intime;
		ut.modtime = intime;
		if ((rc = utime(infpath,&ut)))
			WriteError("$utime failed");
	}

	if (isfreq) {
		if ((rc != 0) || (!success)) {
			Syslog('+', "Removing unsuccessfuly received wazoo freq");
			unlink(freqname);
			free(freqname);
			freqname=NULL;
		}
		isfreq = FALSE;
	} else if ((rc == 0) && success) {
		newpath = xstrcpy(inbound);
		newpath = xstrcat(newpath, strrchr(infpath,'/'));
		
		p = newpath + strlen(newpath) - 1;
		x = *p;
		ncount = 0;
		while (((rc = stat(newpath, &st)) == 0) &&
		       (ncount++ < 62)) {
			if (x == '9') 
				x = 'a';
			else if (x == 'z') 
				x = 'A';
			else if (x == 'Z') 
				x = '0';
			else 
				x++;
			*p = x;
		}
		if (ncount >= 62) { /* names exhausted */
			rc = 1;
			p = strrchr(newpath,'/');
			*p = '\0';
			p = tempnam(newpath,",");
			free(newpath);
			newpath = xstrcpy(p); /* tempnam() returns pointer to 
						 a static buffer, right? */
		}

#ifdef DEBUGO
		Syslog('s', "moving \"%s\" -> \"%s\"",S(infpath),S(newpath));
#endif
		rc = rename(infpath, newpath);
		if (rc) 
			WriteError("$error renaming \"%s\" -> \"%s\"",
				S(infpath),S(newpath));
		else
			gotfiles = TRUE;
		free(newpath);
	}
	free(infpath);
	infpath = NULL;
	return rc;
}


