diff --git a/Makefile b/Makefile
index 474435f..ac52c76 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,7 @@ MODULES += udp sa net tcp tls
MODULES += list mbuf hash
MODULES += fmt tmr main mem dbg sys lock mqueue
MODULES += mod conf
+MODULES += bfcp
INSTALL := install
ifeq ($(DESTDIR),)
diff --git a/docs/README b/docs/README
index e6b24c9..f3d6b9a 100644
--- a/docs/README
+++ b/docs/README
@@ -88,6 +88,7 @@ Features:
* RFC 3994 - Indication of Message Composition for Instant Messaging
* RFC 4346 - The TLS Protocol Version 1.1
* RFC 4566 - SDP: Session Description Protocol
+* RFC 4582 - The Binary Floor Control Protocol (BFCP)
* RFC 4585 - Extended RTP Profile for RTCP-Based Feedback
* RFC 4733 - RTP Payload for DTMF Digits, Telephony Tones, and Teleph. Signals
* RFC 4961 - Symmetric RTP / RTP Control Protocol (RTCP)
diff --git a/include/re.h b/include/re.h
index cf6e36e..ffca789 100644
--- a/include/re.h
+++ b/include/re.h
@@ -17,6 +17,7 @@ extern "C" {
/* Library modules */
#include "re_base64.h"
+#include "re_bfcp.h"
#include "re_conf.h"
#include "re_crc32.h"
#include "re_dns.h"
diff --git a/include/re_bfcp.h b/include/re_bfcp.h
new file mode 100644
index 0000000..7de1f97
--- /dev/null
+++ b/include/re_bfcp.h
@@ -0,0 +1,236 @@
+/**
+ * @file re_bfcp.h Interface to Binary Floor Control Protocol (BFCP)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+enum {BFCP_VERSION = 1};
+
+/** BFCP Primitives */
+enum bfcp_prim {
+ BFCP_FLOOR_REQUEST = 1,
+ BFCP_FLOOR_RELEASE = 2,
+ BFCP_FLOOR_REQUEST_QUERY = 3,
+ BFCP_FLOOR_REQUEST_STAT = 4,
+ BFCP_USER_QUERY = 5,
+ BFCP_USER_STATUS = 6,
+ BFCP_FLOOR_QUERY = 7,
+ BFCP_FLOOR_STATUS = 8,
+ BFCP_CHAIR_ACTION = 9,
+ BFCP_CHAIR_ACTION_ACK = 10,
+ BFCP_HELLO = 11,
+ BFCP_HELLO_ACK = 12,
+ BFCP_ERROR = 13,
+};
+
+/** BFCP Attributes */
+enum bfcp_attrib {
+ BFCP_BENEFICIARY_ID = 1,
+ BFCP_FLOOR_ID = 2,
+ BFCP_FLOOR_REQUEST_ID = 3,
+ BFCP_PRIORITY = 4,
+ BFCP_REQUEST_STATUS = 5,
+ BFCP_ERROR_CODE = 6,
+ BFCP_ERROR_INFO = 7,
+ BFCP_PARTICIPANT_PROV_INFO = 8,
+ BFCP_STATUS_INFO = 9,
+ BFCP_SUPPORTED_ATTRIBUTES = 10,
+ BFCP_SUPPORTED_PRIMITIVES = 11,
+ BFCP_USER_DISPLAY_NAME = 12,
+ BFCP_USER_URI = 13,
+ /* grouped: */
+ BFCP_BENEFICIARY_INFO = 14,
+ BFCP_FLOOR_REQUEST_INFO = 15,
+ BFCP_REQUESTED_BY_INFO = 16,
+ BFCP_FLOOR_REQUEST_STATUS = 17,
+ BFCP_OVERALL_REQUEST_STATUS = 18,
+};
+
+/** BFCP Request Status */
+enum bfcp_rstat {
+ BFCP_PENDING = 1,
+ BFCP_ACCEPTED = 2,
+ BFCP_GRANTED = 3,
+ BFCP_DENIED = 4,
+ BFCP_CANCELLED = 5,
+ BFCP_RELEASED = 6,
+ BFCP_REVOKED = 7
+};
+
+/** BFCP Error Codes */
+enum bfcp_err {
+ BFCP_ERR_CONF_NOT_EXIST = 1,
+ BFCP_ERR_USER_NOT_EXIST = 2,
+ BFCP_ERR_UNKNOWN_PRIM = 3,
+ BFCP_ERR_UNKNOWN_MAND_ATTR = 4,
+ BFCP_ERR_UNAUTH_OPERATION = 5,
+ BFCP_ERR_INVALID_FLOOR_ID = 6,
+ BFCP_ERR_FLOOR_REQ_ID_NOT_EXIST = 7,
+ BFCP_ERR_MAX_FLOOR_REQ_REACHED = 8,
+ BFCP_ERR_USE_TLS = 9
+};
+
+enum bfcp_prio {
+ BFCP_PRIO_LOWEST = 0,
+ BFCP_PRIO_LOW = 1,
+ BFCP_PRIO_NORMAL = 2,
+ BFCP_PRIO_HIGH = 3,
+ BFCP_PRIO_HIGHEST = 4
+};
+
+struct bfcp_reqstat {
+ enum bfcp_rstat stat;
+ uint8_t qpos;
+};
+
+struct bfcp_errcode {
+ enum bfcp_err code;
+ uint8_t *details; /* optional */
+ size_t len;
+};
+
+struct bfcp_supattr {
+ enum bfcp_attrib *attrv;
+ size_t attrc;
+};
+
+struct bfcp_supprim {
+ enum bfcp_prim *primv;
+ size_t primc;
+};
+
+struct bfcp_overall_reqstat {
+ uint16_t freqid;
+ struct bfcp_reqstat reqstat;
+ char *statinfo;
+};
+
+struct bfcp_beneficiary_info {
+ uint16_t bfid;
+ char *dname;
+ char *uri;
+};
+
+struct bfcp_reqby_info {
+ uint16_t rbid;
+ char *dname;
+ char *uri;
+};
+
+struct bfcp_floor_reqstat {
+ uint16_t floorid;
+ struct bfcp_reqstat reqstat;
+ char *statinfo;
+};
+
+struct bfcp_floor_reqinfo {
+ uint16_t freqid;
+ struct bfcp_overall_reqstat ors;
+ struct bfcp_floor_reqstat *frsv;
+ size_t frsc;
+ struct bfcp_beneficiary_info bfi;
+ struct bfcp_reqby_info rbi;
+ uint8_t prio;
+ char *ppi;
+};
+
+struct bfcp_attr {
+ struct le le;
+ enum bfcp_attrib type;
+ bool mand;
+ union bfcp_union {
+ /* generic types */
+ char *str;
+ uint16_t u16;
+
+ /* actual attributes */
+ uint16_t bfid;
+ uint16_t floorid;
+ uint16_t freqid;
+ uint8_t prio;
+ struct bfcp_reqstat reqstat;
+ struct bfcp_errcode errcode;
+ char *errinfo;
+ char *ppi;
+ char *statinfo;
+ struct bfcp_supattr supattr;
+ struct bfcp_supprim supprim;
+ char *userdname;
+ char *useruri;
+
+ /* grouped attributes */
+ struct bfcp_beneficiary_info bfi;
+ struct bfcp_floor_reqinfo fri;
+ struct bfcp_reqby_info rbi;
+ struct bfcp_floor_reqstat frs;
+ struct bfcp_overall_reqstat ors;
+ } v;
+};
+
+enum bfcp_transp {
+ BFCP_TRANSP_TCP = 0,
+ BFCP_TRANSP_TLS = 1
+};
+
+
+/* BFCP Message */
+
+struct bfcp_msg;
+
+typedef bool (bfcp_attr_h)(const struct bfcp_attr *attr, void *arg);
+
+int bfcp_msg_vencode(struct mbuf *mb, enum bfcp_prim prim,
+ uint32_t confid, uint16_t tid, uint16_t userid,
+ uint32_t attrc, va_list ap);
+int bfcp_msg_encode(struct mbuf *mb, enum bfcp_prim prim, uint32_t confid,
+ uint16_t tid, uint16_t userid, uint32_t attrc, ...);
+int bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb);
+struct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg,
+ enum bfcp_attrib type);
+struct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg,
+ bfcp_attr_h *h, void *arg);
+int bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg);
+enum bfcp_prim bfcp_msg_prim(const struct bfcp_msg *msg);
+uint32_t bfcp_msg_confid(const struct bfcp_msg *msg);
+uint16_t bfcp_msg_tid(const struct bfcp_msg *msg);
+uint16_t bfcp_msg_userid(const struct bfcp_msg *msg);
+void bfcp_msg_set_src(struct bfcp_msg *msg, const struct sa *src);
+const struct sa *bfcp_msg_src(const struct bfcp_msg *msg);
+
+
+/* BFCP supplement */
+
+const char *bfcp_prim_name(enum bfcp_prim prim);
+const char *bfcp_attr_name(enum bfcp_attrib attr);
+const char *bfcp_reqstat_name(enum bfcp_rstat stat);
+const char *bfcp_errcode_name(enum bfcp_err code);
+
+
+/* BFCP Transport */
+
+bool bfcp_transp_reliable(enum bfcp_transp tp);
+const char *bfcp_transp_proto(enum bfcp_transp tp);
+
+
+/* BFCP Socket */
+
+struct tls;
+struct bfcp_sock;
+struct bfcp_ctrans;
+
+typedef void (bfcp_msg_h)(const struct bfcp_msg *msg, void *arg);
+typedef void (bfcp_resp_h)(int err, const struct bfcp_msg *msg, void *arg);
+
+
+int bfcp_listen(struct bfcp_sock **sockp, enum bfcp_transp transp,
+ struct tls *tls, const struct sa *laddr,
+ bfcp_msg_h *msgh, void *arg);
+int bfcp_request(struct bfcp_ctrans **ctp, struct bfcp_sock *sock,
+ const struct sa *dst,
+ enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+ bfcp_resp_h *resph, void *arg, uint32_t attrc, ...);
+int bfcp_reply(struct bfcp_sock *sock, const struct bfcp_msg *req,
+ enum bfcp_prim prim, uint32_t attrc, ...);
+int bfcp_ereply(struct bfcp_sock *sock, const struct bfcp_msg *req,
+ enum bfcp_err code, ...);
diff --git a/mk/symbian/bld.inf b/mk/symbian/bld.inf
index 1a735a1..fcfb8c3 100644
--- a/mk/symbian/bld.inf
+++ b/mk/symbian/bld.inf
@@ -8,6 +8,7 @@ PRJ_EXPORTS
..\..\include\re.h \epoc32\include\re\re.h
..\..\include\re_base64.h \epoc32\include\re\re_base64.h
+..\..\include\re_bfcp.h \epoc32\include\re\re_bfcp.h
..\..\include\re_bitv.h \epoc32\include\re\re_bitv.h
..\..\include\re_conf.h \epoc32\include\re\re_conf.h
..\..\include\re_crc32.h \epoc32\include\re\re_crc32.h
@@ -51,6 +52,7 @@ PRJ_EXPORTS
PRJ_MMPFILES
re.mmp
+rebfcp.mmp
redns.mmp
resdp.mmp
resip.mmp
diff --git a/mk/symbian/rebfcp.mmp b/mk/symbian/rebfcp.mmp
new file mode 100644
index 0000000..d5d9a26
--- /dev/null
+++ b/mk/symbian/rebfcp.mmp
@@ -0,0 +1,43 @@
+/**
+ * @file rebfcp.mmp Symbian makefile for libre BFCP
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+TARGET rebfcp.lib
+TARGETTYPE lib
+TARGETPATH system\libs
+UID 0x10000fd3 0x20011308
+
+#ifdef EKA2
+VENDORID 0
+CAPABILITY NetworkServices
+#endif
+
+MACRO HAVE_SYS_TIME_H
+MACRO HAVE_UNISTD_H
+MACRO HAVE_ACTSCHED
+
+#ifndef EKA2
+SOURCEPATH .
+SOURCE dll.cpp
+#endif
+
+SOURCEPATH ..\..\src\bfcp
+SOURCE attr.c
+SOURCE hdr.c
+SOURCE msg.c
+SOURCE rep.c
+SOURCE req.c
+SOURCE sock.c
+SOURCE transp.c
+
+USERINCLUDE . ..\..\include
+SYSTEMINCLUDE \epoc32\include
+SYSTEMINCLUDE \epoc32\include\libc
+SYSTEMINCLUDE ..\..\include
+#ifndef EKA2
+LIBRARY estlib.lib euser.lib
+LIBRARY esock.lib insock.lib
+#endif
+
+EXPORTUNFROZEN
diff --git a/mk/win32/re.vcproj b/mk/win32/re.vcproj
index de43786..d003d9a 100644
--- a/mk/win32/re.vcproj
+++ b/mk/win32/re.vcproj
@@ -108,6 +108,31 @@
RelativePath="..\..\src\base64\b64.c">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/bfcp/attr.c b/src/bfcp/attr.c
new file mode 100644
index 0000000..71f0a9c
--- /dev/null
+++ b/src/bfcp/attr.c
@@ -0,0 +1,763 @@
+/**
+ * @file bfcp/attr.c BFCP Attributes
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bfcp.h"
+
+
+static int attr_decode(struct bfcp_attr *attr, union bfcp_union *v,
+ struct mbuf *mb);
+
+
+static void destructor(void *arg)
+{
+ struct bfcp_attr *attr = arg;
+ size_t i;
+
+ switch (attr->type) {
+
+ case BFCP_ERROR_INFO:
+ case BFCP_PARTICIPANT_PROV_INFO:
+ case BFCP_STATUS_INFO:
+ case BFCP_USER_DISPLAY_NAME:
+ case BFCP_USER_URI:
+ mem_deref(attr->v.str);
+ break;
+
+ case BFCP_SUPPORTED_ATTRIBUTES:
+ mem_deref(attr->v.supattr.attrv);
+ break;
+
+ case BFCP_SUPPORTED_PRIMITIVES:
+ mem_deref(attr->v.supprim.primv);
+ break;
+
+ case BFCP_ERROR_CODE:
+ mem_deref(attr->v.errcode.details);
+ break;
+
+ /* grouped */
+
+ case BFCP_BENEFICIARY_INFO:
+ mem_deref(attr->v.bfi.dname);
+ mem_deref(attr->v.bfi.uri);
+ break;
+
+ case BFCP_FLOOR_REQUEST_INFO:
+ mem_deref(attr->v.fri.ors.statinfo);
+ for (i=0; iv.fri.frsc; i++)
+ mem_deref(attr->v.fri.frsv[i].statinfo);
+ mem_deref(attr->v.fri.frsv);
+ mem_deref(attr->v.fri.bfi.dname);
+ mem_deref(attr->v.fri.bfi.uri);
+ mem_deref(attr->v.fri.rbi.dname);
+ mem_deref(attr->v.fri.rbi.uri);
+ mem_deref(attr->v.fri.ppi);
+ break;
+
+ case BFCP_REQUESTED_BY_INFO:
+ mem_deref(attr->v.rbi.dname);
+ mem_deref(attr->v.rbi.uri);
+ break;
+
+ case BFCP_FLOOR_REQUEST_STATUS:
+ mem_deref(attr->v.frs.statinfo);
+ break;
+
+ case BFCP_OVERALL_REQUEST_STATUS:
+ mem_deref(attr->v.ors.statinfo);
+ break;
+
+ default:
+ /* nothing allocated */
+ break;
+ }
+}
+
+
+int bfcp_attr_encode(struct mbuf *mb, bool mand, enum bfcp_attrib type,
+ const void *v)
+{
+ const struct bfcp_errcode *ec = v;
+ const struct bfcp_supattr *sa = v;
+ const struct bfcp_supprim *sp = v;
+ const struct bfcp_reqstat *rs = v;
+ const struct bfcp_beneficiary_info *bfi = v;
+ const struct bfcp_floor_reqinfo *fri = v;
+ const struct bfcp_reqby_info *rbi = v;
+ const struct bfcp_floor_reqstat *frs = v;
+ const struct bfcp_overall_reqstat *ors = v;
+ const unsigned int *num = v;
+ size_t start, len, i;
+ int err = 0;
+
+ if (!mb || !v)
+ return EINVAL;
+
+ start = mb->pos;
+ mb->pos += ATTR_HDR_SIZE;
+
+ switch (type) {
+
+ case BFCP_BENEFICIARY_ID:
+ case BFCP_FLOOR_ID:
+ case BFCP_FLOOR_REQUEST_ID:
+ err |= mbuf_write_u16(mb, htons(*num));
+ break;
+
+ case BFCP_PRIORITY:
+ err |= mbuf_write_u8(mb, *num << 5);
+ err |= mbuf_write_u8(mb, 0x00);
+ break;
+
+ case BFCP_REQUEST_STATUS:
+ err |= mbuf_write_u8(mb, rs->stat);
+ err |= mbuf_write_u8(mb, rs->qpos);
+ break;
+
+ case BFCP_ERROR_CODE:
+ err |= mbuf_write_u8(mb, ec->code);
+ if (ec->details && ec->len)
+ err |= mbuf_write_mem(mb, ec->details, ec->len);
+ break;
+
+ case BFCP_ERROR_INFO:
+ case BFCP_PARTICIPANT_PROV_INFO:
+ case BFCP_STATUS_INFO:
+ case BFCP_USER_DISPLAY_NAME:
+ case BFCP_USER_URI:
+ err |= mbuf_write_str(mb, v);
+ break;
+
+ case BFCP_SUPPORTED_ATTRIBUTES:
+ for (i=0; iattrc; i++)
+ err |= mbuf_write_u8(mb, sa->attrv[i] << 1);
+ break;
+
+ case BFCP_SUPPORTED_PRIMITIVES:
+ for (i=0; iprimc; i++)
+ err |= mbuf_write_u8(mb, sp->primv[i]);
+ break;
+
+ /* grouped attributes: */
+
+ case BFCP_BENEFICIARY_INFO:
+ err |= mbuf_write_u16(mb, htons(bfi->bfid));
+
+ if (bfi->dname)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_USER_DISPLAY_NAME,
+ bfi->dname);
+ if (bfi->uri)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_USER_URI,
+ bfi->uri);
+ break;
+
+ case BFCP_FLOOR_REQUEST_INFO:
+ err |= mbuf_write_u16(mb, htons(fri->freqid));
+
+ if (fri->ors.freqid)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_OVERALL_REQUEST_STATUS,
+ &fri->ors);
+
+ for (i=0; ifrsc; i++) {
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_FLOOR_REQUEST_STATUS,
+ &fri->frsv[i]);
+ }
+
+ if (fri->bfi.bfid)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_BENEFICIARY_INFO,
+ &fri->bfi);
+
+ if (fri->rbi.rbid)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_REQUESTED_BY_INFO,
+ &fri->rbi);
+
+ err |= bfcp_attr_encode(mb, mand, BFCP_PRIORITY, &fri->prio);
+
+ if (fri->ppi)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_PARTICIPANT_PROV_INFO,
+ fri->ppi);
+ break;
+
+ case BFCP_REQUESTED_BY_INFO:
+ err |= mbuf_write_u16(mb, htons(rbi->rbid));
+
+ if (rbi->dname)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_USER_DISPLAY_NAME,
+ rbi->dname);
+ if (rbi->uri)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_USER_URI,
+ rbi->uri);
+ break;
+
+ case BFCP_FLOOR_REQUEST_STATUS:
+ err |= mbuf_write_u16(mb, htons(frs->floorid));
+
+ if (frs->reqstat.stat)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_REQUEST_STATUS,
+ &frs->reqstat);
+ if (frs->statinfo)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_STATUS_INFO,
+ frs->statinfo);
+ break;
+
+ case BFCP_OVERALL_REQUEST_STATUS:
+ err |= mbuf_write_u16(mb, htons(ors->freqid));
+
+ if (ors->reqstat.stat)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_REQUEST_STATUS,
+ &ors->reqstat);
+ if (ors->statinfo)
+ err |= bfcp_attr_encode(mb, mand,
+ BFCP_STATUS_INFO,
+ ors->statinfo);
+ break;
+
+ default:
+ err = EINVAL;
+ break;
+ }
+
+ if (err)
+ return err;
+
+ len = mb->pos - start;
+
+ /* padding */
+ while ((mb->pos - start) & 0x03)
+ err |= mbuf_write_u8(mb, 0x00);
+
+ if (bfcp_attr_isgrouped(type))
+ len = mb->pos - start;
+
+ /* header */
+ mb->pos = start;
+ err |= mbuf_write_u8(mb, (type<<1) | (mand&1));
+ err |= mbuf_write_u8(mb, len);
+ mb->pos = mb->end;
+
+ return err;
+}
+
+
+static int decv(struct mbuf *mb, uint8_t type, void **vp,
+ size_t elemsz, size_t *count)
+{
+ size_t n = 0;
+ void *v = NULL;
+ int err = 0;
+
+ while (mbuf_get_left(mb) >= ATTR_HDR_SIZE) {
+
+ struct bfcp_attr attr;
+ uint8_t *bp;
+
+ if (type != (mbuf_buf(mb)[0] >> 1))
+ break;
+
+ ++n;
+
+ if (v) {
+ void *v2 = mem_realloc(v, n * elemsz);
+ if (!v2) {
+ err = ENOMEM;
+ break;
+ }
+
+ v = v2;
+ }
+ else {
+ v = mem_zalloc(1 * elemsz, NULL);
+ if (!v) {
+ err = ENOMEM;
+ break;
+ }
+ }
+
+ bp = ((uint8_t *)v) + (n-1) * elemsz;
+
+ err = attr_decode(&attr, (void *)bp, mb);
+ if (err)
+ break;
+ }
+
+ if (err)
+ mem_deref(v);
+ else {
+ *vp = v;
+ *count = n;
+ }
+
+ return err;
+}
+
+
+/* Decode a Nested attribute */
+static int decn(struct mbuf *mb, uint8_t type, void *p)
+{
+ struct bfcp_attr attr;
+ int err;
+
+ /* sanity check of attribute type */
+ if (mbuf_get_left(mb) < 1 || type != (mbuf_buf(mb)[0] >> 1))
+ return 0;
+
+ err = attr_decode(&attr, p, mb);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+static int attr_decode(struct bfcp_attr *attr, union bfcp_union *v,
+ struct mbuf *mb)
+{
+ size_t i, start, len;
+ uint8_t u8;
+ int err = 0;
+
+ if (!attr || !v || !mb)
+ return EINVAL;
+
+ if (mbuf_get_left(mb) < ATTR_HDR_SIZE)
+ return EBADMSG;
+
+ start = mb->pos;
+
+ u8 = mbuf_read_u8(mb);
+ attr->type = u8 >> 1;
+ attr->mand = u8 & 1;
+ len = mbuf_read_u8(mb) - ATTR_HDR_SIZE;
+
+ if (mbuf_get_left(mb) < len)
+ goto badmsg;
+
+ 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->prio = mbuf_read_u8(mb) >> 5;
+ (void)mbuf_read_u8(mb);
+ break;
+
+ case BFCP_REQUEST_STATUS:
+ if (len < 2)
+ goto badmsg;
+
+ v->reqstat.stat = mbuf_read_u8(mb);
+ v->reqstat.qpos = mbuf_read_u8(mb);
+ break;
+
+ case BFCP_ERROR_CODE:
+ if (len < 1)
+ goto badmsg;
+
+ v->errcode.len = len - 1;
+ v->errcode.code = mbuf_read_u8(mb);
+
+ if (v->errcode.len > 0) {
+
+ 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_PARTICIPANT_PROV_INFO:
+ case BFCP_STATUS_INFO:
+ case BFCP_USER_DISPLAY_NAME:
+ case BFCP_USER_URI:
+ err = mbuf_strdup(mb, &v->str, len);
+ break;
+
+ case BFCP_SUPPORTED_ATTRIBUTES:
+ v->supattr.attrv = mem_alloc(len * sizeof(*v->supattr.attrv),
+ NULL);
+ if (!v->supattr.attrv) {
+ err = ENOMEM;
+ goto error;
+ }
+
+ v->supattr.attrc = (uint32_t)len;
+ for (i=0; isupattr.attrv[i] = mbuf_read_u8(mb) >> 1;
+ break;
+
+ case BFCP_SUPPORTED_PRIMITIVES:
+ v->supprim.primv = mem_alloc(len * sizeof(*v->supprim.primv),
+ NULL);
+ if (!v->supprim.primv) {
+ err = ENOMEM;
+ goto error;
+ }
+
+ v->supprim.primc = (uint32_t)len;
+ for (i=0; isupprim.primv[i] = mbuf_read_u8(mb);
+ break;
+
+ /* grouped attributes */
+
+ case BFCP_BENEFICIARY_INFO:
+ if (len < 2)
+ goto badmsg;
+
+ v->bfi.bfid = ntohs(mbuf_read_u16(mb));
+ err |= decn(mb, BFCP_USER_DISPLAY_NAME, &v->bfi.dname);
+ err |= decn(mb, BFCP_USER_URI, &v->bfi.uri);
+ break;
+
+ case BFCP_FLOOR_REQUEST_INFO:
+ if (len < 2)
+ goto badmsg;
+
+ v->fri.freqid = ntohs(mbuf_read_u16(mb));
+ err |= decn(mb, BFCP_OVERALL_REQUEST_STATUS, &v->fri.ors);
+ err |= decv(mb, BFCP_FLOOR_REQUEST_STATUS,
+ (void *)&v->fri.frsv, sizeof(*v->fri.frsv),
+ &v->fri.frsc);
+ err |= decn(mb, BFCP_BENEFICIARY_INFO, &v->fri.bfi);
+ err |= decn(mb, BFCP_REQUESTED_BY_INFO, &v->fri.rbi);
+ err |= decn(mb, BFCP_PRIORITY, &v->fri.prio);
+ err |= decn(mb, BFCP_PARTICIPANT_PROV_INFO, &v->fri.ppi);
+ break;
+
+ case BFCP_REQUESTED_BY_INFO:
+ if (len < 2)
+ goto badmsg;
+
+ v->rbi.rbid = ntohs(mbuf_read_u16(mb));
+ err |= decn(mb, BFCP_USER_DISPLAY_NAME, &v->rbi.dname);
+ err |= decn(mb, BFCP_USER_URI, &v->rbi.uri);
+ break;
+
+ case BFCP_FLOOR_REQUEST_STATUS:
+ if (len < 2)
+ goto badmsg;
+
+ v->frs.floorid = ntohs(mbuf_read_u16(mb));
+ err |= decn(mb, BFCP_REQUEST_STATUS, &v->frs.reqstat);
+ err |= decn(mb, BFCP_STATUS_INFO, &v->frs.statinfo);
+ break;
+
+ case BFCP_OVERALL_REQUEST_STATUS:
+ if (len < 2)
+ goto badmsg;
+
+ v->ors.freqid = ntohs(mbuf_read_u16(mb));
+ err |= decn(mb, BFCP_REQUEST_STATUS, &v->ors.reqstat);
+ err |= decn(mb, BFCP_STATUS_INFO, &v->ors.statinfo);
+ break;
+
+ default:
+ mb->pos += len;
+ (void)re_fprintf(stderr, "bfcp decode: unknown attribute %d\n",
+ attr->type);
+ break;
+ }
+
+ if (err)
+ goto error;
+
+ /* padding */
+ while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
+ ++mb->pos;
+
+ return 0;
+
+ badmsg:
+ err = EBADMSG;
+ error:
+ return err;
+}
+
+
+int bfcp_attr_decode(struct bfcp_attr **attrp, struct mbuf *mb)
+{
+ struct bfcp_attr *attr;
+ int err;
+
+ if (!attrp || !mb)
+ return EINVAL;
+
+ attr = mem_zalloc(sizeof(*attr), destructor);
+ if (!attr)
+ return ENOMEM;
+
+ err = attr_decode(attr, &attr->v, mb);
+
+ if (err)
+ mem_deref(attr);
+ else
+ *attrp = attr;
+
+ return err;
+}
+
+
+const char *bfcp_attr_name(enum bfcp_attrib attr)
+{
+ switch (attr) {
+
+ 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_PARTICIPANT_PROV_INFO: return "PARTICIPANT-PROVIDED-INFO";
+ case BFCP_STATUS_INFO: return "STATUS-INFO";
+ case BFCP_SUPPORTED_ATTRIBUTES: return "SUPPORTED-ATTRIBUTES";
+ case BFCP_SUPPORTED_PRIMITIVES: return "SUPPORTED-PRIMITIVES";
+ case BFCP_USER_DISPLAY_NAME: return "USER-DISPLAY-NAME";
+ case BFCP_USER_URI: return "USER-URI";
+ case BFCP_BENEFICIARY_INFO: return "BENEFICIARY-INFORMATION";
+ case BFCP_FLOOR_REQUEST_INFO: return "FLOOR-REQUEST-INFORMATION";
+ case BFCP_REQUESTED_BY_INFO: return "REQUESTED-BY-INFORMATION";
+ case BFCP_FLOOR_REQUEST_STATUS: return "FLOOR-REQUEST-STATUS";
+ case BFCP_OVERALL_REQUEST_STATUS: return "OVERALL-REQUEST-STATUS";
+ default: return "???";
+ }
+}
+
+
+static int leadh(struct re_printf *pf, void *arg)
+{
+ int16_t level = *(int16_t *)arg;
+ int err = 0;
+
+ while (level--)
+ err |= re_hprintf(pf, " ");
+
+ return err;
+}
+
+
+static int attr_print(int16_t level, struct re_printf *pf,
+ uint8_t type, const void *p)
+{
+ const union bfcp_union *v = p;
+ uint32_t i;
+ int err = 0;
+
+ if (!v)
+ return EINVAL;
+
+ ++level;
+
+ err |= re_hprintf(pf, "%H%-28s", leadh, &level, bfcp_attr_name(type));
+
+ if (bfcp_attr_isgrouped(type)) {
+ const uint16_t level2 = level + 1;
+ err |= re_hprintf(pf, "\n%H{\n%H", leadh, &level,
+ leadh, &level2);
+ }
+
+ switch (type) {
+
+ case BFCP_BENEFICIARY_ID:
+ case BFCP_FLOOR_ID:
+ case BFCP_FLOOR_REQUEST_ID:
+ case BFCP_PRIORITY:
+ err |= re_hprintf(pf, "%u", v->u16);
+ break;
+
+ case BFCP_REQUEST_STATUS:
+ err |= re_hprintf(pf, "%s (%d), qpos=%u",
+ bfcp_reqstat_name(v->reqstat.stat),
+ v->reqstat.stat,
+ v->reqstat.qpos);
+ break;
+
+ case BFCP_ERROR_CODE:
+ err |= re_hprintf(pf, "%u (%s)", v->errcode.code,
+ bfcp_errcode_name(v->errcode.code));
+ if (v->errcode.code == BFCP_ERR_UNKNOWN_MAND_ATTR) {
+ for (i=0; ierrcode.len; i++) {
+ uint8_t t = v->errcode.details[i] >> 1;
+ err |= re_hprintf(pf, " %s",
+ bfcp_attr_name(t));
+ }
+ }
+ break;
+
+ case BFCP_ERROR_INFO:
+ case BFCP_PARTICIPANT_PROV_INFO:
+ case BFCP_STATUS_INFO:
+ case BFCP_USER_DISPLAY_NAME:
+ case BFCP_USER_URI:
+ err |= re_hprintf(pf, "\"%s\"", v->str);
+ break;
+
+ case BFCP_SUPPORTED_ATTRIBUTES:
+ err |= re_hprintf(pf, "%u:", v->supattr.attrc);
+ for (i=0; isupattr.attrc; i++) {
+ const enum bfcp_attrib attr = v->supattr.attrv[i];
+ err |= re_hprintf(pf, " %s", bfcp_attr_name(attr));
+ }
+ break;
+
+ case BFCP_SUPPORTED_PRIMITIVES:
+ err |= re_hprintf(pf, "%u:", v->supprim.primc);
+ for (i=0; isupprim.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, "bfid=%u\n", v->bfi.bfid);
+ err |= attr_print(level, pf, BFCP_USER_DISPLAY_NAME,
+ &v->bfi.dname);
+ err |= attr_print(level, pf, BFCP_USER_URI, &v->bfi.uri);
+ break;
+
+ case BFCP_FLOOR_REQUEST_INFO:
+ err |= re_hprintf(pf, "freqid=%u\n", v->fri.freqid);
+ err |= attr_print(level, pf, BFCP_OVERALL_REQUEST_STATUS,
+ &v->fri.ors);
+ for (i=0; ifri.frsc; i++) {
+ err |= attr_print(level, pf, BFCP_FLOOR_REQUEST_STATUS,
+ &v->fri.frsv[i]);
+ }
+ err |= attr_print(level, pf, BFCP_BENEFICIARY_INFO,
+ &v->fri.bfi);
+ err |= attr_print(level, pf, BFCP_REQUESTED_BY_INFO,
+ &v->fri.rbi);
+ err |= attr_print(level, pf, BFCP_PRIORITY, &v->fri.prio);
+ err |= attr_print(level, pf, BFCP_PARTICIPANT_PROV_INFO,
+ &v->fri.ppi);
+ break;
+
+ case BFCP_REQUESTED_BY_INFO:
+ err |= re_hprintf(pf, "rbid=%u\n", v->rbi.rbid);
+ err |= attr_print(level, pf, BFCP_USER_DISPLAY_NAME,
+ &v->rbi.dname);
+ err |= attr_print(level, pf, BFCP_USER_URI, &v->rbi.uri);
+ break;
+
+ case BFCP_FLOOR_REQUEST_STATUS:
+ err |= re_hprintf(pf, "floorid=%u\n", v->frs.floorid);
+ err |= attr_print(level, pf, BFCP_REQUEST_STATUS,
+ &v->frs.reqstat);
+ err |= attr_print(level, pf, BFCP_STATUS_INFO,
+ &v->frs.statinfo);
+ break;
+
+ case BFCP_OVERALL_REQUEST_STATUS:
+ err |= re_hprintf(pf, "freqid=%u\n", v->ors.freqid);
+ err |= attr_print(level, pf, BFCP_REQUEST_STATUS,
+ &v->ors.reqstat);
+ err |= attr_print(level, pf, BFCP_STATUS_INFO,
+ &v->ors.statinfo);
+ break;
+
+ default:
+ err |= re_hprintf(pf, "?");
+ break;
+ }
+
+ if (bfcp_attr_isgrouped(type))
+ err |= re_hprintf(pf, "%H}", leadh, &level);
+
+ err |= re_hprintf(pf, "\n");
+
+ return err;
+}
+
+
+int bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *a)
+{
+ if (!a)
+ return 0;
+
+ return attr_print(0, pf, a->type, &a->v);
+}
+
+
+bool bfcp_attr_isgrouped(enum bfcp_attrib attr)
+{
+ switch (attr) {
+
+ case BFCP_BENEFICIARY_INFO:
+ case BFCP_FLOOR_REQUEST_INFO:
+ case BFCP_REQUESTED_BY_INFO:
+ case BFCP_FLOOR_REQUEST_STATUS:
+ case BFCP_OVERALL_REQUEST_STATUS:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+
+const char *bfcp_errcode_name(enum bfcp_err code)
+{
+ switch (code) {
+
+ case BFCP_ERR_CONF_NOT_EXIST:
+ return "Conference does not Exist";
+ case BFCP_ERR_USER_NOT_EXIST:
+ return "User does not Exist";
+ case BFCP_ERR_UNKNOWN_PRIM:
+ return "Unknown Primitive";
+ case BFCP_ERR_UNKNOWN_MAND_ATTR:
+ return "Unknown Mandatory Attribute";
+ case BFCP_ERR_UNAUTH_OPERATION:
+ return "Unauthorized Operation";
+ case BFCP_ERR_INVALID_FLOOR_ID:
+ return "Invalid Floor ID";
+ case BFCP_ERR_FLOOR_REQ_ID_NOT_EXIST:
+ return "Floor Request ID Does Not Exist";
+ case BFCP_ERR_MAX_FLOOR_REQ_REACHED:
+ return "You have Already Reached the Maximum Number"
+ " of Ongoing Floor Requests for this Floor";
+ case BFCP_ERR_USE_TLS:
+ return "Use TLS";
+ default:
+ return "???";
+ }
+}
diff --git a/src/bfcp/bfcp.h b/src/bfcp/bfcp.h
new file mode 100644
index 0000000..bc0cbed
--- /dev/null
+++ b/src/bfcp/bfcp.h
@@ -0,0 +1,60 @@
+/**
+ * @file bfcp.h Internal interface to Binary Floor Control Protocol (BFCP)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+/* header */
+enum {
+ BFCP_HDR_SIZE = 12,
+ ATTR_HDR_SIZE = 2
+};
+
+
+struct bfcp_hdr {
+ uint8_t ver;
+ unsigned i:1;
+ enum bfcp_prim prim;
+ uint16_t len;
+ uint32_t confid;
+ uint16_t tid;
+ uint16_t userid;
+};
+
+int bfcp_hdr_encode(struct mbuf *mb, enum bfcp_prim prim, uint16_t len,
+ uint32_t confid, uint16_t tid, uint16_t userid);
+int bfcp_hdr_decode(struct mbuf *mb, struct bfcp_hdr *hdr);
+
+
+/* attributes */
+int bfcp_attr_encode(struct mbuf *mb, bool mand, enum bfcp_attrib type,
+ const void *v);
+int bfcp_attr_decode(struct bfcp_attr **attrp, struct mbuf *mb);
+int bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *a);
+bool bfcp_attr_isgrouped(enum bfcp_attrib attr);
+
+
+/* socket */
+
+struct bfcp_sock {
+ struct list transl;
+ struct list connl;
+ struct tcp_sock *ts;
+ struct tls *tls;
+ enum bfcp_transp transp;
+ uint16_t tidc;
+ bool active;
+ bfcp_msg_h *msgh;
+ void *arg;
+};
+
+
+int bfcp_send(struct bfcp_sock *sock, const struct sa *dst, struct mbuf *mb);
+
+
+/* ctrans request */
+
+void bfcp_ctrans_completed(struct bfcp_ctrans *ct, int err,
+ const struct bfcp_msg *msg);
+struct bfcp_ctrans *bfcp_ctrans_find(struct bfcp_sock *sock, uint16_t tid);
diff --git a/src/bfcp/hdr.c b/src/bfcp/hdr.c
new file mode 100644
index 0000000..13c2350
--- /dev/null
+++ b/src/bfcp/hdr.c
@@ -0,0 +1,57 @@
+/**
+ * @file hdr.c BFCP Message header
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bfcp.h"
+
+
+int bfcp_hdr_encode(struct mbuf *mb, enum bfcp_prim prim, uint16_t len,
+ uint32_t confid, uint16_t tid, uint16_t userid)
+{
+ int err;
+
+ err = mbuf_write_u8(mb, BFCP_VERSION << 5);
+ 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;
+}
+
+
+int bfcp_hdr_decode(struct mbuf *mb, struct bfcp_hdr *hdr)
+{
+ uint8_t b;
+
+ if (mbuf_get_left(mb) < BFCP_HDR_SIZE)
+ return EBADMSG;
+
+ b = mbuf_read_u8(mb);
+ hdr->ver = b >> 5;
+ hdr->i = (b >> 4) & 1;
+ hdr->prim = mbuf_read_u8(mb);
+ hdr->len = ntohs(mbuf_read_u16(mb));
+ hdr->confid = ntohl(mbuf_read_u32(mb));
+ hdr->tid = ntohs(mbuf_read_u16(mb));
+ hdr->userid = ntohs(mbuf_read_u16(mb));
+
+ if (hdr->ver != BFCP_VERSION)
+ return EBADMSG;
+
+ if (mbuf_get_left(mb) < (size_t)(hdr->len*4)) {
+
+ return ENODATA;
+ }
+
+ return 0;
+}
diff --git a/src/bfcp/mod.mk b/src/bfcp/mod.mk
new file mode 100644
index 0000000..f46e564
--- /dev/null
+++ b/src/bfcp/mod.mk
@@ -0,0 +1,13 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS += bfcp/attr.c
+SRCS += bfcp/hdr.c
+SRCS += bfcp/msg.c
+SRCS += bfcp/rep.c
+SRCS += bfcp/req.c
+SRCS += bfcp/sock.c
+SRCS += bfcp/transp.c
diff --git a/src/bfcp/msg.c b/src/bfcp/msg.c
new file mode 100644
index 0000000..c931b68
--- /dev/null
+++ b/src/bfcp/msg.c
@@ -0,0 +1,260 @@
+/**
+ * @file bfcp/msg.c BFCP Message
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bfcp.h"
+
+
+struct bfcp_msg {
+ struct sa src;
+ struct bfcp_hdr hdr;
+ struct list attrl;
+};
+
+
+static void destructor(void *arg)
+{
+ struct bfcp_msg *msg = arg;
+
+ list_flush(&msg->attrl);
+}
+
+
+int bfcp_msg_vencode(struct mbuf *mb, enum bfcp_prim prim,
+ uint32_t confid, uint16_t tid, uint16_t userid,
+ uint32_t attrc, va_list ap)
+{
+ size_t start;
+ uint32_t i;
+ uint16_t len;
+ int err = 0;
+
+ if (!mb)
+ return EINVAL;
+
+ start = mb->pos;
+ mb->pos += BFCP_HDR_SIZE;
+
+ for (i=0; ipos - start - BFCP_HDR_SIZE) / 4;
+ mb->pos = start;
+ err = bfcp_hdr_encode(mb, prim, len, confid, tid, userid);
+
+ mb->pos = mb->end;
+
+ return err;
+}
+
+
+int bfcp_msg_encode(struct mbuf *mb, enum bfcp_prim prim, uint32_t confid,
+ uint16_t tid, uint16_t userid, uint32_t attrc, ...)
+{
+ va_list ap;
+ int err;
+
+ va_start(ap, attrc);
+ err = bfcp_msg_vencode(mb, prim, confid, tid, userid, attrc, ap);
+ va_end(ap);
+
+ return err;
+}
+
+
+int bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb)
+{
+ struct bfcp_msg *msg;
+ size_t start, extra;
+ int err;
+
+ if (!msgp || !mb)
+ return EINVAL;
+
+ start = mb->pos;
+
+ msg = mem_zalloc(sizeof(*msg), destructor);
+ if (!msg)
+ return ENOMEM;
+
+ err = bfcp_hdr_decode(mb, &msg->hdr);
+ if (err) {
+ mb->pos = start;
+ goto out;
+ }
+
+ extra = mbuf_get_left(mb) - 4*msg->hdr.len;
+
+ while (mbuf_get_left(mb) - extra >= ATTR_HDR_SIZE) {
+
+ struct bfcp_attr *attr;
+
+ err = bfcp_attr_decode(&attr, mb);
+ if (err)
+ break;
+
+ list_append(&msg->attrl, &attr->le, attr);
+ }
+
+ out:
+ if (err)
+ mem_deref(msg);
+ else
+ *msgp = msg;
+
+ return err;
+}
+
+
+static bool attr_match(const struct bfcp_attr *attr, void *arg)
+{
+ return attr->type == *(uint8_t *)arg;
+}
+
+
+struct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg,
+ enum bfcp_attrib type)
+{
+ return bfcp_msg_attr_apply(msg, attr_match, &type);
+}
+
+
+struct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg,
+ bfcp_attr_h *h, void *arg)
+{
+ struct le *le = msg ? list_head(&msg->attrl) : NULL;
+
+ while (le) {
+ struct bfcp_attr *attr = le->data;
+
+ le = le->next;
+
+ if (h && h(attr, arg))
+ return attr;
+ }
+
+ return NULL;
+}
+
+
+static bool attr_print(const struct bfcp_attr *attr, void *arg)
+{
+ struct re_printf *pf = arg;
+
+ return 0 != bfcp_attr_print(pf, attr);
+}
+
+
+int bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg)
+{
+ int err;
+
+ if (!msg)
+ return 0;
+
+ err = re_hprintf(pf, "%s (len=%u confid=%u tid=%u userid=%u)\n",
+ bfcp_prim_name(msg->hdr.prim), msg->hdr.len,
+ msg->hdr.confid, msg->hdr.tid, msg->hdr.userid);
+
+ bfcp_msg_attr_apply(msg, attr_print, pf);
+
+ return err;
+}
+
+
+enum bfcp_prim bfcp_msg_prim(const struct bfcp_msg *msg)
+{
+ return msg ? msg->hdr.prim : 0;
+}
+
+
+uint32_t bfcp_msg_confid(const struct bfcp_msg *msg)
+{
+ return msg ? msg->hdr.confid : 0;
+}
+
+
+uint16_t bfcp_msg_tid(const struct bfcp_msg *msg)
+{
+ return msg ? msg->hdr.tid : 0;
+}
+
+
+uint16_t bfcp_msg_userid(const struct bfcp_msg *msg)
+{
+ return msg ? msg->hdr.userid : 0;
+}
+
+
+const char *bfcp_reqstat_name(enum bfcp_rstat stat)
+{
+ switch (stat) {
+
+ 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 "???";
+ }
+}
+
+
+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_STAT: 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";
+ default: return "???";
+ }
+}
+
+
+void bfcp_msg_set_src(struct bfcp_msg *msg, const struct sa *src)
+{
+ if (!msg || !src)
+ return;
+
+ msg->src = *src;
+}
+
+
+const struct sa *bfcp_msg_src(const struct bfcp_msg *msg)
+{
+ return msg ? &msg->src : NULL;
+}
diff --git a/src/bfcp/rep.c b/src/bfcp/rep.c
new file mode 100644
index 0000000..f296bfa
--- /dev/null
+++ b/src/bfcp/rep.c
@@ -0,0 +1,70 @@
+/**
+ * @file bfcp/rep.c BFCP Reply
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bfcp.h"
+
+
+int bfcp_reply(struct bfcp_sock *sock, const struct bfcp_msg *req,
+ enum bfcp_prim prim, uint32_t attrc, ...)
+{
+ struct mbuf *mb;
+ va_list ap;
+ int err;
+
+ if (!sock || !req)
+ return EINVAL;
+
+ mb = mbuf_alloc(64);
+ if (!mb)
+ return ENOMEM;
+
+ va_start(ap, attrc);
+ err = bfcp_msg_vencode(mb, prim, bfcp_msg_confid(req),
+ bfcp_msg_tid(req), bfcp_msg_userid(req),
+ attrc, ap);
+ va_end(ap);
+
+ if (err)
+ goto out;
+
+ mb->pos = 0;
+
+ err = bfcp_send(sock, bfcp_msg_src(req), mb);
+
+ out:
+ mem_deref(mb);
+
+ return err;
+}
+
+
+int bfcp_ereply(struct bfcp_sock *sock, const struct bfcp_msg *req,
+ enum bfcp_err code, ...)
+{
+ struct bfcp_errcode ec;
+ va_list ap;
+
+ va_start(ap, code);
+
+ memset(&ec, 0, sizeof(ec));
+ ec.code = code;
+
+ if (code == BFCP_ERR_UNKNOWN_MAND_ATTR) {
+ ec.details = va_arg(ap, uint8_t *);
+ ec.len = va_arg(ap, size_t);
+ }
+
+ va_end(ap);
+
+ return bfcp_reply(sock, req, BFCP_ERROR, 1, BFCP_ERROR_CODE, &ec);
+}
diff --git a/src/bfcp/req.c b/src/bfcp/req.c
new file mode 100644
index 0000000..d464a01
--- /dev/null
+++ b/src/bfcp/req.c
@@ -0,0 +1,156 @@
+/**
+ * @file bfcp/req.c BFCP Client request
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bfcp.h"
+
+
+struct bfcp_ctrans {
+ struct le le;
+ struct tmr tmr;
+ struct bfcp_ctrans **ctp;
+ uint16_t tid;
+ bfcp_resp_h *resph;
+ void *arg;
+};
+
+
+static void destructor(void *arg)
+{
+ struct bfcp_ctrans *ct = arg;
+
+ list_unlink(&ct->le);
+ tmr_cancel(&ct->tmr);
+}
+
+
+static void timeout(void *arg)
+{
+ struct bfcp_ctrans *ct = arg;
+
+ bfcp_ctrans_completed(ct, ETIMEDOUT, NULL);
+}
+
+
+static struct bfcp_ctrans *ctrans_new(struct bfcp_sock *sock,
+ bfcp_resp_h *resph, void *arg)
+{
+ struct bfcp_ctrans *ct;
+
+ ct = mem_zalloc(sizeof(*ct), destructor);
+ if (!ct)
+ return NULL;
+
+ list_append(&sock->transl, &ct->le, ct);
+
+ sock->tidc++;
+
+ if (sock->tidc == 0)
+ sock->tidc++;
+
+ ct->tid = sock->tidc;
+ ct->resph = resph;
+ ct->arg = arg;
+
+ tmr_start(&ct->tmr, 10000, timeout, ct);
+
+ return ct;
+}
+
+
+void bfcp_ctrans_completed(struct bfcp_ctrans *ct, int err,
+ const struct bfcp_msg *msg)
+{
+ bfcp_resp_h *resph = ct->resph;
+ void *arg = ct->arg;
+
+ list_unlink(&ct->le);
+ tmr_cancel(&ct->tmr);
+
+ if (ct->ctp) {
+ *ct->ctp = NULL;
+ ct->ctp = NULL;
+ }
+
+ ct->resph = NULL;
+
+ mem_deref(ct);
+
+ if (resph)
+ resph(err, msg, arg);
+}
+
+
+struct bfcp_ctrans *bfcp_ctrans_find(struct bfcp_sock *sock, uint16_t tid)
+{
+ struct le *le;
+
+ for (le = sock->transl.head; le; le = le->next) {
+
+ struct bfcp_ctrans *ct = le->data;
+
+ if (ct->tid == tid)
+ return ct;
+ }
+
+ return NULL;
+}
+
+
+int bfcp_request(struct bfcp_ctrans **ctp, struct bfcp_sock *sock,
+ const struct sa *dst,
+ enum bfcp_prim prim, uint32_t confid, uint16_t userid,
+ bfcp_resp_h *resph, void *arg, uint32_t attrc, ...)
+{
+ struct bfcp_ctrans *ct;
+ struct mbuf *mb;
+ va_list ap;
+ int err;
+
+ if (!sock || !dst || !confid || !userid)
+ return EINVAL;
+
+ ct = ctrans_new(sock, resph, arg);
+ if (!ct)
+ return ENOMEM;
+
+ mb = mbuf_alloc(512);
+ if (!mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ va_start(ap, attrc);
+ err = bfcp_msg_vencode(mb, prim, confid, ct->tid, userid,
+ attrc, ap);
+ va_end(ap);
+ if (err)
+ goto out;
+
+ mb->pos = 0;
+
+ err = bfcp_send(sock, dst, mb);
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(ct);
+ else if (ctp) {
+ ct->ctp = ctp;
+ *ctp = ct;
+ }
+
+ mem_deref(mb);
+ return err;
+}
diff --git a/src/bfcp/sock.c b/src/bfcp/sock.c
new file mode 100644
index 0000000..d235052
--- /dev/null
+++ b/src/bfcp/sock.c
@@ -0,0 +1,343 @@
+/**
+ * @file bfcp/sock.c BFCP Socket
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bfcp.h"
+
+
+struct bfcp_conn {
+ struct le le;
+ struct sa paddr;
+ struct mbuf *mbtx;
+ struct mbuf *mbrx;
+ struct tcp_conn *tc;
+ struct tls_conn *sc;
+ struct bfcp_sock *bs;
+ bool established;
+};
+
+
+static void destructor(void *arg)
+{
+ struct bfcp_sock *sock = arg;
+
+ list_flush(&sock->transl);
+ list_flush(&sock->connl);
+ mem_deref(sock->tls);
+ mem_deref(sock->ts);
+}
+
+
+static void conn_destructor(void *arg)
+{
+ struct bfcp_conn *conn = arg;
+
+ list_unlink(&conn->le);
+ mem_deref(conn->mbtx);
+ mem_deref(conn->mbrx);
+ mem_deref(conn->sc);
+ mem_deref(conn->tc);
+}
+
+
+static struct bfcp_conn *conn_add(struct bfcp_sock *bs,
+ const struct sa *paddr)
+{
+ struct bfcp_conn *bc = mem_zalloc(sizeof(*bc), conn_destructor);
+ if (!bc)
+ return NULL;
+
+ list_append(&bs->connl, &bc->le, bc);
+
+ bc->bs = bs;
+ bc->paddr = *paddr;
+
+ return bc;
+}
+
+
+static void tcp_estab_handler(void *arg)
+{
+ struct bfcp_conn *conn = arg;
+
+#ifdef USE_TLS
+ if (conn->sc) {
+ char cn[256];
+ int err;
+
+ err = tls_verify_cert(conn->sc, cn, sizeof(cn));
+
+ (void)re_printf("CN: '%s' (%sverified)\n",
+ cn, err ? "not " : "");
+ }
+#endif
+
+ conn->established = true;
+
+ /* flush transmit buffer */
+ if (conn->mbtx) {
+
+ conn->mbtx->pos = 0;
+ if (tcp_send(conn->tc, conn->mbtx))
+ return;
+
+ conn->mbtx = mem_deref(conn->mbtx);
+ }
+}
+
+
+static void tcp_recv_handler(struct mbuf *mb, void *arg)
+{
+ struct bfcp_conn *conn = arg;
+ size_t pos;
+ int err = 0;
+
+ if (conn->mbrx) {
+ pos = conn->mbrx->pos;
+
+ conn->mbrx->pos = conn->mbrx->end;
+
+ err = mbuf_write_mem(conn->mbrx,
+ mbuf_buf(mb), mbuf_get_left(mb));
+ if (err)
+ goto out;
+
+ conn->mbrx->pos = pos;
+ }
+ else {
+ conn->mbrx = mem_ref(mb);
+ }
+
+ for (;;) {
+ struct bfcp_msg *msg;
+ struct bfcp_ctrans *ct;
+
+ pos = conn->mbrx->pos;
+
+ err = bfcp_msg_decode(&msg, conn->mbrx);
+ if (err) {
+ if (err == ENODATA) {
+ conn->mbrx->pos = pos;
+ err = 0;
+ }
+ break;
+ }
+
+ bfcp_msg_set_src(msg, &conn->paddr);
+
+ ct = bfcp_ctrans_find(conn->bs, bfcp_msg_tid(msg));
+ if (ct) {
+ bfcp_ctrans_completed(ct, 0, msg);
+ }
+ else {
+ if (conn->bs->msgh)
+ conn->bs->msgh(msg, conn->bs->arg);
+ }
+
+ mem_deref(msg);
+
+ if (0 == mbuf_get_left(conn->mbrx)) {
+ conn->mbrx = mem_deref(conn->mbrx);
+ break;
+ }
+ }
+
+ out:
+ if (err)
+ mem_deref(conn);
+}
+
+
+static void tcp_close_handler(int err, void *arg)
+{
+ struct bfcp_conn *conn = arg;
+
+ (void)re_printf("BFCP connection closed: %s\n", strerror(err));
+
+ mem_deref(conn);
+}
+
+
+static void tcp_conn_handler(const struct sa *addr, void *arg)
+{
+ struct bfcp_sock *bs = arg;
+ struct bfcp_conn *conn;
+
+ (void)re_printf("bfcpd: Connection from %J via %s\n", addr,
+ bs->transp == BFCP_TRANSP_TLS ? "TLS" : "TCP");
+
+ conn = conn_add(bs, addr);
+ if (conn) {
+ if (tcp_accept(&conn->tc, bs->ts, tcp_estab_handler,
+ tcp_recv_handler, tcp_close_handler, conn))
+ goto error;
+
+#ifdef USE_TLS
+ if (bs->transp == BFCP_TRANSP_TLS) {
+ if (tls_start_tcp(&conn->sc, bs->tls, conn->tc))
+ goto error;
+ }
+#endif
+
+ return;
+ }
+
+ error:
+ tcp_reject(bs->ts);
+ mem_deref(conn);
+}
+
+
+static struct bfcp_conn *findconn(const struct bfcp_sock *bs,
+ const struct sa *peer)
+{
+ struct le *le;
+
+ for (le = bs->connl.head; le; le = le->next) {
+
+ struct bfcp_conn *bc = le->data;
+
+ if (sa_cmp(&bc->paddr, peer, SA_ALL))
+ return bc;
+ }
+
+ return NULL;
+}
+
+
+int bfcp_listen(struct bfcp_sock **sockp, enum bfcp_transp transp,
+ struct tls *tls, const struct sa *laddr,
+ bfcp_msg_h *msgh, void *arg)
+{
+ struct bfcp_sock *sock;
+ int err = 0;
+
+ if (!sockp)
+ return EINVAL;
+
+ sock = mem_zalloc(sizeof(*sock), destructor);
+ if (!sock)
+ return ENOMEM;
+
+ sock->transp = transp;
+ sock->tls = mem_ref(tls);
+ sock->msgh = msgh;
+ sock->arg = arg;
+
+ /* Server */
+ if (laddr) {
+ switch (transp) {
+
+ case BFCP_TRANSP_TLS:
+ if (!tls) {
+ err = EINVAL;
+ goto out;
+ }
+ /*@fallthrough@*/
+
+ case BFCP_TRANSP_TCP:
+ sock->active = false;
+ err = tcp_listen(&sock->ts, laddr, tcp_conn_handler,
+ sock);
+ break;
+
+ default:
+ err = EPROTONOSUPPORT;
+ break;
+ }
+ }
+ else {
+ sock->active = true;
+ }
+
+ out:
+ if (err)
+ mem_deref(sock);
+ else if (sockp)
+ *sockp = sock;
+
+ return err;
+}
+
+
+int bfcp_send(struct bfcp_sock *sock, const struct sa *dst, struct mbuf *mb)
+{
+ struct bfcp_conn *conn = NULL;
+ int err = 0;
+
+ if (!sock || !dst || !mb)
+ return EINVAL;
+
+ switch (sock->transp) {
+
+ case BFCP_TRANSP_TCP:
+ case BFCP_TRANSP_TLS:
+
+ conn = findconn(sock, dst);
+
+ if (!conn) {
+
+ if (!sock->active)
+ return ENOTCONN;
+
+ conn = conn_add(sock, dst);
+ if (!conn) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ err = tcp_connect(&conn->tc, dst, tcp_estab_handler,
+ tcp_recv_handler,
+ tcp_close_handler, conn);
+ if (err)
+ goto out;
+
+#ifdef USE_TLS
+ if (sock->transp == BFCP_TRANSP_TLS) {
+
+ err = tls_start_tcp(&conn->sc, sock->tls,
+ conn->tc);
+ if (err)
+ goto out;
+ }
+#endif
+ }
+
+ if (conn->established) {
+ err = tcp_send(conn->tc, mb);
+ }
+ else {
+ if (!conn->mbtx) {
+ conn->mbtx = mem_ref(mb);
+ }
+ else {
+ conn->mbtx->pos = conn->mbtx->end;
+ err = mbuf_write_mem(conn->mbtx, mbuf_buf(mb),
+ mbuf_get_left(mb));
+ }
+ }
+ break;
+
+ default:
+ err = EPROTONOSUPPORT;
+ break;
+ }
+
+ out:
+ if (err)
+ mem_deref(conn);
+
+ return err;
+}
diff --git a/src/bfcp/transp.c b/src/bfcp/transp.c
new file mode 100644
index 0000000..a561705
--- /dev/null
+++ b/src/bfcp/transp.c
@@ -0,0 +1,38 @@
+/**
+ * @file transp.c BFCP Transport
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bfcp.h"
+
+
+bool bfcp_transp_reliable(enum bfcp_transp tp)
+{
+ switch (tp) {
+
+ case BFCP_TRANSP_TCP: return true;
+ case BFCP_TRANSP_TLS: return true;
+ default: return false;
+ }
+}
+
+
+const char *bfcp_transp_proto(enum bfcp_transp tp)
+{
+ switch (tp) {
+
+ case BFCP_TRANSP_TCP: return "TCP/BFCP";
+ case BFCP_TRANSP_TLS: return "TCP/TLS/BFCP";
+ default: return "???";
+ }
+}