#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <libubus.h>
#include <libubox/blobmsg_json.h>
#include <sys/timerfd.h>
#include <assert.h>

#include <libpicoevent.h>
#include "libvoice.h"
#include "ubus.h"
#include "line.h"
#include "line-dect.h"
#include "main.h"

extern int log_level;

enum {
	STATUS_LINE,
};

enum {
	COUNT_EFFECTIVE,
};

enum {
	SIGNAL_LINE,
	SIGNAL_TYPE,
	SIGNAL_STATE,
	SIGNAL_DATA,
	__SIGNAL_MAX,
};

enum {
	CONNECTION_LINE,
	CONNECTION_ID,
	CONNECTION_ACTION,
	CONNECTION_DATA_CODEC,
	CONNECTION_DATA_PTIME,
	CONNECTION_DATA_DTMF_MODE,
	CONNECTION_DATA_DTMF_PT,
	__CONNECTION_MAX,
};

enum {
	RTP_STATS_LINE,
	RTP_STATS_RESET,
	__RTP_STATS_MAX,
};

enum {								// Line config enums
	LINE_NAME,						// Line name as in GUI
	LINE_NOISE,						// Comfort nose
	LINE_ECHO,						// Echo cancelation
	LINE_ID,						// Iopsys ID of line
	LINE_SILENCE,					// Silence suppression
	LINE_TXGAIN,
	LINE_RXGAIN,
};

enum {
	UCI_REPLY_STRING,				// UBUS reply consisting of one single string
	UCI_REPLY_TABLE,				// UBUS reply consisting of a subtable
};

// New ubus objects in global context
enum {
	OBJ_ID,							// UBUS ID of object
	OBJ_PATH,						// String path of object
};

enum {
	DECT_TERM,						// Terminal ID
	DECT_ADD,						// Add call using PCMx
	DECT_REL,						// Release call using PCMx
	DECT_CID,						// Caller ID
	DECT_CALLER_NAME,				// Caller Name
	DECT_SIP_CLIENT_ID,
	DECT_PCM_ID,					// PCM ID
};

// Keeping track of sent but not yet answered UBUS requests, for timeout.
struct outstandingReq_t {
	const char *dest;				// UBUS path destination of the request.
	int timerFd;					// timerfd
	pe_stream_t *timerStream;		// libpicoevent stream for timerfd
	struct ubus_request *ubusReq;	// The ubus request that has timed out
};

typedef void (*ubus_query_cb)(struct ubus_request *req, int type, struct blob_attr *msg);

static int ubus_request_count(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
	struct ubus_request_data *req, const char *method __attribute__((unused)),
	struct blob_attr *msg);
static int ubus_request_signal(struct ubus_context *uctx __attribute__((unused)),
	struct ubus_object *obj __attribute__((unused)),
	struct ubus_request_data *req __attribute__((unused)),
	const char *method __attribute__((unused)),
	struct blob_attr *msg);
static int ubus_request_connection(struct ubus_context *uctx __attribute__((unused)),
	struct ubus_object *obj __attribute__((unused)),
	struct ubus_request_data *req __attribute__((unused)), const char *method __attribute__((unused)),
	struct blob_attr *msg);
static int ubus_request_status(struct ubus_context *uctx __attribute__((unused)),
	struct ubus_object *obj __attribute__((unused)),  struct ubus_request_data *req,
	const char *method __attribute__((unused)), struct blob_attr *msg);
static void got_dectmngr_complete(struct ubus_request *req, int ret __attribute__((unused)));
static void got_asterisk_complete(struct ubus_request *req, int ret __attribute__((unused)));
static int ubus_request_call(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
	struct ubus_request_data *req, const char *method __attribute__((unused)),
	struct blob_attr *msg);
static int ubus_request_rtp_stats(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
	struct ubus_request_data *req, const char *method __attribute__((unused)),
	struct blob_attr *msg);
static int ubus_request_codec_capability(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
	struct ubus_request_data *req, const char *method __attribute__((unused)),
	struct blob_attr *msg __attribute__((unused)));


const char uciStrConfig[] = "asterisk";							// UCI package name

static struct ubus_context *ctx;
static uint32_t asterisk_id;
static uint32_t dectmngr_id, dectmngr_rpc_id;
static pe_stream_t *ubus_stream;
static pe_list_t *ubus_pending_requests;						// List of async requests we have sent, waiting for answer
static void *ubus_async_req;									// Current async request in investigation
static int isReceiveing;										// True when ubus receiving is enabled
static struct ubus_event_handler ObjAddListener;				// Event handler for new ubus object events
static struct ubus_event_handler ObjRmListener;					// Event handler for removed ubus object events
static const char asterisk_path[] = "asterisk";					// UBUS path name to reach Asterisk
static const char dectmngr_path[] = "dect";						// UBUS path name to reach dectmngr
static const char broadcast_path[] = "voice.endpoint";			// UBUS path name for broadcasting events
static const char uciStrComSect[] = "tel_options";				// Common endpoint section name
static const char uciStrLineSect[] = "extension";				// Line specific section name
static const char uciStrCountry[] = "country";					// Endpoint country
static const char uciStrLoglevel[] = "vmloglevel";			// syslog level of voicemngr
static const char ubusStrObjAdd[] = "ubus.object.add";			// UBUS objects added to global context
static const char ubusStrObjRm[] = "ubus.object.remove";		// UBUS objects added removed from global context

static const struct blobmsg_policy request_connection_policy[] = {
	[CONNECTION_LINE] = { .name = "line", .type = BLOBMSG_TYPE_INT32 },
	[CONNECTION_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },			// rtp stream connection identifier (unique number generated by asterisk)
	[CONNECTION_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_STRING },
	[CONNECTION_DATA_CODEC] = { .name = "codec", .type = BLOBMSG_TYPE_INT32 },
	[CONNECTION_DATA_PTIME] =  { .name = "ptime", .type = BLOBMSG_TYPE_INT32 },
	[CONNECTION_DATA_DTMF_MODE] =  { .name = "dtmf_mode", .type = BLOBMSG_TYPE_INT32 },
	[CONNECTION_DATA_DTMF_PT] =  { .name = "dtmf_pt", .type = BLOBMSG_TYPE_INT32 },
};

static const struct blobmsg_policy request_count_policy[] = {
	[COUNT_EFFECTIVE] = { .name = "effective", .type = BLOBMSG_TYPE_BOOL },
};

static const struct blobmsg_policy request_signal_policy[] = {
	[SIGNAL_LINE] = { .name = "line", .type = BLOBMSG_TYPE_INT32 },
	[SIGNAL_TYPE] = { .name = "signal", .type = BLOBMSG_TYPE_STRING },
	[SIGNAL_STATE] = { .name = "state", .type = BLOBMSG_TYPE_STRING },
	[SIGNAL_DATA] = { .name = "data", .type = BLOBMSG_TYPE_STRING },
};

static const struct blobmsg_policy request_status_policy[] = {
	[STATUS_LINE] = { .name = "line", .type = BLOBMSG_TYPE_INT32 },
};

static const struct blobmsg_policy request_rtp_stats_policy[] = {
	[RTP_STATS_LINE] = { .name = "line", .type = BLOBMSG_TYPE_INT32 },
	[RTP_STATS_RESET] = { .name = "reset", .type = BLOBMSG_TYPE_BOOL },
};

static const struct blobmsg_policy new_obj_policy[] = {
	[OBJ_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },			// UBUS ID of object
	[OBJ_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },		// String path of object
};

// dectmngr ubus "RPC call" arguments
static const struct blobmsg_policy request_call_policy[] = {
	[DECT_TERM] = { .name = "terminal", .type = BLOBMSG_TYPE_INT32 },
	[DECT_ADD] = { .name = "add", .type = BLOBMSG_TYPE_INT32 },
	[DECT_REL] = { .name = "release", .type = BLOBMSG_TYPE_INT32 },
	[DECT_CID] = { .name = "cid", .type = BLOBMSG_TYPE_STRING },
	[DECT_CALLER_NAME] = { .name = "caller_name", .type = BLOBMSG_TYPE_STRING },
	[DECT_SIP_CLIENT_ID] =  { .name = "sip_client_id",      .type = BLOBMSG_TYPE_INT32 },
	[DECT_PCM_ID] = { .name = "pcm_id", .type = BLOBMSG_TYPE_INT32 },
};

// Our ubus RPC methods
static struct ubus_method rpc_methods[] = {
	UBUS_METHOD("count", ubus_request_count, request_count_policy),
	UBUS_METHOD("signal", ubus_request_signal, request_signal_policy),
	UBUS_METHOD("connection", ubus_request_connection, request_connection_policy),
	UBUS_METHOD("status", ubus_request_status, request_status_policy),
	UBUS_METHOD("call", ubus_request_call, request_call_policy),
	UBUS_METHOD("rtp_stats", ubus_request_rtp_stats, request_rtp_stats_policy),
	UBUS_METHOD_NOARG("codecs", ubus_request_codec_capability),
};

static struct ubus_object_type rpc_obj_type =
	UBUS_OBJECT_TYPE("endpt", rpc_methods);


struct ubus_object rpcObj = {
	.name = "endpt",
	.type = &rpc_obj_type,
	.methods = rpc_methods,
	.n_methods = ARRAY_SIZE(rpc_methods),
};

// Process ubus events by ubus stack.
void ubus_fd_handler(pe_stream_t *stream __attribute__((unused)), pe_event_t *event __attribute__((unused))) {
	ubus_handle_event(ctx);
}

// libpicoevent let's us iterate though a list with outstanding requests waiting for an answer. In this list; find the
// ubus request object of interest and ignore the rest.
static int outstReqStreamIter(void *arg) {
	struct outstandingReq_t *outReq = (struct outstandingReq_t*)arg;
	return (outReq->timerStream && (void*)outReq->timerStream == ubus_async_req);
}

// Timout handler for UBUS messages sent to dectmngr.
static void timerHandler(pe_stream_t *stream, pe_event_t *event __attribute__((unused))) {
	struct outstandingReq_t *outReq;
	uint64_t expired = 0;
	int res;

	// Find the timer container of the ubus request (but don't delete it).
	assert(!ubus_async_req);
	ubus_async_req = stream;
	outReq = pe_list_find(ubus_pending_requests, outstReqStreamIter);
	ubus_async_req = NULL;
	if (!outReq) {
		ENDPT_DBG("The pending UBUS request is not found\n");
		return;
	}

	res = read(outReq->timerFd, &expired, sizeof(expired));
	if (!expired || (res == -1 && errno != EAGAIN)) {
		 ENDPT_DBG("Not expired or failed to read timer fd: %s", strerror(errno));
	}

	ENDPT_DBG("Timeout ubus call to %s\n", outReq->dest);
	outReq->ubusReq->status_code = UBUS_STATUS_TIMEOUT;
	ubus_abort_request(ctx, outReq->ubusReq);
	if (!strcmp(outReq->dest, dectmngr_path) || !strcmp(outReq->dest, DECTMNGR_RPC_UBUS_OBJECT)) {
		got_dectmngr_complete(outReq->ubusReq, UBUS_STATUS_TIMEOUT);
	} else if (!strcmp(outReq->dest, asterisk_path)) {
		got_asterisk_complete(outReq->ubusReq, UBUS_STATUS_TIMEOUT);
	}
}

//-------------------------------------------------------------
// Add a timer to a ubus request we have sent. To
// be able to discover timeout if no answer.
static int ubus_queue_request_with_timer(struct ubus_request *req) {
	struct outstandingReq_t *outReq;
	struct itimerspec newTimer;

	outReq = calloc(1, sizeof(struct outstandingReq_t));
	memset(&newTimer, 0, sizeof(newTimer));

	// Use timerfd since it will work with poll() in libpicoevent.
	outReq->timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
	if(outReq->timerFd == -1) {
		ENDPT_ERR("Failed to create ubus timer, %s\n", strerror(errno));
		free(outReq);
		return -1;
	}

	/* Save the ubus destination as a non-volatile reference due
	 * to the ordinariy reference IS volatile. It can change after
	 * we has sent the request, which otherwise would make the
	 * timeout handler confused. */
	if (req->peer == dectmngr_id) {
		outReq->dest = dectmngr_path;
	} else if(req->peer == dectmngr_rpc_id) {
		outReq->dest = DECTMNGR_RPC_UBUS_OBJECT;
	} else if(req->peer == asterisk_id) {
		outReq->dest = asterisk_path;
	} else {
		ENDPT_ERR("%s(): req->peer is invalid, %u\n", __func__, req->peer);
		free(outReq);
		return -1;
	}
	newTimer.it_value.tv_sec = UBUS_REQUEST_TIMEOUT;

	if(timerfd_settime(outReq->timerFd, 0, &newTimer, NULL) == -1) {
		ENDPT_ERR("Failed to set ubus timer, %s\n", strerror(errno));
		free(outReq);
		return -1;
	}

	outReq->ubusReq = req;
	outReq->timerStream = pe_stream_new(outReq->timerFd);
	pe_stream_add_handler(outReq->timerStream, 0, timerHandler);
	pe_base_add_stream(picoBase, outReq->timerStream);
	pe_list_add(ubus_pending_requests, outReq);

	ENDPT_DBG("Add a timer to ubus call toward %s\n", outReq->dest);
	return 0;
}

// libpicoevent let us iterate though a list with outstanding requests waiting for an answer. In this list; find the
// ubus request object of interest and ignore the rest.
static int outstReqUbusIter(void *arg) {
	struct outstandingReq_t *outReq = (struct outstandingReq_t*)arg;
	return (outReq->ubusReq && (void*)outReq->ubusReq == ubus_async_req);
}

//-------------------------------------------------------------
// After receiving an answer for a waiting ubus reqest;
// delete the timeout timer associated with the request.
static int delReqTimer(struct ubus_request *req) {
	struct outstandingReq_t *outReq;

	if(picoBase < 0) return -1;

	// Find the timer container of the ubus request.
	assert(!ubus_async_req);
	ubus_async_req = req;
	outReq = pe_list_find(ubus_pending_requests, outstReqUbusIter);
	ubus_async_req = NULL;
	if(!outReq)
		return -1;
	pe_list_delete(ubus_pending_requests, outReq);

	// Free timer
	pe_base_delete_stream(picoBase, outReq->timerStream);
	pe_stream_remove_handler(outReq->timerStream, timerHandler);
	close(outReq->timerFd);
	outReq->timerFd = -1;
	outReq->timerStream = NULL;		// Invalidate data to find any use-after-free.
	outReq->ubusReq = NULL;
	free(outReq);

	return 0;
}

static void uci_query_lineX(struct uci_context *context, struct uci_section *section, int phoneLine)
{
	int32_t intVal;
	const char *strVal, *option;
	char *error;

	if(phoneLine < 0 || phoneLine >= terminal_info.num_voice_ports) {
		ENDPT_ERR("Error, invalid phone line %d\n", phoneLine);
		return;
	}

	option = "name";
	strVal = uci_lookup_option_string(context, section, option);
	if (!strVal || !*strVal) {
		uci_get_errorstr(context, &error, "");
		ENDPT_ERR("Failed to look up line option %s, %s\n", option, error);
		free(error);
	} else {
		ENDPT_DBG("  %s=%s\n", option, strVal);
		lines[phoneLine].line_conf.name = calloc(1, strlen(strVal) + 1);
		strcpy(lines[phoneLine].line_conf.name, strVal);
	}

	option = "comfort_noise";
	strVal = uci_lookup_option_string(context, section, option);
	if (!strVal || !*strVal) {
		uci_get_errorstr(context, &error, "");
		ENDPT_ERR("Failed to look up line option %s, %s\n", option, error);
		free(error);
	} else {
		intVal = strtol(strVal, NULL, 10);
		if(intVal >= 0 && intVal <= 1) {
			lines[phoneLine].line_conf.comfort_noise = (intVal ? 1 : 0);
			lines[phoneLine].line_conf.silence = (intVal ? 1 : 0);
			ENDPT_DBG("  %s=%d\n", option, intVal);
		} else {
			ENDPT_ERR("Error comfort_noise value: '%s', not in range '0' - '1'\n", strVal);
		}
	}

	option = "echo_cancel";
	strVal = uci_lookup_option_string(context, section, option);
	if (!strVal || !*strVal) {
		uci_get_errorstr(context, &error, "");
		ENDPT_ERR("Failed to look up line option %s, %s\n", option, error);
		free(error);
	} else {
		intVal = strtol(strVal, NULL, 10);
		if(intVal >= 0 && intVal <= 1) {
			lines[phoneLine].line_conf.echo_cancel = (intVal ? 1 : 0);
			ENDPT_DBG("  %s=%d\n", option, intVal);
		} else {
			ENDPT_ERR("Error echo_cancel value: '%s', not in range '0' - '1'\n", strVal);
		}
	}

	option = "txgain";
	strVal = uci_lookup_option_string(context, section, option);
	if (!strVal || !*strVal) {
		uci_get_errorstr(context, &error, "");
		ENDPT_ERR("Failed to look up line option %s, %s\n", option, error);
		free(error);
	} else {
		intVal = strtol(strVal, NULL, 10);
		if(intVal >= voice_get_min_tx_gain() && intVal <= voice_get_max_tx_gain()) {
			lines[phoneLine].line_conf.txgain = intVal;
			ENDPT_DBG("  %s=%d\n", option, intVal);
		} else {
			ENDPT_ERR("Error txgain value: '%s', not in range %d - %d\n", strVal, voice_get_min_tx_gain(), voice_get_max_tx_gain());
		}
	}

	option = "rxgain";
	strVal = uci_lookup_option_string(context, section, option);
	if (!strVal || !*strVal) {
		uci_get_errorstr(context, &error, "");
		ENDPT_ERR("Failed to look up line option %s, %s\n", option, error);
		free(error);
	} else {
		intVal = strtol(strVal, NULL, 10);
		if(intVal >= voice_get_min_rx_gain() && intVal <= voice_get_max_rx_gain()) {
			lines[phoneLine].line_conf.rxgain = intVal;
			ENDPT_DBG("  %s=%d\n", option, intVal);
		} else {
			ENDPT_ERR("Error value: %s='%s', not in range %d - %d\n", option, strVal, voice_get_min_rx_gain(), voice_get_max_rx_gain());
		}
	}

	option = "hookflash";
	strVal = uci_lookup_option_string(context, section, option);
	if (strVal) {
		int min = 0;
		int max = 0;
		char *s = strchr(strVal,':');

		if (s) {
			min = strtol(strVal, NULL, 10);
			max = strtol(s+1, NULL, 10);
		}

		if ( min <= max &&
		     min > 0 && min < 5000 &&
		     max > 0 && max < 5000 ) {
			lines[phoneLine].line_conf.hookflash_min = min;
			lines[phoneLine].line_conf.hookflash_max = max;
		} else {
			ENDPT_DBG("Line %d: error hookflash timer '%s'\n", phoneLine, strVal);
		}

		ENDPT_DBG("Line %d: hookflash timer '%d:%d'\n", phoneLine,
			lines[phoneLine].line_conf.hookflash_min,
			lines[phoneLine].line_conf.hookflash_max);
	}

	lines[phoneLine].line_conf.config_loaded = 1;
}

// Reception of event { "ubus.object.add": {"id":123, "path":"foo"} }
static void ubus_event_new_obj(struct ubus_context *ctx __attribute__((unused)),
		struct ubus_event_handler *ev __attribute__((unused)), const char *type, struct blob_attr *blob)
{
	struct blob_attr *keys[ARRAY_SIZE(new_obj_policy)];
	const char *objPath;
	uint32_t objId;

	// Tokenize message key/value paris into an array
	if(blobmsg_parse(new_obj_policy, ARRAY_SIZE(new_obj_policy),
			keys, blob_data(blob), blob_len(blob))) {
		return;
	}

	if(!type || !keys[OBJ_ID] || !keys[OBJ_PATH])
		return;											// Did we get all arguments we need?

	objId = blobmsg_get_u32(keys[OBJ_ID]);
	objPath = blobmsg_get_string(keys[OBJ_PATH]);

	if(strcmp(type, ubusStrObjAdd) == 0) {				// Object added to global context
		if(strcmp(objPath, asterisk_path) == 0) {
			asterisk_id = objId;						// Asterisk has started up
		}
		else if(strcmp(objPath, dectmngr_path) == 0) {
			dectmngr_id = objId;						// dectmngr has started up
		}
		else if(strcmp(objPath, DECTMNGR_RPC_UBUS_OBJECT) == 0) {
			dectmngr_rpc_id = objId;					// dectmngr RPC has started up
		}
	}
	else if(strcmp(type, ubusStrObjRm) == 0) {			// Object removed from global context
		if(strcmp(objPath, asterisk_path) == 0) {
			asterisk_id = 0;							// Asterisk has closed down
		}
		else if(strcmp(objPath, dectmngr_path) == 0) {
			dectmngr_id = 0;							// dectmngr has closed down
		}
		else if(strcmp(objPath, DECTMNGR_RPC_UBUS_OBJECT) == 0) {
			dectmngr_rpc_id = 0;						// dectmngr RPC has closed down
		}
	}
}

// Reception of ubus call endpt count '{"effective": true}'
static int ubus_request_count(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
			  struct ubus_request_data *req, const char *method __attribute__((unused)),
			  struct blob_attr *msg)
{
	struct blob_attr *keys[ARRAY_SIZE(request_count_policy)];
	int32_t dectEndpoints, totEndpoints;
	struct blob_buf bb;

	if(blobmsg_parse(request_count_policy, ARRAY_SIZE(request_count_policy),
		keys, blob_data(msg), blob_len(msg)) && blob_len(msg)) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	/* Special case handling of possibly unpopulated Dect HW.
	 * Asterisk always needs the raw true kernel values. Everybody
	 * else should satisfy with faked zero number of Dect lines.
	 * This is due to the kernel and libvoip always reports the
	 * same number of lines, no matter if Dect is populated or not. */
	if (!dectmngr_id && (!keys[COUNT_EFFECTIVE] || blobmsg_get_bool(keys[COUNT_EFFECTIVE]))) {
		totEndpoints = terminal_info.num_voice_ports - terminal_info.num_dect;
		dectEndpoints = 0;
	}
	else {
		totEndpoints = terminal_info.num_voice_ports;
		dectEndpoints = terminal_info.num_dect;
	}

	memset(&bb, 0, sizeof(bb));
	if(blob_buf_init(&bb, 0)) return UBUS_STATUS_UNKNOWN_ERROR;
	blobmsg_add_u32(&bb, "num_endpoints", totEndpoints);
	blobmsg_add_u32(&bb, "num_fxo_endpoints", terminal_info.num_fxo);
	blobmsg_add_u32(&bb, "num_fxs_endpoints", terminal_info.num_fxs);
	blobmsg_add_u32(&bb, "num_dect_endpoints", dectEndpoints);
	ubus_send_reply(uctx, req, bb.head);
	blob_buf_free(&bb);

	return UBUS_STATUS_OK;
}

// Reception of ubus call endpt signal '{ "line" : 1 , "signal" : "dial" , "state" : "on" }'
static int ubus_request_signal(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
			  struct ubus_request_data *req, const char *method __attribute__((unused)),
			  struct blob_attr *msg)
{
	struct blob_attr *keys[__SIGNAL_MAX];
	char *signame, *state, *data;
	struct voice_ubus_req_t ubusReq;
	const char *enable;
	int line;

	if(blobmsg_parse(request_signal_policy, __SIGNAL_MAX, keys,
			blob_data(msg), blob_len(msg))) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	/* Check that we have all arguments */
	if (!keys[SIGNAL_LINE] || !keys[SIGNAL_TYPE] || !keys[SIGNAL_STATE]) {
		ENDPT_ERR("Error! missing argument\n");
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	/* Check that endpt exist */
	line = blobmsg_get_u32(keys[SIGNAL_LINE]);
	if (line < 0 || line >= terminal_info.num_voice_ports) {
		ENDPT_ERR("Error! No such line value: %d\n", line);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	signame = blobmsg_get_string(keys[SIGNAL_TYPE]);
	ENDPT_DBG("line=%d, signame=%s\n", line, signame);

	// on or off signal?
	enable = "0";
	data = NULL;
	state = blobmsg_get_string(keys[SIGNAL_STATE]);
	if (strcmp(state, "on") == 0) {
		enable = "1";
	} else if (strcmp(state, "off")) {
		ENDPT_ERR("Error! invalid state %s\n", state);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	// Do we have extra data that should be passed to endpoint?
	if (keys[SIGNAL_DATA])
		data = blobmsg_get_string(keys[SIGNAL_DATA]);

	// Prepare for possibly deferring the request
	memset(&ubusReq, 0, sizeof(struct voice_ubus_req_t));
	clock_gettime(CLOCK_MONOTONIC, &ubusReq.timeStamp);
	ubusReq.ctx = uctx;
	ubusReq.reqIn = req;

	if(line_signal(line, signame, (data ? data : enable), &ubusReq))
		return UBUS_STATUS_INVALID_ARGUMENT;
	else
		return UBUS_STATUS_OK;
}

// Reception of ubus call endpt connection '{ "line" : 1 , "id" : 0, "action" : "create" }'
static int ubus_request_connection(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
			  struct ubus_request_data *req, const char *method __attribute__((unused)),
			  struct blob_attr *msg)
{
	struct blob_attr *keys[__CONNECTION_MAX];
	struct voice_ubus_req_t ubusReq;
	int line, id = -1;
	char *action_str;

	if(blobmsg_parse(request_connection_policy, __CONNECTION_MAX,
			keys, blob_data(msg), blob_len(msg))) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	/* Check that we have all arguments */
	if (!keys[CONNECTION_LINE] || !keys[CONNECTION_ACTION] || !keys[CONNECTION_ID]) {
		ENDPT_ERR("Error! missing argument\n");
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	/* Check that endpt exist */
	line = blobmsg_get_u32(keys[CONNECTION_LINE]);
	if (line < 0 || line >= terminal_info.num_voice_ports) {
		ENDPT_ERR("Error! No such line value: %d\n", line);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	id = blobmsg_get_u32(keys[CONNECTION_ID]);

	// Prepare for possibly deferring the request
	memset(&ubusReq, 0, sizeof(struct voice_ubus_req_t));
	clock_gettime(CLOCK_MONOTONIC, &ubusReq.timeStamp);
	ubusReq.ctx = uctx;
	ubusReq.reqIn = req;

	action_str = blobmsg_get_string(keys[CONNECTION_ACTION]);
	struct config_update_struct data = {
		.mask = 0,
		.codec = VOICE_CODEC_G711A,
		.ptime = 0,
		.dtmf_mode = VOICE_DTMF_RFC_4733,
		.dtmf_pt = 101,
	};

	if (keys[CONNECTION_DATA_CODEC]){
		data.codec = blobmsg_get_u32(keys[CONNECTION_DATA_CODEC]);
		data.mask = data.mask|UBUS_DATA_CODEC_BIT;
	}
	if (keys[CONNECTION_DATA_PTIME]){
		data.ptime = blobmsg_get_u32(keys[CONNECTION_DATA_PTIME]);
		data.mask = data.mask|UBUS_DATA_PTIME_BIT;
	}
	if (keys[CONNECTION_DATA_DTMF_MODE]){
		data.dtmf_mode = blobmsg_get_u32(keys[CONNECTION_DATA_DTMF_MODE]);
		data.mask = data.mask|UBUS_DATA_DTMF_BIT;
	}
	if (keys[CONNECTION_DATA_DTMF_PT]){
		data.dtmf_pt = blobmsg_get_u32(keys[CONNECTION_DATA_DTMF_PT]);
		data.mask = data.mask|UBUS_DATA_DTMF_PT_BIT;
	}

	ENDPT_DBG("line: %d connection_id: %d action: %s\n", line, id, action_str);
	if (strcmp("create", action_str) == 0) {
		if(line_new_connection_by_asterisk(line, id, &ubusReq))
			return UBUS_STATUS_UNKNOWN_ERROR;
	} else if (strcmp("destroy", action_str) == 0) {
		if(line_close_connection_by_asterisk(line, id, &ubusReq))
			return UBUS_STATUS_UNKNOWN_ERROR;
	} else if (strcmp("start_conference", action_str) == 0) {
		if(voice_connection_conference_start(line, id))
			return UBUS_STATUS_UNKNOWN_ERROR;
	} else if (strcmp("stop_conference", action_str) == 0) {
		if(voice_connection_conference_stop(line, id))
			return UBUS_STATUS_UNKNOWN_ERROR;
	} else if (strcmp("release", action_str) == 0) {
		if(line_release_connection_by_asterisk(line, id, &ubusReq))
			return UBUS_STATUS_UNKNOWN_ERROR;
	} else if (strcmp("update", action_str) == 0) {
		if(line_update_connection_by_pbx(line, id))
			return UBUS_STATUS_UNKNOWN_ERROR;
	} else if (strcmp("parm_update", action_str) == 0) {
		ENDPT_DBG("parm_update request for line: %d, id: %d, mask: %d, codec: %d, ptime: %d, dtmf_mode: %d \n", line, id, data.mask, data.codec, data.ptime, data.dtmf_mode);
		if(line_connection_parm_update_by_asterisk(line, id, &data))
			return UBUS_STATUS_UNKNOWN_ERROR;
	} else {
		ENDPT_ERR("Error! No such action: %s\n", action_str);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	return UBUS_STATUS_OK;
}

// Reception of ubus call endpt status '{ "line" : 1 }'
static int ubus_request_status(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
			  struct ubus_request_data *req, const char *method __attribute__((unused)),
			  struct blob_attr *msg)
{
	struct blob_attr *keys[ARRAY_SIZE(request_status_policy)];
	enum ubus_msg_status res = UBUS_STATUS_UNKNOWN_ERROR;
	struct blob_buf blob;
	int line, unpopulatedDectEndpoints, totEndpoints;

	if(blobmsg_parse(request_status_policy, ARRAY_SIZE(request_status_policy),
			keys, blob_data(msg), blob_len(msg)) && blob_len(msg)) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	/* Special case handling of possibly unpopulated Dect HW.
	 * Asterisk always needs the raw true kernel values. Everybody
	 * else should satisfy with faked zero number of Dect lines.
	 * This is due to the kernel and libvoip always reports the
	 * same number of lines, no matter if Dect is populated or not. */
	if (!dectmngr_id) {
		totEndpoints = terminal_info.num_voice_ports - terminal_info.num_dect;
		unpopulatedDectEndpoints = terminal_info.num_dect;
	} else {
		totEndpoints = terminal_info.num_voice_ports;
		unpopulatedDectEndpoints = 0;
	}

	memset(&blob, 0, sizeof(blob));
	if(blob_buf_init(&blob, 0))
		return UBUS_STATUS_UNKNOWN_ERROR;

	if (keys[STATUS_LINE]) {
		/* Check that endpt exist */
		line = blobmsg_get_u32(keys[STATUS_LINE]);
		if (line >= 0 && line < totEndpoints) {
			blobmsg_add_u32(&blob, "line", line);
			blobmsg_add_u32(&blob, "offhook", voice_line_is_offhook(line + unpopulatedDectEndpoints));
			res = UBUS_STATUS_OK;
		} else {
			ENDPT_ERR("Error! No such line value: %d\n", line);
			res = UBUS_STATUS_INVALID_ARGUMENT;
		}
	} else {
		void *list1;
		list1 = blobmsg_open_array(&blob, "lines");

		for(line = 0; line < totEndpoints; line++) {
			void  *tbl;
			tbl = blobmsg_open_table(&blob, NULL);

			blobmsg_add_u32(&blob, "line", line);
			blobmsg_add_u32(&blob, "offhook", voice_line_is_offhook(line + unpopulatedDectEndpoints));

			blobmsg_close_table(&blob, tbl);
		}

		blobmsg_close_table(&blob, list1);
		res = UBUS_STATUS_OK;
	}

	ubus_send_reply(uctx, req, blob.head);
	blob_buf_free(&blob);
	return res;
}

// Reception of ubus call endpt rtp_stats '{ "line" : 1, "reset": true }'
static int ubus_request_rtp_stats(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
			  struct ubus_request_data *req, const char *method __attribute__((unused)),
			  struct blob_attr *msg)
{
	struct blob_attr *keys[__RTP_STATS_MAX];
	int line, reset, conIdx;
	struct blob_buf blob;
	struct rtp_stats_t rtp_stats = {0};

	if(blobmsg_parse(request_rtp_stats_policy, __RTP_STATS_MAX, keys, blob_data(msg), blob_len(msg))) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	/* Check that we have all arguments */
	if (!keys[RTP_STATS_LINE] || !keys[RTP_STATS_RESET]) {
		ENDPT_ERR("Error! missing argument\n");
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	/* Check that endpt exist */
	line = blobmsg_get_u32(keys[RTP_STATS_LINE]);
	if (line < 0 || line >= terminal_info.num_voice_ports) {
		ENDPT_ERR("Error! No such line value: %d\n", line);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	reset = blobmsg_get_bool(keys[RTP_STATS_RESET]);

	// Make sure this line has an active connection
	for(conIdx = 0; conIdx < max_num_connections && connections[conIdx].line != line; conIdx++)
		continue;
	if (conIdx >= max_num_connections) {
		ENDPT_ERR("Error: connection is not found on line %d from maximum %d existing connections\n",
			line, max_num_connections);
		return UBUS_STATUS_UNKNOWN_ERROR;
	}

	memset(&blob, 0, sizeof(blob));
	if(blob_buf_init(&blob, 0))
		return UBUS_STATUS_UNKNOWN_ERROR;

	if (voice_get_rtp_stats(line, connections[conIdx].connection_id, reset, &rtp_stats) != 0)
		return UBUS_STATUS_UNKNOWN_ERROR;

	blobmsg_add_u16(&blob, "lineId", line);

	blobmsg_add_u16(&blob, "localBurstDensity", rtp_stats.local_burst_density);
	blobmsg_add_u16(&blob, "remoteBurstDensity", rtp_stats.remote_burst_density);
	blobmsg_add_u16(&blob, "localBurstDuration", rtp_stats.local_burst_duration);
	blobmsg_add_u16(&blob, "remoteBurstDuration", rtp_stats.remote_burst_duration);
	blobmsg_add_u16(&blob, "localGapDensity", rtp_stats.local_gap_density);
	blobmsg_add_u16(&blob, "remoteGapDensity", rtp_stats.remote_gap_density);
	blobmsg_add_u16(&blob, "localGapDuration", rtp_stats.local_gap_duration);
	blobmsg_add_u16(&blob, "remoteGapDuration", rtp_stats.remote_gap_duration);
	blobmsg_add_u16(&blob, "localJbRate", rtp_stats.local_jb_rate);
	blobmsg_add_u16(&blob, "remoteJbRate", rtp_stats.remote_jb_rate);
	blobmsg_add_u16(&blob, "localJbMax", rtp_stats.local_jb_max);
	blobmsg_add_u16(&blob, "remoteJbMax", rtp_stats.remote_jb_max);
	blobmsg_add_u16(&blob, "localJbNominal", rtp_stats.local_jb_nominal);
	blobmsg_add_u16(&blob, "remoteJbNominal", rtp_stats.remote_jb_nominal);
	blobmsg_add_u16(&blob, "localJbAbsMax", rtp_stats.local_jb_abs_max);
	blobmsg_add_u16(&blob, "remoteJbAbsMax", rtp_stats.remote_jb_abs_max);
	blobmsg_add_u32(&blob, "discarded", rtp_stats.discarded);
	blobmsg_add_u32(&blob, "lost", rtp_stats.lost);
	blobmsg_add_u32(&blob, "rxpkts", rtp_stats.rxpkts);
	blobmsg_add_u32(&blob, "txpkts", rtp_stats.txpkts);
	blobmsg_add_u16(&blob, "jbAvg", rtp_stats.jb_avg);
	blobmsg_add_u32(&blob, "jitter", rtp_stats.jitter);
	blobmsg_add_u16(&blob, "localLossRate", rtp_stats.local_loss_rate);
	blobmsg_add_u16(&blob, "remoteLossRate", rtp_stats.remote_loss_rate);
	blobmsg_add_u32(&blob, "maxJitter", rtp_stats.max_jitter);
	blobmsg_add_u16(&blob, "overruns", rtp_stats.jb_overruns);
	blobmsg_add_u16(&blob, "underruns", rtp_stats.jb_underruns);

	ubus_send_reply(uctx, req, blob.head);
	blob_buf_free(&blob);

	return UBUS_STATUS_OK;
}

// RPC handler for ubus call endpt codecs : Provides list of supported codecs and parameters
static int ubus_request_codec_capability(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
			  struct ubus_request_data *req, const char *method __attribute__((unused)),
			  struct blob_attr *msg __attribute__((unused)))
{
	struct codec_capability codecs_list = {0};
	struct blob_buf blob;
	memset(&blob, 0, sizeof(blob));

	if(blob_buf_init(&blob, 0))
		return UBUS_STATUS_UNKNOWN_ERROR;

	int status = voice_get_codec_capability(&codecs_list);
	if(status == 0)
	{
		for(int count = 0 ; count < codecs_list.num_codecs; count++)
		{
			void *table;
			table = blobmsg_open_table(&blob, codecs_list.codecs[count].uciName);
			blobmsg_add_string(&blob, "name", codecs_list.codecs[count].codec);
			blobmsg_add_u32(&blob, "ptime_min", codecs_list.codecs[count].ptimeMin);
			blobmsg_add_u32(&blob, "ptime_max", codecs_list.codecs[count].ptimeMax);
			blobmsg_add_u32(&blob, "ptime_default", codecs_list.codecs[count].ptimeDefault);
			blobmsg_add_u32(&blob, "ptime_increment", codecs_list.codecs[count].ptimeIncrement);
			blobmsg_add_double(&blob, "bitrate", codecs_list.codecs[count].bitRate);
			if (codecs_list.codecs[count].rtpPayload)
				blobmsg_add_u32(&blob, "rtp_payload", codecs_list.codecs[count].rtpPayload);
			blobmsg_close_table(&blob, table);
		}
	}
	else
	{
		static const char *codecfile = "/lib/voice/codecs.json";

		ENDPT_INFO("Codec capability query failed (%d). Use the information from %s\n", status, codecfile);
		int res = blobmsg_add_json_from_file(&blob, codecfile);
		if(!res)
		{
			ENDPT_ERR("%s is missing or invalid\n", codecfile);
			return UBUS_STATUS_UNKNOWN_ERROR;
		}
	}
	ubus_send_reply(uctx, req, blob.head);
	blob_buf_free(&blob);
	return UBUS_STATUS_OK;
}

// RPC handler for ubus call endpt call '{ "terminal": x, "add": x }'
static int ubus_request_call(struct ubus_context *uctx, struct ubus_object *obj __attribute__((unused)),
		struct ubus_request_data *req, const char *method __attribute__((unused)),
		struct blob_attr *msg)
{
	struct blob_attr *keys[ARRAY_SIZE(request_call_policy)];
	int termId, pcmId, add, release;
	struct voice_ubus_req_t ubusReq;
	const char *cid;

	termId = -1;
	pcmId = -1;
	add = 0;
	release = 0;
	cid = NULL;

	// Tokenize message key/value paris into an array
	if(blobmsg_parse(request_call_policy, ARRAY_SIZE(request_call_policy),
			keys, blob_data(msg), blob_len(msg))) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	if(keys[DECT_TERM]) {
		termId = blobmsg_get_u32(keys[DECT_TERM]);
		ENDPT_DBG("call term %d\n", termId);
	}

	if(keys[DECT_ADD]) {
		add = blobmsg_get_u32(keys[DECT_ADD]);
		ENDPT_DBG("call add %d\n", add);
	}

	if(keys[DECT_REL]) {
		release = blobmsg_get_u32(keys[DECT_REL]);
		ENDPT_DBG("call release %d\n", release);
	}

	if(keys[DECT_CID]) {
		cid = blobmsg_get_string(keys[DECT_CID]);
		ENDPT_DBG("call cid %s\n", cid);
	}

	if(keys[DECT_PCM_ID]) {
		pcmId = blobmsg_get_u32(keys[DECT_PCM_ID]);
		ENDPT_DBG("pcmId %d\n", pcmId);
	}

	if((pcmId & 0xFFFF) < CALL_DEFAULT0 || (pcmId & 0xFFFF) >= CALL_LAST) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	// Prepare for possibly deferring the request
	memset(&ubusReq, 0, sizeof(struct voice_ubus_req_t));
	clock_gettime(CLOCK_MONOTONIC, &ubusReq.timeStamp);
	ubusReq.ctx = uctx;
	ubusReq.reqIn = req;

	// Did we get all arguments we need?
	if(release && termId >= 0) {
		if(line_close_connection_by_dect(dectmngr_line_to_voicemngr_line(termId), pcmId, &ubusReq)) {
			send_reply_dectmngr(&ubusReq, termId, pcmId, UBUS_STATUS_UNKNOWN_ERROR);
			return UBUS_STATUS_UNKNOWN_ERROR;
		}
	} else if(add && termId >= 0) {
		if(line_new_connection_by_dect(dectmngr_line_to_voicemngr_line(termId), cid, pcmId, &ubusReq)) {
			send_reply_dectmngr(&ubusReq, termId, pcmId, UBUS_STATUS_UNKNOWN_ERROR);
			return UBUS_STATUS_UNKNOWN_ERROR;
		}
	} else if (!add && !release && termId >= 1) {
		if (pcmId != CALL_MODE_SINGLE && pcmId != CALL_MODE_MULTIPLE) {
			send_reply_dectmngr(&ubusReq, termId, pcmId, UBUS_STATUS_UNKNOWN_ERROR);
			return UBUS_STATUS_UNKNOWN_ERROR;
		}

		struct line_event_t *ast_msg = malloc(sizeof(struct line_event_t));
		if (ast_msg) {
			ast_msg->name = pcmId == CALL_MODE_MULTIPLE ? "LINE_CALL_MODE_MULTIPLE" : "LINE_CALL_MODE_SINGLE";
			ast_msg->data = 0;
			ast_msg->line = dectmngr_line_to_voicemngr_line(termId);
			send_event_main(ast_msg, EVENT_MAIN_LINE);
		}
		return send_reply_dectmngr(&ubusReq, termId, // +1 Dectmngr lines starting from 1
			pcmId, UBUS_STATUS_OK);
	} else {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	return UBUS_STATUS_OK;
}

// Callback for a ubus call (invocation).
// Asterisk has replied without data.
static void got_asterisk_complete(struct ubus_request *req, int ret __attribute__((unused)))
{
	if(req->peer != asterisk_id || req->status_code != UBUS_STATUS_OK) {
		ENDPT_ERR("Failed sending event to Asterisk\n");
	}

	ubus_cb_asterisk_replied((struct line_event_t*) req->priv, req->status_code);

	delReqTimer(req);
	free(req->priv);
	free(req);

	while(ubus_process_queued_reqs_to_dectmngr());
}

// Send an event to Asterisk by calling a remote RPC
// method. Don't wait for completion. Instead, when
// the invocation finishes, we get "called back".
int ubus_call_asterisk(const struct line_event_t* const ev)
{
	struct ubus_request *req;
	struct blob_buf bb;
	int res;

	ENDPT_DBG("ubus call to asterisk, line:%d, event:%s, data:%d\n", ev->line, ev->name, ev->data);

	// Do we know ubus address of Asterisk?
	if (!asterisk_id) {
		ENDPT_ERR("%s(): Asterisk ubus object id is invalid\n", __func__);
		return -1;
	}

	memset(&bb, 0, sizeof(bb));
	if (blob_buf_init(&bb, 0)) return -1;
	blobmsg_add_string(&bb, "event", ev->name);
	blobmsg_add_u32(&bb, "data", ev->data);
	blobmsg_add_u32(&bb, "line", ev->line);

	req = calloc(1, sizeof(struct ubus_request));
	if (!req) {
		res = -1;
		goto out;
	}

	res = ubus_invoke_async(ctx, asterisk_id, "event", bb.head, req);
	if (res != UBUS_STATUS_OK) {
		ENDPT_ERR("%s(): Error ubus invoking\n", __func__);
		res = -1;
		goto out;
	}

	req->complete_cb = got_asterisk_complete; // Callback when the request finishes
	req->priv = (void*) ev;
	ubus_complete_request_async(ctx, req);
	res = ubus_queue_request_with_timer(req);

out:
	blob_buf_free(&bb);

	return res;
}

// Send an event to Asterisk by calling a remote RPC
// method. Don't wait for completion. Instead, when
// the invocation finishes, we get "called back".
int ubus_broadcast_event(const struct line_event_t* const ev)
{
	struct blob_buf blob;
	int res = 0;

	// Filter out repeated DTMF events
	if (strncasecmp(ev->name, "DTMF", strlen("DTMF")) == 0 && ev->data == 1)
		return 0;

	memset(&blob, 0, sizeof(blob));
	if(blob_buf_init(&blob, 0))
		return -1;

	blobmsg_add_u32(&blob, "id", ev->line);
	blobmsg_add_string(&blob, "event", ev->name);

	if(ubus_send_event(ctx, broadcast_path, blob.head) != UBUS_STATUS_OK) {
		ENDPT_ERR("Error sending ubus message %s\n", ev->name);
		res = -1;
	}

	blob_buf_free(&blob);

	return res;
}

// Reply to a deferred request from Asterisk
int send_reply_asterisk(struct voice_ubus_req_t *req, enum ubus_msg_status ubusRet)
{
	ENDPT_DBG("Sending ubus reply to Asterisk\n");
	ubus_complete_deferred_request(req->ctx, &req->reqOut, ubusRet);

	return 0;
}

// Reply to a deferred request from dectmngr
int send_reply_dectmngr(struct voice_ubus_req_t *req, int terminal, int pcmId, enum ubus_msg_status ubusRet)
{
	struct blob_buf bb;

	memset(&bb, 0, sizeof(bb));
	if(blob_buf_init(&bb, 0))
		return -1;
	if(terminal >= 0)
		blobmsg_add_u32(&bb, request_call_policy[DECT_TERM].name, terminal);
	blobmsg_add_u32(&bb, "pcm", pcmId);
	blobmsg_add_u32(&bb, "errno", ubusRet != UBUS_STATUS_OK);
	ENDPT_DBG("Sending ubus reply to dectmngr term %d pcm %d stat %u\n",
		terminal, pcmId, ubusRet != UBUS_STATUS_OK);

	// Fast replys may have not yet been deferred.
	if(req->reqIn && !req->reqIn->deferred) {
		ubus_defer_request(req->ctx, req->reqIn, &req->reqOut);
	}

	ubus_send_reply(req->ctx, &req->reqOut, bb.head);
	ubus_complete_deferred_request(req->ctx, &req->reqOut, ubusRet);
	blob_buf_free(&bb);

	return 0;
}

// Callback for a ubus call (invocation).
// dectmngr has replied with some data.
static void got_dectmngr_data(struct ubus_request *req, int type, struct blob_attr *msg __attribute__((unused)))
{
	if(type != UBUS_MSG_DATA && type != UBUS_MSG_STATUS) return;

	if(req->priv) {
		struct line_req_t *lineReq = (struct line_req_t*)req->priv;
		ENDPT_DBG("Received ubus answer from dectmngr with data, action: %d, line: %d, pcmId: %d, status_code: %d\n",
			lineReq->action, lineReq->line, lineReq->pcm_id, req->status_code);
		ubus_cb_dectmngr_replied((struct line_req_t*) req->priv, req->status_code);
		req->priv = NULL;
	}

	while(ubus_process_queued_reqs_to_dectmngr());
}

// Callback for a ubus call (invocation).
// dectmngr has replied without data.
static void got_dectmngr_complete(struct ubus_request *req, int ret __attribute__((unused))) {
	got_dectmngr_data(req, UBUS_MSG_STATUS, NULL);							// Reuse handler
	delReqTimer(req);
	free(req);
}

// Send a request to dectmngr by "ubus call dect call". Don't wait for completion. Instead, when the invocation
// finishes, we get "called back".
int ubus_call_dectmngr(int terminal, int add, int release, const char *cid, const char *caller_name, int sip_client_id, int pcmId, struct line_req_t *lineReq)
{
	struct ubus_request *req;
	struct blob_buf bb;
	int res;

	// Do we know ubus address of dectmngr?
	if(!dectmngr_id)
		return -1;

	memset(&bb, 0, sizeof(bb));
	if(blob_buf_init(&bb, 0))
		return -1;

	if(terminal >= 0)
		blobmsg_add_u32(&bb, request_call_policy[DECT_TERM].name, terminal);
	if(add >= 0)
		blobmsg_add_u32(&bb, request_call_policy[DECT_ADD].name, add);
	if(release >= 0)
		blobmsg_add_u32(&bb, request_call_policy[DECT_REL].name, release);
	if(cid && *cid)
		blobmsg_add_string(&bb, request_call_policy[DECT_CID].name, cid);
	if(caller_name && *caller_name)
		blobmsg_add_string(&bb, request_call_policy[DECT_CALLER_NAME].name, caller_name);
	if(sip_client_id >= 0)
		blobmsg_add_u32(&bb, request_call_policy[DECT_SIP_CLIENT_ID].name, sip_client_id);

	blobmsg_add_u32(&bb, request_call_policy[DECT_PCM_ID].name, pcmId);

	ENDPT_DBG("Sending ubus request to dectmngr %d %d %d %s %s %d %d\n", terminal, add, release, cid, caller_name, sip_client_id, pcmId);

	req = calloc(1, sizeof(struct ubus_request));
	if (!req) {
		res = -1;
		goto out;
	}

	res = ubus_invoke_async(ctx, dectmngr_id, "call", bb.head, req);
	if(res != UBUS_STATUS_OK) {
		ENDPT_ERR("Error ubus invoking\n");
		res = -1;
		goto out;
	}

	req->data_cb = got_dectmngr_data;	// Callback when req finish
	req->complete_cb = got_dectmngr_complete;
	req->priv = (void*) lineReq;
	ubus_complete_request_async(ctx, req);
	res = ubus_queue_request_with_timer(req);

out:
	blob_buf_free(&bb);

	return res;
}

// Send a request to dectmngr by "ubus call dect-rpc <method>". Don't wait for completion. Instead, when the invocation
// finishes, we get "called back".
int ubus_dectmngr_rpc(const struct dectmngr_rpc_t *dectmngr_rpc, const struct line_req_t *line_req)
{
	struct ubus_request *req;
	struct blob_buf bb;
	int res = -1;

	// Do we know ubus address of dectmngr?
	if(!dectmngr_rpc_id || !dectmngr_rpc || !line_req)
		return res;

	memset(&bb, 0, sizeof(bb));
	if(blob_buf_init(&bb, 0))
		return res;

	req = calloc(1, sizeof(struct ubus_request));
	if (!req) {
		goto __return;
	}

	switch (dectmngr_rpc->action) {
	case ACTION_RINGING_STOP:
		blobmsg_add_u32(&bb, DECTMNGR_RPC_PARAM_EXTENSION_ID, dectmngr_rpc->extension_id);
		res = ubus_invoke_async(ctx, dectmngr_rpc_id, DECTMNGR_RPC_RINGING_STOP, bb.head, req);
		ENDPT_DBG("Invoke ubus call %s %s {'%s':%d}\n", DECTMNGR_RPC_UBUS_OBJECT, DECTMNGR_RPC_RINGING_STOP,
				DECTMNGR_RPC_PARAM_EXTENSION_ID, dectmngr_rpc->extension_id);
		break;

	case ACTION_CALL_MISSED:
		blobmsg_add_u32(&bb, DECTMNGR_RPC_PARAM_EXTENSION_ID, dectmngr_rpc->extension_id);
		res = ubus_invoke_async(ctx, dectmngr_rpc_id, DECTMNGR_RPC_CALL_MISSED, bb.head, req);
		ENDPT_DBG("Invoke ubus call %s %s {'%s':%d}\n", DECTMNGR_RPC_UBUS_OBJECT, DECTMNGR_RPC_CALL_MISSED,
				DECTMNGR_RPC_PARAM_EXTENSION_ID, dectmngr_rpc->extension_id);
		break;

	case ACTION_CONN_CLOSE:
		blobmsg_add_u32(&bb, DECTMNGR_RPC_PARAM_EXTENSION_ID, dectmngr_rpc->extension_id);
		blobmsg_add_u32(&bb, DECTMNGR_RPC_PARAM_PCM_ID, dectmngr_rpc->pcm_id);
		res = ubus_invoke_async(ctx, dectmngr_rpc_id, DECTMNGR_RPC_CALLS_RELEASE, bb.head, req);
		ENDPT_DBG("Invoke ubus call %s %s {'%s':%d,'%s':%d}\n", DECTMNGR_RPC_UBUS_OBJECT, DECTMNGR_RPC_CALLS_RELEASE,
				DECTMNGR_RPC_PARAM_EXTENSION_ID, dectmngr_rpc->extension_id,
				DECTMNGR_RPC_PARAM_PCM_ID, dectmngr_rpc->pcm_id);
		break;

	default:
		ENDPT_DBG("Unsupported action, %d\n", dectmngr_rpc->action);
		free(req);
		goto __return;
	}

	if (res != UBUS_STATUS_OK) {
		ENDPT_ERR("Error ubus invoking\n");
		res = -1;
		free(req);
		goto __return;
	} else {
		res = 0;
	}

	req->data_cb = got_dectmngr_data; // Callback when req finish
	req->complete_cb = got_dectmngr_complete;
	req->priv = (void *)line_req;
	ubus_complete_request_async(ctx, req);
	res = ubus_queue_request_with_timer(req);
__return:
	blob_buf_free(&bb);
	return res;
}

void config_syslog(struct uci_context *context, struct uci_package *package)
{
	struct uci_section *section = NULL;
	char *error;
	const char *loglevel;

	section = uci_lookup_section(context, package, uciStrComSect);
	if (!section) {
		uci_get_errorstr(context, &error, "");
		ENDPT_ERR("Failed to look up section %s.%s, %s\n", uciStrConfig, uciStrComSect, error);
		free(error);
		return;
	}

	// configure the syslog level for voicemngr
	loglevel = uci_lookup_option_string(context, section, uciStrLoglevel);
	if (loglevel && *loglevel) {
		log_level = atoi(loglevel);
	}
}

// Read settings from UCI for this application.
int config_init(struct uci_context *context, struct uci_package *package)
{
	struct uci_section *section = NULL;
	char *error;
	const char *country;
	char uciSection[sizeof(uciStrLineSect) + 10];
	int line;

	// Configure the country
	section = uci_lookup_section(context, package, uciStrComSect);
	if (!section) {
		uci_get_errorstr(context, &error, "");
		ENDPT_ERR("Failed to look up section %s.%s, %s\n", uciStrConfig, uciStrComSect, error);
		free(error);
		return -1;
	}
	country = uci_lookup_option_string(context, section, uciStrCountry);
	if (!country || !*country) {
		uci_get_errorstr(context, &error, "");
		ENDPT_ERR("Failed to look up option %s.%s.%s, %s\n", uciStrConfig, uciStrComSect, uciStrCountry, error);
		free(error);
		return -1;
	}
	if (voice_set_country(country) != 0) {
		ENDPT_ERR("Failed to set country %s\n", country);
		return -1;
	}

	// Configure the voice lines
	for (line = 0; line < terminal_info.num_voice_ports; line++) {
		snprintf(uciSection, sizeof(uciSection), "%s%d", uciStrLineSect, line);
		section = uci_lookup_section(context, package, uciSection);
		if (!section) {
			uci_get_errorstr(context, &error, "");
			ENDPT_ERR("Failed to look up section %s.%s, %s\n", uciStrConfig, uciSection, error);
			free(error);
			return -1;
		}

		ENDPT_DBG("Got UCI config for %s.%s\n", uciStrConfig, uciSection);
		uci_query_lineX(context, section, line);
	}

	// Free the memory
	uci_unload(context, package);

	return 0;
}

// When we know we are going to be busy for a long time
// and thus can't process received ubus messages - block
// them entirely. Then anyone trying to send us a message
// will fail immediately as opposed to timing out.
int ubus_disable_receive(void) {
	int res;

	if(!ctx) return -1;
	if(!isReceiveing) return -1;
	res = 0;

	if(ubus_remove_object(ctx, &rpcObj) != UBUS_STATUS_OK) {
		ENDPT_ERR("Error deregistering ubus endpt object\n");
		res = -1;
	}

	if(ubus_unregister_event_handler(ctx, &ObjRmListener) != UBUS_STATUS_OK) {
		ENDPT_ERR("Error deregistering ubus event handler %s", ubusStrObjRm);
		res = -1;
	}

	if(ubus_unregister_event_handler(ctx, &ObjAddListener) != UBUS_STATUS_OK) {
		ENDPT_ERR("Error deregistering ubus event handler %s", ubusStrObjAdd);
		res = -1;
	}

	isReceiveing = 0;
	ENDPT_DBG("UBUS receiver has been disabled\n");

	return res;
}

// Start accepting ubus messages (when we know we are
// ready for processing).
int ubus_enable_receive(void) {
	if(!ctx) return -1;
	if(isReceiveing) return 0;
	isReceiveing = 1;

	/* Register event handlers (not calls) for:
	 * { "ubus.object.add": {"id":123, "path":"foo"} } */
	memset(&ObjAddListener, 0, sizeof(ObjAddListener));
	ObjAddListener.cb = ubus_event_new_obj;
	if(ubus_register_event_handler(ctx, &ObjAddListener,
			ubusStrObjAdd) != UBUS_STATUS_OK) {
		ENDPT_ERR("Error registering ubus event handler %s", ubusStrObjAdd);
		return -1;
	}

	memset(&ObjRmListener, 0, sizeof(ObjRmListener));
	ObjRmListener.cb = ubus_event_new_obj;
	if(ubus_register_event_handler(ctx, &ObjRmListener,
			ubusStrObjRm) != UBUS_STATUS_OK) {
		ENDPT_ERR("Error registering ubus event handler %s", ubusStrObjRm);
		return -1;
	}

	// Invoke our RPC handler when ubus calls (not events) arrive
	if (ubus_add_object(ctx, &rpcObj) != UBUS_STATUS_OK) {
		ENDPT_ERR("Failed to register UBUS endpt object\n");
		return -1;
	}

	/* Lookup path to Asterisk AFTER registration of ubus object
	 * event handler above. It's no error if lookup fails. */
	if(ubus_lookup_id(ctx, asterisk_path, &asterisk_id) != UBUS_STATUS_OK) {
		asterisk_id = 0;
	}

	if(ubus_lookup_id(ctx, dectmngr_path, &dectmngr_id) != UBUS_STATUS_OK) {
		dectmngr_id = 0;
	}

	if(ubus_lookup_id(ctx, DECTMNGR_RPC_UBUS_OBJECT, &dectmngr_rpc_id) != UBUS_STATUS_OK) {
		dectmngr_rpc_id = 0;
	}
	return 0;
}

int ubus_init(void) {
	ctx = ubus_connect(NULL);
	if (!ctx) {
		exit_failure("Failed to connect to ubus");
	}

	/* Listen for data on ubus socket in our main event loop. */
	ubus_stream = pe_stream_new(ctx->sock.fd);
	pe_stream_add_handler(ubus_stream, 0, ubus_fd_handler);
	pe_base_add_stream(picoBase, ubus_stream);

	ubus_pending_requests = pe_list_new();
	if(!ubus_pending_requests) {
		ENDPT_ERR("%s: out of memory\n", __func__);
		return -1;
	}

	return 0;
}

int ubus_close(void) {
	struct outstandingReq_t *outReq;

	if(picoBase < 0 || !ubus_pending_requests) return -1;

	// Free resources
	do {
		outReq = pe_list_get(ubus_pending_requests);
		if(outReq) {
			outReq->ubusReq->status_code = UBUS_STATUS_TIMEOUT;
			ubus_abort_request(ctx, outReq->ubusReq);
			pe_base_delete_stream(picoBase, outReq->timerStream);
			pe_stream_remove_handler(outReq->timerStream, timerHandler);
			close(outReq->timerFd);
			free(outReq);
		}
	} while(outReq);

	return 0;
}
