/*
 *	binkleyforce -- unix FTN mailer project
 *	
 *	Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
 *	
 *	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 2 of the License, or
 *	(at your option) any later version.
 *	
 *	$Id: outb_bsy.c,v 1.1.1.1 2001/08/30 13:33:49 kst Exp $
 */

#include "includes.h"
#include "confread.h"
#include "logger.h"
#include "util.h"
#include "outbound.h"

typedef struct bsylist {
	s_faddr addr;
	char *name_4d;
	char *name_domain;
	char *name_amiga;
	struct bsylist *next;
#ifdef BFORCE_USE_CSY
	bool is_csy;
#endif
} s_bsylist;

s_bsylist *bsylist = NULL;

static int out_bsy_convert_csy_to_bsy(s_bsylist *ptrl);

/* ========================================================================= */
/* PRIVATE part                                                              */
/* ========================================================================= */

static int out_bsy_file_check(const char *name, const char *ext)
{
	char lockname[BFORCE_MAX_PATH+1];
	
	ASSERT(name != NULL && ext != NULL);
	
	strnxcpy(lockname, name, sizeof(lockname));
	strnxcat(lockname, ext, sizeof(lockname));
	
	return plock_check(lockname);
}

static int out_bsy_file_create(const char *name, const char *ext)
{
	char lockname[BFORCE_MAX_PATH+1];
	
	ASSERT(name != NULL && ext != NULL);
	
	strnxcpy(lockname, name, sizeof(lockname));
	strnxcat(lockname, ext, sizeof(lockname));
	
	return plock_create(lockname);
}

static int out_bsy_file_link(const char *oldname, const char *oldext,
                             const char *newname, const char *newext)
{
	char old_lockname[BFORCE_MAX_PATH+1];
	char new_lockname[BFORCE_MAX_PATH+1];
	
	ASSERT(oldname != NULL && oldext != NULL);
	ASSERT(newname != NULL && newext != NULL);
	
	strnxcpy(old_lockname, oldname, sizeof(old_lockname));
	strnxcat(old_lockname, oldext, sizeof(old_lockname));
	strnxcpy(new_lockname, newname, sizeof(new_lockname));
	strnxcat(new_lockname, newext, sizeof(new_lockname));
	
	return plock_link(new_lockname, old_lockname);
}

static int out_bsy_file_unlink(const char *name, const char *ext)
{
	char lockname[BFORCE_MAX_PATH+1];
	
	ASSERT(name != NULL && ext != NULL);
	
	strnxcpy(lockname, name, sizeof(lockname));
	strnxcat(lockname, ext, sizeof(lockname));
	
	return plock_remove(lockname);
}

/* ========================================================================= */
/* PUBLIC part                                                               */
/* ========================================================================= */

int out_bsy_check(s_faddr addr)
{
	bool isfailed = FALSE;
	char *name_4d = NULL;
	char *name_domain = NULL;
	char *name_amiga = NULL;

	name_4d     = out_getname_4d(addr);
	name_domain = out_getname_domain(addr);
	name_amiga  = out_getname_amiga(addr);
	
	DEB((D_OUTBOUND, "out_bsy_check: name_4d = \"%s\"", name_4d));
	DEB((D_OUTBOUND, "out_bsy_check: name_domain = \"%s\"", name_domain));
	DEB((D_OUTBOUND, "out_bsy_check: name_amiga = \"%s\"", name_amiga));
	
	/* Check for BSY files in all outbounds */
	if( (name_4d     && out_bsy_file_check(name_4d, ".bsy") == PLOCK_EXIST)
	 || (name_domain && out_bsy_file_check(name_domain, ".bsy") == PLOCK_EXIST)
	 || (name_amiga  && out_bsy_file_check(name_amiga, ".bsy") == PLOCK_EXIST) )
		isfailed = TRUE;
	
#ifdef BFORCE_USE_CSY
	if( !isfailed )
	{
		/* Check for CSY files in all outbounds */
		if( (name_4d     && out_bsy_file_check(name_4d, ".csy") == PLOCK_EXIST)
		 || (name_domain && out_bsy_file_check(name_domain, ".csy") == PLOCK_EXIST)
		 || (name_amiga  && out_bsy_file_check(name_amiga, ".csy") == PLOCK_EXIST) )
			isfailed = TRUE;
	}
#endif
	
	if( name_4d )
		free(name_4d);
	if( name_domain )
		free(name_domain);
	if( name_amiga )
		free(name_amiga);
	
	return isfailed ? -1 : 0;
}

#ifdef BFORCE_USE_CSY
int out_bsy_lock(s_faddr addr, bool csy_locks)
#else
int out_bsy_lock(s_faddr addr)
#endif
{
	char *name_4d = NULL;
	char *name_domain = NULL;
	char *name_amiga = NULL;
	bool locked_4d = FALSE;
	bool locked_domain = FALSE;
	bool locked_amiga = FALSE;
	s_bsylist bsytmp, **bsylst;
	bool isfailed = FALSE;
#ifdef DEBUG
	char abuf[BF_MAXADDRSTR+1];
#endif
#ifdef BFORCE_USE_CSY
	const char *lockext = csy_locks ? ".csy" : ".bsy";
#else
	const char *lockext = ".bsy";
#endif

	DEB((D_OUTBOUND, "out_bsy_lock: lock address %s",
		ftn_addrstr(abuf, addr)));

	/*
	 * Check, may be we've allready locked this address.
	 */
	for( bsylst = &bsylist; *bsylst; bsylst = &(*bsylst)->next )
	{
		if( !ftn_addrcomp((*bsylst)->addr, addr) )
		{
			DEB((D_OUTBOUND, "out_bsy_lock: address is allready locked"));
#ifdef BFORCE_USE_CSY
			if( (*bsylst)->is_csy && !csy_locks )
				return out_bsy_convert_csy_to_bsy(*bsylst);
#else
			return 0;
#endif
		}
	}
	
	name_4d     = out_getname_4d(addr);
	name_domain = out_getname_domain(addr);
	name_amiga  = out_getname_amiga(addr);

	DEB((D_OUTBOUND, "out_bsy_lock: name_4d = \"%s\"", name_4d));
	DEB((D_OUTBOUND, "out_bsy_lock: name_domain = \"%s\"", name_domain));
	DEB((D_OUTBOUND, "out_bsy_lock: name_amiga = \"%s\"", name_amiga));
	
	/* Check for BSY files in all outbounds */
	if( (name_4d     && out_bsy_file_check(name_4d, ".bsy") == PLOCK_EXIST)
	 || (name_domain && out_bsy_file_check(name_domain, ".bsy") == PLOCK_EXIST)
	 || (name_amiga  && out_bsy_file_check(name_amiga, ".bsy") == PLOCK_EXIST) )
		isfailed = TRUE;
	
#ifdef BFORCE_USE_CSY
	if( !isfailed )
	{
		/* Check for CSY files in all outbounds */
		if( (name_4d     && out_bsy_file_check(name_4d, ".csy") == PLOCK_EXIST)
		 || (name_domain && out_bsy_file_check(name_domain, ".csy") == PLOCK_EXIST)
		 || (name_amiga  && out_bsy_file_check(name_amiga, ".csy") == PLOCK_EXIST) )
			isfailed = TRUE;
	}
#endif
	
	DEB((D_OUTBOUND, "out_bsy_lock: lock check result: %s",
		isfailed ? "Locked" : "Not locked"));
	
	/* Create ?SY file in 4D outbound */
	if( !isfailed && name_4d )
	{
		switch( out_bsy_file_create(name_4d, lockext) ) {
		case PLOCK_OK:	locked_4d = TRUE; break;
		case PLOCK_EXIST:	isfailed = TRUE; break;
		case PLOCK_ERROR:	locked_4d = FALSE; break;
		}
	}
	
	/* Create ?SY file in domain outbound */
	if( !isfailed && name_domain )
	{
		switch( out_bsy_file_create(name_domain, lockext) ) {
		case PLOCK_OK:	locked_domain = TRUE; break;
		case PLOCK_EXIST:	isfailed = TRUE; break;
		case PLOCK_ERROR:	locked_domain = FALSE; break;
		}
	}

	/* Create ?SY file in "AmigaDos" outbound */
	if( !isfailed && name_amiga )
	{
		switch( out_bsy_file_create(name_amiga, lockext) ) {
		case PLOCK_OK:	locked_amiga = TRUE; break;
		case PLOCK_EXIST:	isfailed = TRUE; break;
		case PLOCK_ERROR:	locked_amiga = FALSE; break;
		}
	}

#ifdef BFORCE_USE_CSY
	if( !isfailed )
	{
		/* Check for BSY files in all outbounds */
		if( (name_4d     && out_bsy_file_check(name_4d, ".bsy") == PLOCK_EXIST)
		 || (name_domain && out_bsy_file_check(name_domain, ".bsy") == PLOCK_EXIST)
		 || (name_amiga  && out_bsy_file_check(name_amiga, ".bsy") == PLOCK_EXIST) )
			isfailed = TRUE;
	}
#endif
	
	DEB((D_OUTBOUND, "out_bsy_lock: lock create result: %s",
		isfailed ? "Failed" : "Successful"));

	/* Somebody else create BSY before us?! */
	if( isfailed )
	{
		if( locked_4d )
			out_bsy_file_unlink(name_4d, lockext);
		if( locked_domain )
			out_bsy_file_unlink(name_domain, lockext);
		if( locked_amiga )
			out_bsy_file_unlink(name_amiga, lockext);
		
		if( name_4d )
			free(name_4d);
		if( name_domain )
			free(name_domain);
		if( name_amiga )
			free(name_amiga);

		return -1;
	}
	
	/*
	 * Successful locking. Add new entry to the ``bsylist''.
	 */
	
	memset(&bsytmp, '\0', sizeof(s_bsylist));
	
	bsytmp.addr = addr;
	
#ifdef BFORCE_USE_CSY
	bsytmp.is_csy = csy_locks;
#endif
	
	if( locked_4d )
		bsytmp.name_4d = name_4d;
	if( locked_domain )
		bsytmp.name_domain = name_domain;
	if( locked_amiga )
		bsytmp.name_amiga = name_amiga;
	
	for( bsylst = &bsylist; *bsylst; bsylst = &(*bsylst)->next )
	{
		/* EMPTY LOOP */
	}
	
	(*bsylst) = (s_bsylist*)xmalloc(sizeof(s_bsylist));
	**bsylst = bsytmp;
	
	return 0;
}

int out_bsy_unlockall(void)
{
	s_bsylist *ptrl, *next;
#ifdef DEBUG
	char abuf[BFORCE_MAX_ADDRSTR+1];
#endif
	
	for( ptrl = bsylist; ptrl; ptrl = next )
	{
#ifdef BFORCE_USE_CSY
		const char *lockext = ptrl->is_csy ? ".csy" : ".bsy";
#else
		const char *lockext = ".bsy";
#endif
		next = ptrl->next;
		
		DEB((D_OUTBOUND, "out_bsy_unlockall: unlock address %s",
			ftn_addrstr(abuf, ptrl->addr)));
		
		if( ptrl->name_4d )
		{
			out_bsy_file_unlink(ptrl->name_4d, lockext);
			free(ptrl->name_4d);
		}
		
		if( ptrl->name_domain )
		{
			out_bsy_file_unlink(ptrl->name_domain, lockext);
			free(ptrl->name_domain);
		}
		
		if( ptrl->name_amiga )
		{
			out_bsy_file_unlink(ptrl->name_amiga, lockext);
			free(ptrl->name_amiga);
		}
		
		free(ptrl);
	}
	
	bsylist = NULL;
	
	return 0;
}

#ifdef BFORCE_USE_CSY
static int out_bsy_convert_csy_to_bsy(s_bsylist *ptrl)
{
	bool locked_4d = FALSE;
	bool locked_domain = FALSE;
	bool locked_amiga = FALSE;
	bool isfailed = FALSE;
#ifdef DEBUG
	char abuf[BF_MAXADDRSTR+1];
#endif
	
	DEB((D_OUTBOUND, "out_bsy_convert_csy_to_bsy: process address %s",
		ftn_addrstr(abuf, ptrl->addr)));

	if( !ptrl->is_csy )
	{
		DEB((D_OUTBOUND, "out_bsy_convert_csy_to_bsy: skip (no csy locks)"));
		return 0;
	}

#define LINK_CSY_TO_BSY(lockname) \
	out_bsy_file_link(lockname, ".csy", lockname, ".bsy")
	
	/* Create BSY file in 4D outbound */
	if( !isfailed && ptrl->name_4d )
	{
		switch( LINK_CSY_TO_BSY(ptrl->name_4d) ) {
		case PLOCK_OK:	locked_4d = TRUE; break;
		case PLOCK_EXIST:	isfailed = TRUE; break;
		case PLOCK_ERROR:	locked_4d = FALSE; break;
		}
	}
	
	/* Create BSY file in domain outbound */
	if( !isfailed && ptrl->name_domain )
	{
		switch( LINK_CSY_TO_BSY(ptrl->name_domain) ) {
		case PLOCK_OK:	locked_domain = TRUE; break;
		case PLOCK_EXIST:	isfailed = TRUE; break;
		case PLOCK_ERROR:	locked_domain = FALSE; break;
		}
	}

	/* Create BSY file in "AmigaDos" outbound */
	if( !isfailed && ptrl->name_amiga )
	{
		switch( LINK_CSY_TO_BSY(ptrl->name_amiga) ) {
		case PLOCK_OK:	locked_amiga = TRUE; break;
		case PLOCK_EXIST:	isfailed = TRUE; break;
		case PLOCK_ERROR:	locked_amiga = FALSE; break;
		}
	}

	DEB((D_OUTBOUND, "out_csy_to_bsy: lock create result: %s",
		isfailed ? "Failed" : "Successful"));

	/* Somebody else create BSY before us?! */
	if( isfailed )
	{
		if( locked_4d )
			out_bsy_file_unlink(ptrl->name_4d, ".bsy");
		if( locked_domain )
			out_bsy_file_unlink(ptrl->name_domain, ".bsy");
		if( locked_amiga )
			out_bsy_file_unlink(ptrl->name_amiga, ".bsy");
		
		/* Leave ``.csy'' files untouched! */
		
		return 1;
	}

	ptrl->is_csy = FALSE;
	
	return 0;
}
#endif /* BFORCE_USE_CSY */

