/* Distributed Checksum Clearinghouse
 *
 * ask for and administrative operation
 *
 * 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.19 $Revision$
 */

#include "dcc_clnt.h"


/* ask for an administrative operation */
DCC_OPS					/* DCC_OP_INVALID=failed, else result */
dcc_aop(DCC_EMSG emsg,			/* result if DCC_OP_ERROR or _INVALID */
	DCC_CLNT_CTXT *ctxt, u_char grey, int anum,
	DCC_AOPS aop, u_int32_t val1, u_char val2, u_char val3, u_char val4,
	DCC_ADMN_RESP_VAL *pbuf, int *pbuf_len, DCC_SOCKU *resp_su)
{
	DCC_ADMN_REQ req;
	union {
	    DCC_HDR	hdr;
	    DCC_OK	ok;
	    DCC_ERROR	error;
	    DCC_ADMN_RESP resp;
	    u_char	b[1];
	} resp;
	DCC_EMSG loc_emsg;
	int max_str_len, out_len;

	memset(&req, 0, sizeof(req));
	req.date = htonl(time(0));
	req.aop = aop;
	req.val1 = ntohl(val1);
	req.val2 = val2;
	req.val3 = val3;
	req.val4 = val4;
	memset(&resp, 0, sizeof(resp));
	if (!dcc_clnt_op(loc_emsg, ctxt,
			 (grey ? DCC_CLNT_FG_GREY : 0) | DCC_CLNT_FG_NO_FAIL,
			 &anum, 0,
			 &req.hdr, sizeof(req), DCC_OP_ADMN,
			 &resp.hdr, sizeof(resp), resp_su)) {
		dcc_pemsg(dcc_ex_code, emsg, "%s: %s",
			  dcc_op2str(DCC_OP_ADMN, aop, val1), loc_emsg);
		return DCC_OP_INVALID;
	}

	if (resp.hdr.op ==  DCC_OP_OK) {
		if (pbuf_len && *pbuf_len && pbuf) {
			out_len = snprintf(pbuf->string, *pbuf_len,
					   "%s ok", resp.ok.brand);
			*pbuf_len = min(*pbuf_len, out_len);
		}
		return DCC_OP_OK;
	}
	if (resp.hdr.op == DCC_OP_ERROR) {
		dcc_pemsg(dcc_ex_code, emsg, "%s: %s",
			  dcc_op2str(DCC_OP_ADMN, aop, val1),
			  resp.resp.val.string);
		return DCC_OP_ERROR;
	}
	if (resp.hdr.op == DCC_OP_ADMN
	    && pbuf && pbuf_len && *pbuf_len) {
		/* copy whatever the server said to the caller's buffer
		 * and add a '\0' if it looks like a string */
		max_str_len = (ntohs(resp.hdr.len)
			       - (sizeof(resp.resp)
				  - sizeof(resp.resp.val.string)));
		if (max_str_len == 0
		    || (resp.resp.val.string[0] >= ' '
			&& resp.resp.val.string[0] < 0x7f)) {
			out_len = 1;
		} else {
			out_len = 0;
		}
		if (max_str_len > *pbuf_len-out_len)
			max_str_len = *pbuf_len-out_len;
		if (max_str_len <= 0)
			max_str_len = 0;
		else
			memcpy(pbuf, resp.resp.val.string, max_str_len);
		if (out_len)
			pbuf->string[max_str_len] = '\0';
		*pbuf_len = max_str_len + out_len;
		return DCC_OP_ADMN;
	}

	dcc_pemsg(EX_PROTOCOL, emsg, "%s unexpected response: %s",
		  dcc_op2str(DCC_OP_ADMN, aop, val1),
		  dcc_hdr_op2str(&resp.hdr));
	return DCC_OP_INVALID;
}
