Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b

Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/bfcp/attr.c b/src/bfcp/attr.c
new file mode 100644
index 0000000..7607fe0
--- /dev/null
+++ b/src/bfcp/attr.c
@@ -0,0 +1,746 @@
+/**
+ * @file bfcp/attr.c BFCP Attributes
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+enum {
+	BFCP_ATTR_HDR_SIZE = 2,
+};
+
+
+static void destructor(void *arg)
+{
+	struct bfcp_attr *attr = arg;
+
+	switch (attr->type) {
+
+	case BFCP_ERROR_CODE:
+		mem_deref(attr->v.errcode.details);
+		break;
+
+	case BFCP_ERROR_INFO:
+	case BFCP_PART_PROV_INFO:
+	case BFCP_STATUS_INFO:
+	case BFCP_USER_DISP_NAME:
+	case BFCP_USER_URI:
+		mem_deref(attr->v.str);
+		break;
+
+	case BFCP_SUPPORTED_ATTRS:
+		mem_deref(attr->v.supattr.attrv);
+		break;
+
+	case BFCP_SUPPORTED_PRIMS:
+		mem_deref(attr->v.supprim.primv);
+		break;
+
+	default:
+		break;
+	}
+
+	list_flush(&attr->attrl);
+	list_unlink(&attr->le);
+}
+
+
+static int attr_encode(struct mbuf *mb, bool mand, enum bfcp_attrib type,
+		       const void *v)
+{
+	const struct bfcp_reqstatus *reqstatus = v;
+	const struct bfcp_errcode *errcode = v;
+	const struct bfcp_supattr *supattr = v;
+	const struct bfcp_supprim *supprim = v;
+	const enum bfcp_priority *priority = v;
+	const uint16_t *u16 = v;
+	size_t start, len, i;
+	int err;
+
+	start = mb->pos;
+	mb->pos += BFCP_ATTR_HDR_SIZE;
+
+	switch (type) {
+
+	case BFCP_BENEFICIARY_ID:
+	case BFCP_FLOOR_ID:
+	case BFCP_FLOOR_REQUEST_ID:
+	case BFCP_BENEFICIARY_INFO:
+	case BFCP_FLOOR_REQ_INFO:
+	case BFCP_REQUESTED_BY_INFO:
+	case BFCP_FLOOR_REQ_STATUS:
+	case BFCP_OVERALL_REQ_STATUS:
+		err = mbuf_write_u16(mb, htons(*u16));
+		break;
+
+	case BFCP_PRIORITY:
+		err  = mbuf_write_u8(mb, *priority << 5);
+		err |= mbuf_write_u8(mb, 0x00);
+		break;
+
+	case BFCP_REQUEST_STATUS:
+		err  = mbuf_write_u8(mb, reqstatus->status);
+		err |= mbuf_write_u8(mb, reqstatus->qpos);
+		break;
+
+	case BFCP_ERROR_CODE:
+		err = mbuf_write_u8(mb, errcode->code);
+		if (errcode->details && errcode->len)
+			err |= mbuf_write_mem(mb, errcode->details,
+					      errcode->len);
+		break;
+
+	case BFCP_ERROR_INFO:
+	case BFCP_PART_PROV_INFO:
+	case BFCP_STATUS_INFO:
+	case BFCP_USER_DISP_NAME:
+	case BFCP_USER_URI:
+		err = mbuf_write_str(mb, v);
+		break;
+
+	case BFCP_SUPPORTED_ATTRS:
+		for (i=0, err=0; i<supattr->attrc; i++)
+			err |= mbuf_write_u8(mb, supattr->attrv[i] << 1);
+		break;
+
+	case BFCP_SUPPORTED_PRIMS:
+		for (i=0, err=0; i<supprim->primc; i++)
+			err |= mbuf_write_u8(mb, supprim->primv[i]);
+		break;
+
+	default:
+		err = EINVAL;
+		break;
+	}
+
+	/* header */
+	len = mb->pos - start;
+
+	mb->pos = start;
+	err |= mbuf_write_u8(mb, (type<<1) | (mand ? 1 : 0));
+	err |= mbuf_write_u8(mb, len);
+	mb->pos += (len - BFCP_ATTR_HDR_SIZE);
+
+	/* padding */
+	while ((mb->pos - start) & 0x03)
+		err |= mbuf_write_u8(mb, 0x00);
+
+	return err;
+}
+
+
+/**
+ * Encode BFCP Attributes with variable arguments
+ *
+ * @param mb    Mbuf to encode into
+ * @param attrc Number of attributes
+ * @param ap    Variable argument of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_attrs_vencode(struct mbuf *mb, unsigned attrc, va_list *ap)
+{
+	unsigned i;
+
+	if (!mb)
+		return EINVAL;
+
+	for (i=0; i<attrc; i++) {
+
+		int  type     = va_arg(*ap, int);
+		unsigned subc = va_arg(*ap, unsigned);
+		const void *v = va_arg(*ap, const void *);
+		size_t start, len;
+		int err;
+
+		if (!v)
+			continue;
+
+		start = mb->pos;
+
+		if (type == BFCP_ENCODE_HANDLER) {
+
+			const struct bfcp_encode *enc = v;
+
+			if (enc->ench) {
+				err = enc->ench(mb, enc->arg);
+				if (err)
+					return err;
+			}
+
+			continue;
+		}
+
+		err = attr_encode(mb, type>>7, type & 0x7f, v);
+		if (err)
+			return err;
+
+		if (subc == 0)
+			continue;
+
+		err = bfcp_attrs_vencode(mb, subc, ap);
+		if (err)
+			return err;
+
+		/* update total length for grouped attributes */
+		len = mb->pos - start;
+
+		mb->pos = start + 1;
+		err = mbuf_write_u8(mb, (uint8_t)len);
+		if (err)
+			return err;
+
+		mb->pos += (len - BFCP_ATTR_HDR_SIZE);
+	}
+
+	return 0;
+}
+
+
+/**
+ * Encode BFCP Attributes
+ *
+ * @param mb      Mbuf to encode into
+ * @param attrc   Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_attrs_encode(struct mbuf *mb, unsigned attrc, ...)
+{
+	va_list ap;
+	int err;
+
+	va_start(ap, attrc);
+	err = bfcp_attrs_vencode(mb, attrc, &ap);
+	va_end(ap);
+
+	return err;
+}
+
+
+static int attr_decode(struct bfcp_attr **attrp, struct mbuf *mb,
+		       struct bfcp_unknown_attr *uma)
+{
+	struct bfcp_attr *attr;
+	union bfcp_union *v;
+	size_t i, start, len;
+	int err = 0;
+	uint8_t b;
+
+	if (mbuf_get_left(mb) < BFCP_ATTR_HDR_SIZE)
+		return EBADMSG;
+
+	attr = mem_zalloc(sizeof(*attr), destructor);
+	if (!attr)
+		return ENOMEM;
+
+	start = mb->pos;
+
+	b = mbuf_read_u8(mb);
+	attr->type = b >> 1;
+	attr->mand = b & 1;
+	len = mbuf_read_u8(mb);
+
+	if (len < BFCP_ATTR_HDR_SIZE)
+		goto badmsg;
+
+	len -= BFCP_ATTR_HDR_SIZE;
+
+	if (mbuf_get_left(mb) < len)
+		goto badmsg;
+
+	v = &attr->v;
+
+	switch (attr->type) {
+
+	case BFCP_BENEFICIARY_ID:
+	case BFCP_FLOOR_ID:
+	case BFCP_FLOOR_REQUEST_ID:
+		if (len < 2)
+			goto badmsg;
+
+		v->u16 = ntohs(mbuf_read_u16(mb));
+		break;
+
+	case BFCP_PRIORITY:
+		if (len < 2)
+			goto badmsg;
+
+		v->priority = mbuf_read_u8(mb) >> 5;
+		(void)mbuf_read_u8(mb);
+		break;
+
+	case BFCP_REQUEST_STATUS:
+		if (len < 2)
+			goto badmsg;
+
+		v->reqstatus.status = mbuf_read_u8(mb);
+		v->reqstatus.qpos   = mbuf_read_u8(mb);
+		break;
+
+	case BFCP_ERROR_CODE:
+		if (len < 1)
+			goto badmsg;
+
+		v->errcode.code = mbuf_read_u8(mb);
+		v->errcode.len  = len - 1;
+
+		if (v->errcode.len == 0)
+			break;
+
+		v->errcode.details = mem_alloc(v->errcode.len, NULL);
+		if (!v->errcode.details) {
+			err = ENOMEM;
+			goto error;
+		}
+
+		(void)mbuf_read_mem(mb, v->errcode.details,
+				    v->errcode.len);
+		break;
+
+	case BFCP_ERROR_INFO:
+	case BFCP_PART_PROV_INFO:
+	case BFCP_STATUS_INFO:
+	case BFCP_USER_DISP_NAME:
+	case BFCP_USER_URI:
+		err = mbuf_strdup(mb, &v->str, len);
+		break;
+
+	case BFCP_SUPPORTED_ATTRS:
+		v->supattr.attrc = len;
+		v->supattr.attrv = mem_alloc(len*sizeof(*v->supattr.attrv),
+					     NULL);
+		if (!v->supattr.attrv) {
+			err = ENOMEM;
+			goto error;
+		}
+
+		for (i=0; i<len; i++)
+			v->supattr.attrv[i] = mbuf_read_u8(mb) >> 1;
+		break;
+
+	case BFCP_SUPPORTED_PRIMS:
+		v->supprim.primc = len;
+		v->supprim.primv = mem_alloc(len * sizeof(*v->supprim.primv),
+					     NULL);
+		if (!v->supprim.primv) {
+			err = ENOMEM;
+			goto error;
+		}
+
+		for (i=0; i<len; i++)
+			v->supprim.primv[i] = mbuf_read_u8(mb);
+		break;
+
+		/* grouped attributes */
+
+	case BFCP_BENEFICIARY_INFO:
+	case BFCP_FLOOR_REQ_INFO:
+	case BFCP_REQUESTED_BY_INFO:
+	case BFCP_FLOOR_REQ_STATUS:
+	case BFCP_OVERALL_REQ_STATUS:
+		if (len < 2)
+			goto badmsg;
+
+		v->u16 = ntohs(mbuf_read_u16(mb));
+		err = bfcp_attrs_decode(&attr->attrl, mb, len - 2, uma);
+		break;
+
+	default:
+		mb->pos += len;
+
+		if (!attr->mand)
+			break;
+
+		if (uma && uma->typec < ARRAY_SIZE(uma->typev))
+			uma->typev[uma->typec++] = attr->type<<1;
+		break;
+	}
+
+	if (err)
+		goto error;
+
+	/* padding */
+	while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
+		++mb->pos;
+
+	*attrp = attr;
+
+	return 0;
+
+ badmsg:
+	err = EBADMSG;
+ error:
+	mem_deref(attr);
+
+	return err;
+}
+
+
+int bfcp_attrs_decode(struct list *attrl, struct mbuf *mb, size_t len,
+		      struct bfcp_unknown_attr *uma)
+{
+	int err = 0;
+	size_t end;
+
+	if (!attrl || !mb || mbuf_get_left(mb) < len)
+		return EINVAL;
+
+	end     = mb->end;
+	mb->end = mb->pos + len;
+
+	while (mbuf_get_left(mb) >= BFCP_ATTR_HDR_SIZE) {
+
+		struct bfcp_attr *attr;
+
+		err = attr_decode(&attr, mb, uma);
+		if (err)
+			break;
+
+		list_append(attrl, &attr->le, attr);
+	}
+
+	mb->end = end;
+
+	return err;
+}
+
+
+struct bfcp_attr *bfcp_attrs_find(const struct list *attrl,
+				  enum bfcp_attrib type)
+{
+	struct le *le = list_head(attrl);
+
+	while (le) {
+		struct bfcp_attr *attr = le->data;
+
+		le = le->next;
+
+		if (attr->type == type)
+			return attr;
+	}
+
+	return NULL;
+}
+
+
+struct bfcp_attr *bfcp_attrs_apply(const struct list *attrl,
+				   bfcp_attr_h *h, void *arg)
+{
+	struct le *le = list_head(attrl);
+
+	while (le) {
+		struct bfcp_attr *attr = le->data;
+
+		le = le->next;
+
+		if (h && h(attr, arg))
+			return attr;
+	}
+
+	return NULL;
+}
+
+
+/**
+ * Get a BFCP sub-attribute from a BFCP attribute
+ *
+ * @param attr BFCP attribute
+ * @param type Attribute type
+ *
+ * @return Matching BFCP attribute if found, otherwise NULL
+ */
+struct bfcp_attr *bfcp_attr_subattr(const struct bfcp_attr *attr,
+				    enum bfcp_attrib type)
+{
+	if (!attr)
+		return NULL;
+
+	return bfcp_attrs_find(&attr->attrl, type);
+}
+
+
+/**
+ * Apply a function handler to all sub-attributes in a BFCP attribute
+ *
+ * @param attr BFCP attribute
+ * @param h    Handler
+ * @param arg  Handler argument
+ *
+ * @return BFCP attribute returned by handler, or NULL
+ */
+struct bfcp_attr *bfcp_attr_subattr_apply(const struct bfcp_attr *attr,
+					  bfcp_attr_h *h, void *arg)
+{
+	if (!attr)
+		return NULL;
+
+	return bfcp_attrs_apply(&attr->attrl, h, arg);
+}
+
+
+/**
+ * Print a BFCP attribute
+ *
+ * @param pf   Print function
+ * @param attr BFCP attribute
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *attr)
+{
+	const union bfcp_union *v;
+	size_t i;
+	int err;
+
+	if (!attr)
+		return 0;
+
+	err = re_hprintf(pf, "%c%-28s", attr->mand ? '*' : ' ',
+			 bfcp_attr_name(attr->type));
+
+	v = &attr->v;
+
+	switch (attr->type) {
+
+	case BFCP_BENEFICIARY_ID:
+	case BFCP_FLOOR_ID:
+	case BFCP_FLOOR_REQUEST_ID:
+		err |= re_hprintf(pf, "%u", v->u16);
+		break;
+
+	case BFCP_PRIORITY:
+		err |= re_hprintf(pf, "%d", v->priority);
+		break;
+
+	case BFCP_REQUEST_STATUS:
+		err |= re_hprintf(pf, "%s (%d), qpos=%u",
+				  bfcp_reqstatus_name(v->reqstatus.status),
+				  v->reqstatus.status,
+				  v->reqstatus.qpos);
+		break;
+
+	case BFCP_ERROR_CODE:
+		err |= re_hprintf(pf, "%d (%s)", v->errcode.code,
+				  bfcp_errcode_name(v->errcode.code));
+
+		if (v->errcode.code == BFCP_UNKNOWN_MAND_ATTR) {
+
+			for (i=0; i<v->errcode.len; i++) {
+
+				uint8_t type = v->errcode.details[i] >> 1;
+
+				err |= re_hprintf(pf, " %s",
+						  bfcp_attr_name(type));
+			}
+		}
+		break;
+
+	case BFCP_ERROR_INFO:
+	case BFCP_PART_PROV_INFO:
+	case BFCP_STATUS_INFO:
+	case BFCP_USER_DISP_NAME:
+	case BFCP_USER_URI:
+		err |= re_hprintf(pf, "\"%s\"", v->str);
+		break;
+
+	case BFCP_SUPPORTED_ATTRS:
+		err |= re_hprintf(pf, "%zu:", v->supattr.attrc);
+
+		for (i=0; i<v->supattr.attrc; i++) {
+
+			const enum bfcp_attrib type = v->supattr.attrv[i];
+
+			err |= re_hprintf(pf, " %s", bfcp_attr_name(type));
+		}
+		break;
+
+	case BFCP_SUPPORTED_PRIMS:
+		err |= re_hprintf(pf, "%zu:", v->supprim.primc);
+
+		for (i=0; i<v->supprim.primc; i++) {
+
+			const enum bfcp_prim prim = v->supprim.primv[i];
+
+			err |= re_hprintf(pf, " %s", bfcp_prim_name(prim));
+		}
+		break;
+
+		/* Grouped Attributes */
+
+	case BFCP_BENEFICIARY_INFO:
+		err |= re_hprintf(pf, "beneficiary-id=%u", v->beneficiaryid);
+		break;
+
+	case BFCP_FLOOR_REQ_INFO:
+		err |= re_hprintf(pf, "floor-request-id=%u", v->floorreqid);
+		break;
+
+	case BFCP_REQUESTED_BY_INFO:
+		err |= re_hprintf(pf, "requested-by-id=%u", v->reqbyid);
+		break;
+
+	case BFCP_FLOOR_REQ_STATUS:
+		err |= re_hprintf(pf, "floor-id=%u", v->floorid);
+		break;
+
+	case BFCP_OVERALL_REQ_STATUS:
+		err |= re_hprintf(pf, "floor-request-id=%u", v->floorreqid);
+		break;
+
+	default:
+		err |= re_hprintf(pf, "???");
+		break;
+	}
+
+	return err;
+}
+
+
+int bfcp_attrs_print(struct re_printf *pf, const struct list *attrl,
+		     unsigned level)
+{
+	struct le *le;
+	int err = 0;
+
+	for (le=list_head(attrl); le; le=le->next) {
+
+		const struct bfcp_attr *attr = le->data;
+		unsigned i;
+
+		for (i=0; i<level; i++)
+			err |= re_hprintf(pf, "    ");
+
+		err |= re_hprintf(pf, "%H\n", bfcp_attr_print, attr);
+		err |= bfcp_attrs_print(pf, &attr->attrl, level + 1);
+	}
+
+	return err;
+}
+
+
+/**
+ * Get the BFCP attribute name
+ *
+ * @param type BFCP attribute type
+ *
+ * @return String with BFCP attribute name
+ */
+const char *bfcp_attr_name(enum bfcp_attrib type)
+{
+	switch (type) {
+
+	case BFCP_BENEFICIARY_ID:     return "BENEFICIARY-ID";
+	case BFCP_FLOOR_ID:           return "FLOOR-ID";
+	case BFCP_FLOOR_REQUEST_ID:   return "FLOOR-REQUEST-ID";
+	case BFCP_PRIORITY:           return "PRIORITY";
+	case BFCP_REQUEST_STATUS:     return "REQUEST-STATUS";
+	case BFCP_ERROR_CODE:         return "ERROR-CODE";
+	case BFCP_ERROR_INFO:         return "ERROR-INFO";
+	case BFCP_PART_PROV_INFO:     return "PARTICIPANT-PROVIDED-INFO";
+	case BFCP_STATUS_INFO:        return "STATUS-INFO";
+	case BFCP_SUPPORTED_ATTRS:    return "SUPPORTED-ATTRIBUTES";
+	case BFCP_SUPPORTED_PRIMS:    return "SUPPORTED-PRIMITIVES";
+	case BFCP_USER_DISP_NAME:     return "USER-DISPLAY-NAME";
+	case BFCP_USER_URI:           return "USER-URI";
+	case BFCP_BENEFICIARY_INFO:   return "BENEFICIARY-INFORMATION";
+	case BFCP_FLOOR_REQ_INFO:     return "FLOOR-REQUEST-INFORMATION";
+	case BFCP_REQUESTED_BY_INFO:  return "REQUESTED-BY-INFORMATION";
+	case BFCP_FLOOR_REQ_STATUS:   return "FLOOR-REQUEST-STATUS";
+	case BFCP_OVERALL_REQ_STATUS: return "OVERALL-REQUEST-STATUS";
+	default:                      return "???";
+	}
+}
+
+
+/**
+ * Get the BFCP Request status name
+ *
+ * @param status Request status
+ *
+ * @return String with BFCP Request status name
+ */
+const char *bfcp_reqstatus_name(enum bfcp_reqstat status)
+{
+	switch (status) {
+
+	case BFCP_PENDING:   return "Pending";
+	case BFCP_ACCEPTED:  return "Accepted";
+	case BFCP_GRANTED:   return "Granted";
+	case BFCP_DENIED:    return "Denied";
+	case BFCP_CANCELLED: return "Cancelled";
+	case BFCP_RELEASED:  return "Released";
+	case BFCP_REVOKED:   return "Revoked";
+	default:             return "???";
+	}
+}
+
+
+/**
+ * Get the BFCP Error code name
+ *
+ * @param code BFCP Error code
+ *
+ * @return String with error code
+ */
+const char *bfcp_errcode_name(enum bfcp_err code)
+{
+	switch (code) {
+
+	case BFCP_CONF_NOT_EXIST:
+		return "Conference does not Exist";
+
+	case BFCP_USER_NOT_EXIST:
+		return "User does not Exist";
+
+	case BFCP_UNKNOWN_PRIM:
+		return "Unknown Primitive";
+
+	case BFCP_UNKNOWN_MAND_ATTR:
+		return "Unknown Mandatory Attribute";
+
+	case BFCP_UNAUTH_OPERATION:
+		return "Unauthorized Operation";
+
+	case BFCP_INVALID_FLOOR_ID:
+		return "Invalid Floor ID";
+
+	case BFCP_FLOOR_REQ_ID_NOT_EXIST:
+		return "Floor Request ID Does Not Exist";
+
+	case BFCP_MAX_FLOOR_REQ_REACHED:
+		return "You have Already Reached the Maximum Number "
+		       "of Ongoing Floor Requests for this Floor";
+
+	case BFCP_USE_TLS:
+		return "Use TLS";
+
+	case BFCP_PARSE_ERROR:
+		return "Unable to Parse Message";
+
+	case BFCP_USE_DTLS:
+		return "Use DTLS";
+
+	case BFCP_UNSUPPORTED_VERSION:
+		return "Unsupported Version";
+
+	case BFCP_BAD_LENGTH:
+		return "Incorrect Message Length";
+
+	case BFCP_GENERIC_ERROR:
+		return "Generic Error";
+
+	default:
+		return "???";
+	}
+}
diff --git a/src/bfcp/bfcp.h b/src/bfcp/bfcp.h
new file mode 100644
index 0000000..7e76e0d
--- /dev/null
+++ b/src/bfcp/bfcp.h
@@ -0,0 +1,48 @@
+/**
+ * @file bfcp.h Internal interface to Binary Floor Control Protocol (BFCP)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+struct bfcp_strans {
+	enum bfcp_prim prim;
+	uint32_t confid;
+	uint16_t tid;
+	uint16_t userid;
+};
+
+struct bfcp_conn {
+	struct bfcp_strans st;
+	struct list ctransl;
+	struct tmr tmr1;
+	struct tmr tmr2;
+	struct udp_sock *us;
+	struct mbuf *mb;
+	bfcp_recv_h *recvh;
+	void *arg;
+	enum bfcp_transp tp;
+	unsigned txc;
+	uint16_t tid;
+};
+
+
+/* attributes */
+int bfcp_attrs_decode(struct list *attrl, struct mbuf *mb, size_t len,
+		      struct bfcp_unknown_attr *uma);
+struct bfcp_attr *bfcp_attrs_find(const struct list *attrl,
+				  enum bfcp_attrib type);
+struct bfcp_attr *bfcp_attrs_apply(const struct list *attrl,
+				   bfcp_attr_h *h, void *arg);
+int bfcp_attrs_print(struct re_printf *pf, const struct list *attrl,
+		     unsigned level);
+
+
+/* connection */
+int bfcp_send(struct bfcp_conn *bc, const struct sa *dst, struct mbuf *mb);
+
+
+/* request */
+bool bfcp_handle_response(struct bfcp_conn *bc, const struct bfcp_msg *msg);
+int  bfcp_vrequest(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,
+		   enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+		   bfcp_resp_h *resph, void *arg, unsigned attrc, va_list *ap);
diff --git a/src/bfcp/conn.c b/src/bfcp/conn.c
new file mode 100644
index 0000000..6e4051d
--- /dev/null
+++ b/src/bfcp/conn.c
@@ -0,0 +1,156 @@
+/**
+ * @file bfcp/conn.c BFCP Connection
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_udp.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+static void destructor(void *arg)
+{
+	struct bfcp_conn *bc = arg;
+
+	list_flush(&bc->ctransl);
+	tmr_cancel(&bc->tmr1);
+	tmr_cancel(&bc->tmr2);
+	mem_deref(bc->us);
+	mem_deref(bc->mb);
+}
+
+
+static bool strans_cmp(const struct bfcp_strans *st,
+		       const struct bfcp_msg *msg)
+{
+	if (st->tid != msg->tid)
+		return false;
+
+	if (st->prim != msg->prim)
+		return false;
+
+	if (st->confid != msg->confid)
+		return false;
+
+	if (st->userid != msg->userid)
+		return false;
+
+	return true;
+}
+
+
+static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)
+{
+	struct bfcp_conn *bc = arg;
+	struct bfcp_msg *msg;
+	int err;
+
+	err = bfcp_msg_decode(&msg, mb);
+	if (err)
+		return;
+
+	msg->src = *src;
+
+	if (bfcp_handle_response(bc, msg))
+		goto out;
+
+	if (bc->mb && strans_cmp(&bc->st, msg)) {
+		(void)bfcp_send(bc, &msg->src, bc->mb);
+		goto out;
+	}
+
+	if (bc->recvh)
+		bc->recvh(msg, bc->arg);
+
+out:
+	mem_deref(msg);
+}
+
+
+/**
+ * Create BFCP connection
+ *
+ * @param bcp   Pointer to BFCP connection
+ * @param tp    BFCP Transport type
+ * @param laddr Optional listening address/port
+ * @param tls   TLS Context (optional)
+ * @param recvh Receive handler
+ * @param arg   Receive handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_listen(struct bfcp_conn **bcp, enum bfcp_transp tp, struct sa *laddr,
+		struct tls *tls, bfcp_recv_h *recvh, void *arg)
+{
+	struct bfcp_conn *bc;
+	int err;
+	(void)tls;
+
+	if (!bcp)
+		return EINVAL;
+
+	bc = mem_zalloc(sizeof(*bc), destructor);
+	if (!bc)
+		return ENOMEM;
+
+	bc->tp    = tp;
+	bc->recvh = recvh;
+	bc->arg   = arg;
+
+	switch (bc->tp) {
+
+	case BFCP_UDP:
+		err = udp_listen(&bc->us, laddr, udp_recv_handler, bc);
+		if (err)
+			goto out;
+
+		if (laddr) {
+			err = udp_local_get(bc->us, laddr);
+			if (err)
+				goto out;
+		}
+		break;
+
+	default:
+		err = ENOSYS;
+		goto out;
+	}
+
+ out:
+	if (err)
+		mem_deref(bc);
+	else
+		*bcp = bc;
+
+	return err;
+}
+
+
+int bfcp_send(struct bfcp_conn *bc, const struct sa *dst, struct mbuf *mb)
+{
+	if (!bc || !dst || !mb)
+		return EINVAL;
+
+	switch (bc->tp) {
+
+	case BFCP_UDP:
+		return udp_send(bc->us, dst, mb);
+
+	default:
+		return ENOSYS;
+	}
+}
+
+
+void *bfcp_sock(const struct bfcp_conn *bc)
+{
+	return bc ? bc->us : NULL;
+}
diff --git a/src/bfcp/mod.mk b/src/bfcp/mod.mk
new file mode 100644
index 0000000..b533415
--- /dev/null
+++ b/src/bfcp/mod.mk
@@ -0,0 +1,11 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS	+= bfcp/attr.c
+SRCS	+= bfcp/conn.c
+SRCS	+= bfcp/msg.c
+SRCS	+= bfcp/reply.c
+SRCS	+= bfcp/request.c
diff --git a/src/bfcp/msg.c b/src/bfcp/msg.c
new file mode 100644
index 0000000..03f0b72
--- /dev/null
+++ b/src/bfcp/msg.c
@@ -0,0 +1,288 @@
+/**
+ * @file bfcp/msg.c BFCP Message
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+enum {
+	BFCP_HDR_SIZE = 12,
+};
+
+
+static void destructor(void *arg)
+{
+	struct bfcp_msg *msg = arg;
+
+	list_flush(&msg->attrl);
+}
+
+
+static int hdr_encode(struct mbuf *mb, uint8_t ver, bool r,
+		      enum bfcp_prim prim, uint16_t len, uint32_t confid,
+		      uint16_t tid, uint16_t userid)
+{
+	int err;
+
+	err  = mbuf_write_u8(mb, (ver << 5) | ((r ? 1 : 0) << 4));
+	err |= mbuf_write_u8(mb, prim);
+	err |= mbuf_write_u16(mb, htons(len));
+	err |= mbuf_write_u32(mb, htonl(confid));
+	err |= mbuf_write_u16(mb, htons(tid));
+	err |= mbuf_write_u16(mb, htons(userid));
+
+	return err;
+}
+
+
+static int hdr_decode(struct bfcp_msg *msg, struct mbuf *mb)
+{
+	uint8_t b;
+
+	if (mbuf_get_left(mb) < BFCP_HDR_SIZE)
+		return ENODATA;
+
+	b = mbuf_read_u8(mb);
+
+	msg->ver    = b >> 5;
+	msg->r      = (b >> 4) & 1;
+	msg->f      = (b >> 3) & 1;
+	msg->prim   = mbuf_read_u8(mb);
+	msg->len    = ntohs(mbuf_read_u16(mb));
+	msg->confid = ntohl(mbuf_read_u32(mb));
+	msg->tid    = ntohs(mbuf_read_u16(mb));
+	msg->userid = ntohs(mbuf_read_u16(mb));
+
+	if (msg->ver != BFCP_VER1 && msg->ver != BFCP_VER2)
+		return EBADMSG;
+
+	/* fragmentation not supported */
+	if (msg->f)
+		return ENOSYS;
+
+	if (mbuf_get_left(mb) < (size_t)(4*msg->len))
+		return ENODATA;
+
+	return 0;
+}
+
+
+/**
+ * Encode a BFCP message with variable arguments
+ *
+ * @param mb      Mbuf to encode into
+ * @param ver     Protocol version
+ * @param r       Transaction responder flag
+ * @param prim    BFCP Primitive
+ * @param confid  Conference ID
+ * @param tid     Transaction ID
+ * @param userid  User ID
+ * @param attrc   Number of attributes
+ * @param ap      Variable argument of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_msg_vencode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim,
+		     uint32_t confid, uint16_t tid, uint16_t userid,
+		     unsigned attrc, va_list *ap)
+{
+	size_t start, len;
+	int err;
+
+	if (!mb)
+		return EINVAL;
+
+	start = mb->pos;
+	mb->pos += BFCP_HDR_SIZE;
+
+	err = bfcp_attrs_vencode(mb, attrc, ap);
+	if (err)
+		return err;
+
+	/* header */
+	len = mb->pos - start - BFCP_HDR_SIZE;
+	mb->pos = start;
+	err = hdr_encode(mb, ver, r, prim, (uint16_t)(len/4), confid, tid,
+			 userid);
+	mb->pos += len;
+
+	return err;
+}
+
+
+/**
+ * Encode a BFCP message
+ *
+ * @param mb      Mbuf to encode into
+ * @param ver     Protocol version
+ * @param r       Transaction responder flag
+ * @param prim    BFCP Primitive
+ * @param confid  Conference ID
+ * @param tid     Transaction ID
+ * @param userid  User ID
+ * @param attrc   Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_msg_encode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim,
+		    uint32_t confid, uint16_t tid, uint16_t userid,
+		    unsigned attrc, ...)
+{
+	va_list ap;
+	int err;
+
+	va_start(ap, attrc);
+	err = bfcp_msg_vencode(mb, ver, r, prim, confid, tid, userid,
+			       attrc, &ap);
+	va_end(ap);
+
+	return err;
+}
+
+
+/**
+ * Decode a BFCP message from a buffer
+ *
+ * @param msgp Pointer to allocated and decoded BFCP message
+ * @param mb   Mbuf to decode from
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb)
+{
+	struct bfcp_msg *msg;
+	size_t start;
+	int err;
+
+	if (!msgp || !mb)
+		return EINVAL;
+
+	msg = mem_zalloc(sizeof(*msg), destructor);
+	if (!msg)
+		return ENOMEM;
+
+	start = mb->pos;
+
+	err = hdr_decode(msg, mb);
+	if (err) {
+		mb->pos = start;
+		goto out;
+	}
+
+	err = bfcp_attrs_decode(&msg->attrl, mb, 4*msg->len, &msg->uma);
+	if (err)
+		goto out;
+
+ out:
+	if (err)
+		mem_deref(msg);
+	else
+		*msgp = msg;
+
+	return err;
+}
+
+
+/**
+ * Get a BFCP attribute from a BFCP message
+ *
+ * @param msg  BFCP message
+ * @param type Attribute type
+ *
+ * @return Matching BFCP attribute if found, otherwise NULL
+ */
+struct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg,
+				enum bfcp_attrib type)
+{
+	if (!msg)
+		return NULL;
+
+	return bfcp_attrs_find(&msg->attrl, type);
+}
+
+
+/**
+ * Apply a function handler to all attributes in a BFCP message
+ *
+ * @param msg  BFCP message
+ * @param h    Handler
+ * @param arg  Handler argument
+ *
+ * @return BFCP attribute returned by handler, or NULL
+ */
+struct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg,
+				      bfcp_attr_h *h, void *arg)
+{
+	if (!msg)
+		return NULL;
+
+	return bfcp_attrs_apply(&msg->attrl, h, arg);
+}
+
+
+/**
+ * Print a BFCP message
+ *
+ * @param pf  Print function
+ * @param msg BFCP message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg)
+{
+	int err;
+
+	if (!msg)
+		return 0;
+
+	err = re_hprintf(pf, "%s (confid=%u tid=%u userid=%u)\n",
+			 bfcp_prim_name(msg->prim), msg->confid,
+			 msg->tid, msg->userid);
+
+	err |= bfcp_attrs_print(pf, &msg->attrl, 0);
+
+	return err;
+}
+
+
+/**
+ * Get the BFCP primitive name
+ *
+ * @param prim BFCP primitive
+ *
+ * @return String with BFCP primitive name
+ */
+const char *bfcp_prim_name(enum bfcp_prim prim)
+{
+	switch (prim) {
+
+	case BFCP_FLOOR_REQUEST:        return "FloorRequest";
+	case BFCP_FLOOR_RELEASE:        return "FloorRelease";
+	case BFCP_FLOOR_REQUEST_QUERY:  return "FloorRequestQuery";
+	case BFCP_FLOOR_REQUEST_STATUS: return "FloorRequestStatus";
+	case BFCP_USER_QUERY:           return "UserQuery";
+	case BFCP_USER_STATUS:          return "UserStatus";
+	case BFCP_FLOOR_QUERY:          return "FloorQuery";
+	case BFCP_FLOOR_STATUS:         return "FloorStatus";
+	case BFCP_CHAIR_ACTION:         return "ChairAction";
+	case BFCP_CHAIR_ACTION_ACK:     return "ChairActionAck";
+	case BFCP_HELLO:                return "Hello";
+	case BFCP_HELLO_ACK:            return "HelloAck";
+	case BFCP_ERROR:                return "Error";
+	case BFCP_FLOOR_REQ_STATUS_ACK: return "FloorRequestStatusAck";
+	case BFCP_FLOOR_STATUS_ACK:     return "FloorStatusAck";
+	case BFCP_GOODBYE:              return "Goodbye";
+	case BFCP_GOODBYE_ACK:          return "GoodbyeAck";
+	default:                        return "???";
+	}
+}
diff --git a/src/bfcp/reply.c b/src/bfcp/reply.c
new file mode 100644
index 0000000..321429c
--- /dev/null
+++ b/src/bfcp/reply.c
@@ -0,0 +1,124 @@
+/**
+ * @file bfcp/reply.c BFCP Reply
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+enum {
+	BFCP_T2  = 10000,
+};
+
+
+static void tmr_handler(void *arg)
+{
+	struct bfcp_conn *bc = arg;
+
+	bc->mb = mem_deref(bc->mb);
+}
+
+
+/**
+ * Send a BFCP response
+ *
+ * @param bc      BFCP connection
+ * @param req     BFCP request message
+ * @param prim    BFCP Primitive
+ * @param attrc   Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_reply(struct bfcp_conn *bc, const struct bfcp_msg *req,
+	       enum bfcp_prim prim, unsigned attrc, ...)
+{
+	va_list ap;
+	int err;
+
+	if (!bc || !req)
+		return EINVAL;
+
+	bc->mb = mem_deref(bc->mb);
+	tmr_cancel(&bc->tmr2);
+
+	bc->mb = mbuf_alloc(64);
+	if (!bc->mb)
+		return ENOMEM;
+
+	va_start(ap, attrc);
+	err = bfcp_msg_vencode(bc->mb, req->ver, true, prim, req->confid,
+			       req->tid, req->userid, attrc, &ap);
+	va_end(ap);
+
+	if (err)
+		goto out;
+
+	bc->mb->pos = 0;
+
+	err = bfcp_send(bc, &req->src, bc->mb);
+	if (err)
+		goto out;
+
+	bc->st.prim   = req->prim;
+	bc->st.confid = req->confid;
+	bc->st.tid    = req->tid;
+	bc->st.userid = req->userid;
+
+	tmr_start(&bc->tmr2, BFCP_T2, tmr_handler, bc);
+
+ out:
+	if (err)
+		bc->mb = mem_deref(bc->mb);
+
+	return err;
+}
+
+
+/**
+ * Send a BFCP error response with details
+ *
+ * @param bc      BFCP connection
+ * @param req     BFCP request message
+ * @param code    Error code
+ * @param details Error details
+ * @param len     Details length
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_edreply(struct bfcp_conn *bc, const struct bfcp_msg *req,
+		 enum bfcp_err code, const uint8_t *details, size_t len)
+{
+	struct bfcp_errcode errcode;
+
+	errcode.code    = code;
+	errcode.details = (uint8_t *)details;
+	errcode.len     = len;
+
+	return bfcp_reply(bc, req, BFCP_ERROR, 1,
+			  BFCP_ERROR_CODE, 0, &errcode);
+}
+
+
+/**
+ * Send a BFCP error response
+ *
+ * @param bc      BFCP connection
+ * @param req     BFCP request message
+ * @param code    Error code
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_ereply(struct bfcp_conn *bc, const struct bfcp_msg *req,
+		enum bfcp_err code)
+{
+	return bfcp_edreply(bc, req, code, NULL, 0);
+}
diff --git a/src/bfcp/request.c b/src/bfcp/request.c
new file mode 100644
index 0000000..cfe066e
--- /dev/null
+++ b/src/bfcp/request.c
@@ -0,0 +1,257 @@
+/**
+ * @file bfcp/request.c BFCP Request
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_tmr.h>
+#include <re_bfcp.h>
+#include "bfcp.h"
+
+
+enum {
+	BFCP_T1  = 500,
+	BFCP_TXC = 4,
+};
+
+
+struct bfcp_ctrans {
+	struct le le;
+	struct sa dst;
+	struct mbuf *mb;
+	bfcp_resp_h *resph;
+	void *arg;
+	uint32_t confid;
+	uint16_t userid;
+	uint16_t tid;
+};
+
+
+static void tmr_handler(void *arg);
+
+
+static void dummy_resp_handler(int err, const struct bfcp_msg *msg, void *arg)
+{
+	(void)err;
+	(void)msg;
+	(void)arg;
+}
+
+
+static void destructor(void *arg)
+{
+	struct bfcp_ctrans *ct = arg;
+
+	list_unlink(&ct->le);
+	mem_deref(ct->mb);
+}
+
+
+static void dispatch(struct bfcp_conn *bc)
+{
+	struct le *le = bc->ctransl.head;
+
+	while (le) {
+		struct bfcp_ctrans *ct = le->data;
+		int err;
+
+		le = le->next;
+
+		err = bfcp_send(bc, &ct->dst, ct->mb);
+		if (err) {
+			ct->resph(err, NULL, ct->arg);
+			mem_deref(ct);
+			continue;
+		}
+
+		tmr_start(&bc->tmr1, BFCP_T1, tmr_handler, bc);
+		bc->txc = 1;
+		break;
+	}
+}
+
+
+static void tmr_handler(void *arg)
+{
+	struct bfcp_conn *bc = arg;
+	struct bfcp_ctrans *ct;
+	uint32_t timeout;
+	int err;
+
+	ct = list_ledata(bc->ctransl.head);
+	if (!ct)
+		return;
+
+	timeout = BFCP_T1<<bc->txc;
+
+	if (++bc->txc > BFCP_TXC) {
+		err = ETIMEDOUT;
+		goto out;
+	}
+
+	err = bfcp_send(bc, &ct->dst, ct->mb);
+	if (err)
+		goto out;
+
+	tmr_start(&bc->tmr1, timeout, tmr_handler, bc);
+	return;
+
+ out:
+	ct->resph(err, NULL, ct->arg);
+	mem_deref(ct);
+	dispatch(bc);
+}
+
+
+bool bfcp_handle_response(struct bfcp_conn *bc, const struct bfcp_msg *msg)
+{
+	struct bfcp_ctrans *ct;
+
+	if (!bc || !msg)
+		return false;
+
+	ct = list_ledata(bc->ctransl.head);
+	if (!ct)
+		return false;
+
+	if (msg->tid != ct->tid)
+		return false;
+
+	if (msg->confid != ct->confid)
+		return false;
+
+	if (msg->userid != ct->userid)
+		return false;
+
+	tmr_cancel(&bc->tmr1);
+
+	ct->resph(0, msg, ct->arg);
+	mem_deref(ct);
+
+	dispatch(bc);
+
+	return true;
+}
+
+
+int bfcp_vrequest(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,
+		  enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+		  bfcp_resp_h *resph, void *arg, unsigned attrc, va_list *ap)
+{
+	struct bfcp_ctrans *ct;
+	int err;
+
+	if (!bc || !dst)
+		return EINVAL;
+
+	ct = mem_zalloc(sizeof(*ct), destructor);
+	if (!ct)
+		return ENOMEM;
+
+	if (bc->tid == 0)
+		bc->tid = 1;
+
+	ct->dst    = *dst;
+	ct->confid = confid;
+	ct->userid = userid;
+	ct->tid    = bc->tid++;
+	ct->resph  = resph ? resph : dummy_resp_handler;
+	ct->arg    = arg;
+
+	ct->mb = mbuf_alloc(128);
+	if (!ct->mb) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	err = bfcp_msg_vencode(ct->mb, ver, false, prim, confid, ct->tid,
+			       userid, attrc, ap);
+	if (err)
+		goto out;
+
+	ct->mb->pos = 0;
+
+	if (!bc->ctransl.head) {
+
+		err = bfcp_send(bc, &ct->dst, ct->mb);
+		if (err)
+			goto out;
+
+		tmr_start(&bc->tmr1, BFCP_T1, tmr_handler, bc);
+		bc->txc = 1;
+	}
+
+	list_append(&bc->ctransl, &ct->le, ct);
+
+ out:
+	if (err)
+		mem_deref(ct);
+
+	return err;
+}
+
+
+/**
+ * Send a BFCP request
+ *
+ * @param bc      BFCP connection
+ * @param dst     Destination address
+ * @param ver     BFCP Version
+ * @param prim    BFCP Primitive
+ * @param confid  Conference ID
+ * @param userid  User ID
+ * @param resph   Response handler
+ * @param arg     Response handler argument
+ * @param attrc   Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_request(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,
+		 enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+		 bfcp_resp_h *resph, void *arg, unsigned attrc, ...)
+{
+	va_list ap;
+	int err;
+
+	va_start(ap, attrc);
+	err = bfcp_vrequest(bc, dst, ver, prim, confid, userid, resph, arg,
+			    attrc, &ap);
+	va_end(ap);
+
+	return err;
+}
+
+
+/**
+ * Send a BFCP notification/subsequent response
+ *
+ * @param bc      BFCP connection
+ * @param dst     Destination address
+ * @param ver     BFCP Version
+ * @param prim    BFCP Primitive
+ * @param confid  Conference ID
+ * @param userid  User ID
+ * @param attrc   Number of attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int bfcp_notify(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,
+		enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+		unsigned attrc, ...)
+{
+	va_list ap;
+	int err;
+
+	va_start(ap, attrc);
+	err = bfcp_vrequest(bc, dst, ver, prim, confid, userid, NULL, NULL,
+			    attrc, &ap);
+	va_end(ap);
+
+	return err;
+}