/* Distributed Checksum Clearinghouse
 *
 * connect to a DCC server for an administrative program
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.24 $Revision$
 */

#include "dcc_clnt.h"
#include "dcc_heap_debug.h"
#include "dcc_paths.h"


static DCC_CLNT_CTXT *ctxt_free;
static int ctxts_active;


const char *
dcc_srvr_nm(void)
{
	int inx;
	DCC_SRVR_ADDR *cur_addr;

	inx = dcc_clnt_info->dcc.act_inx;
	if (inx >= dcc_clnt_info->dcc.num_addrs)
		return "server";
	cur_addr = &dcc_clnt_info->dcc.addrs[inx];
	return dcc_clnt_info->dcc.nms[cur_addr->nm_inx].hostname;
}



/* Create a DCC client context
 *	The contexts must be locked */
DCC_CLNT_CTXT *				/* 0=failed */
dcc_alloc_ctxt(void)
{
	DCC_CLNT_CTXT *ctxt;

	ctxt = ctxt_free;
	if (ctxt) {
		ctxt_free = ctxt->fwd;
	} else {
		ctxt = dcc_malloc(sizeof(*ctxt));
	}
	memset(ctxt, 0, sizeof(*ctxt));
	ctxt->soc = INVALID_SOCKET;
	++ctxts_active;

	return ctxt;
}



/* the contexts must be locked */
u_char					/* 0=file problems, 1=ok */
dcc_rel_ctxt(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt)
{
	u_char result = 1;

	if (ctxt->soc != INVALID_SOCKET
	    && SOCKET_ERROR == closesocket(ctxt->soc)) {
		dcc_pemsg(EX_OSERR, emsg, "close(ctxt): %s", ERROR_STR());
		result = 0;
	}

	ctxt->fwd = ctxt_free;
	ctxt_free = ctxt;

	if (--ctxts_active < 0)
		abort();

	return result;
}



/* create a temporary an anonymous map file
 *	The contexts must be locked.
 *	On failure, everything is undone.
 *	On success, the file is initialized and mapped. */
u_char					/* 0=failed; 1=mapped & locked */
dcc_map_lock_tmp_info(DCC_EMSG emsg, const DCC_SRVR_NM *nm, u_char flags)
{
	DCC_PATH tmp_info_nm;
	int fd;

	fd = dcc_mkstemp(emsg, tmp_info_nm, sizeof(tmp_info_nm),
			 _PATH_TMP, "map", 1, 0, 0);
	if (fd < 0)
		return 0;

	if (!dcc_create_map(emsg, tmp_info_nm, &fd, nm, 1, nm, 1, flags))
		return 0;

	return dcc_map_lock_info(emsg, tmp_info_nm, fd);
}



DCC_CLNT_CTXT *				/* 0 failed */
dcc_clnt_init(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt,
	      const char *new_info_map_nm,
	      u_char flags)		/* DCC_CLNT_FG_* */
{
	u_char mapped;

	if (emsg)
		*emsg = '\0';
	dcc_ctxts_lock();

	if (!ctxt) {
		ctxt = dcc_alloc_ctxt();
		if (!ctxt) {
			dcc_ctxts_unlock();
			return 0;
		}
	}

	mapped = dcc_map_lock_info(emsg, new_info_map_nm, -1);
	if (!mapped) {
		dcc_rel_ctxt(0, ctxt);
		dcc_ctxts_unlock();
		return 0;
	}

	if (!dcc_clnt_rdy(emsg, ctxt, flags)) {
		if (flags & DCC_CLNT_FG_NO_SRVR_OK) {
			if (emsg && dcc_clnt_debug)
				dcc_trace_msg("%s", emsg);
		} else {
			if (mapped > 1)
				dcc_unmap_info(0);
			dcc_rel_ctxt(0, ctxt);
			dcc_ctxts_unlock();
			return 0;
		}
	}

	if (!dcc_info_unlock(emsg)) {
		if (mapped > 1)
			dcc_unmap_info(0);
		dcc_rel_ctxt(0, ctxt);
		dcc_ctxts_unlock();
		return 0;
	}

	dcc_ctxts_unlock();
	return ctxt;
}



/* start talking to a DCC server using an temporary parameter file */
DCC_CLNT_CTXT *				/* 0=failed, 1=ready */
dcc_tmp_clnt_init(DCC_EMSG emsg, DCC_CLNT_CTXT *ctxt,
		  const DCC_SRVR_NM *new_srvr,
		  u_char grey, u_char lock_flags)
{
	if (emsg)
		*emsg = '\0';

	dcc_ctxts_lock();
	if (!dcc_map_lock_tmp_info(emsg, new_srvr, lock_flags)) {
		if (ctxt)
			dcc_rel_ctxt(0, ctxt);
		dcc_ctxts_unlock();
		return 0;
	}
	dcc_ctxts_unlock();

	ctxt = dcc_clnt_init(emsg, ctxt, 0,
			     (grey ? DCC_CLNT_FG_GREY : 0)
			     | DCC_CLNT_FG_NO_FAIL);
	if (!ctxt) {
		dcc_unmap_info(0);
		return 0;
	}

	return ctxt;
}
