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/uri/mod.mk b/src/uri/mod.mk
new file mode 100644
index 0000000..4acb507
--- /dev/null
+++ b/src/uri/mod.mk
@@ -0,0 +1,9 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS	+= uri/uri.c
+SRCS	+= uri/ucmp.c
+SRCS	+= uri/uric.c
diff --git a/src/uri/ucmp.c b/src/uri/ucmp.c
new file mode 100644
index 0000000..7112212
--- /dev/null
+++ b/src/uri/ucmp.c
@@ -0,0 +1,110 @@
+/**
+ * @file ucmp.c  URI comparison
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_uri.h>
+
+
+static int param_handler(const struct pl *pname, const struct pl *pvalue,
+			 void *arg)
+{
+	struct pl *other_params = arg;
+	struct pl other_pvalue = PL_INIT;
+	bool both;
+
+	if (0 == pl_strcmp(pname, "user"))
+		both = true;
+	else if (0 == pl_strcmp(pname, "ttl"))
+		both = true;
+	else if (0 == pl_strcmp(pname, "method"))
+		both = true;
+	else if (0 == pl_strcmp(pname, "maddr"))
+		both = true;
+	else if (0 == pl_strcmp(pname, "transport"))
+		both = true;
+	else
+		both = false;
+
+	if (uri_param_get(other_params, pname, &other_pvalue))
+		return both ? ENOENT : 0;
+
+	return pl_casecmp(pvalue, &other_pvalue);
+}
+
+
+static int header_handler(const struct pl *hname, const struct pl *hvalue,
+			  void *arg)
+{
+	struct pl *other_headers = arg;
+	struct pl other_hvalue;
+	int err;
+
+	err = uri_header_get(other_headers, hname, &other_hvalue);
+	if (err)
+		return err;
+
+	return pl_casecmp(hvalue, &other_hvalue);
+}
+
+
+/**
+ * Compare two URIs - see RFC 3261 Section 19.1.4
+ *
+ * @param l  Left-hand URI object
+ * @param r  Right-hand URI object
+ *
+ * @return true if match, otherwise false
+ */
+bool uri_cmp(const struct uri *l, const struct uri *r)
+{
+	int err;
+
+	if (!l || !r)
+		return false;
+
+	if (l == r)
+		return true;
+
+	/* A SIP and SIPS URI are never equivalent. */
+	if (pl_casecmp(&l->scheme, &r->scheme))
+		return false;
+
+	/* Comparison of the userinfo of SIP and SIPS URIs is case-sensitive */
+	if (pl_cmp(&l->user, &r->user))
+		return false;
+
+	if (pl_cmp(&l->password, &r->password))
+		return false;
+
+	if (pl_casecmp(&l->host, &r->host))
+		return false;
+	if (l->af != r->af)
+		return false;
+
+	if (l->port != r->port)
+		return false;
+
+	/* URI parameters */
+	err = uri_params_apply(&l->params, param_handler, (void *)&r->params);
+	if (err)
+		return false;
+	err = uri_params_apply(&r->params, param_handler, (void *)&l->params);
+	if (err)
+		return false;
+
+	/* URI headers */
+	err = uri_headers_apply(&l->headers, header_handler,
+				(void *)&r->headers);
+	if (err)
+		return false;
+	err = uri_headers_apply(&r->headers, header_handler,
+				(void *)&l->headers);
+	if (err)
+		return false;
+
+	/* Match */
+	return true;
+}
diff --git a/src/uri/uri.c b/src/uri/uri.c
new file mode 100644
index 0000000..5409463
--- /dev/null
+++ b/src/uri/uri.c
@@ -0,0 +1,270 @@
+/**
+ * @file uri.c  Uniform Resource Identifier (URI) module
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_uri.h>
+
+
+/**
+ * Encode a URI object
+ *
+ * @param pf  Print function to encode into
+ * @param uri URI object
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_encode(struct re_printf *pf, const struct uri *uri)
+{
+	int err;
+
+	if (!uri)
+		return 0;
+
+	if (!pl_isset(&uri->scheme) || !pl_isset(&uri->host))
+		return EINVAL;
+
+	err = re_hprintf(pf, "%r:", &uri->scheme);
+	if (err)
+		return err;
+
+	if (pl_isset(&uri->user)) {
+		err = re_hprintf(pf, "%r", &uri->user);
+
+		if (pl_isset(&uri->password))
+			err |= re_hprintf(pf, ":%r", &uri->password);
+
+		err |= pf->vph("@", 1, pf->arg);
+
+		if (err)
+			return err;
+	}
+
+	/* The IPv6 address is delimited by '[' and ']' */
+	switch (uri->af) {
+
+#ifdef HAVE_INET6
+	case AF_INET6:
+		err = re_hprintf(pf, "[%r]", &uri->host);
+		break;
+#endif
+
+	default:
+		err = re_hprintf(pf, "%r", &uri->host);
+		break;
+	}
+	if (err)
+		return err;
+
+	if (uri->port)
+		err = re_hprintf(pf, ":%u", uri->port);
+
+	err |= re_hprintf(pf, "%r%r", &uri->params, &uri->headers);
+
+	return err;
+}
+
+
+/**
+ * Decode host-port portion of a URI (if present)
+ *
+ * @param hostport Host and port input string
+ * @param host     Decoded host portion
+ * @param port     Decoded port portion
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_decode_hostport(const struct pl *hostport, struct pl *host,
+			struct pl *port)
+{
+	if (!hostport || !host || !port)
+		return EINVAL;
+
+	/* Try IPv6 first */
+	if (!re_regex(hostport->p, hostport->l, "\\[[0-9a-f:]+\\][:]*[0-9]*",
+		      host, NULL, port))
+		return 0;
+
+	/* Then non-IPv6 host */
+	return re_regex(hostport->p, hostport->l, "[^:]+[:]*[0-9]*",
+			host, NULL, port);
+}
+
+
+/**
+ * Decode a pointer-length object into a URI object
+ *
+ * @param uri  URI object
+ * @param pl   Pointer-length object to decode from
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_decode(struct uri *uri, const struct pl *pl)
+{
+	struct sa addr;
+	struct pl port = PL_INIT;
+	struct pl hostport;
+	int err;
+
+	if (!uri || !pl)
+		return EINVAL;
+
+	memset(uri, 0, sizeof(*uri));
+	if (0 == re_regex(pl->p, pl->l,
+			  "[^:]+:[^@:]*[:]*[^@]*@[^;? ]+[^?]*[^]*",
+			  &uri->scheme, &uri->user, NULL, &uri->password,
+			  &hostport, &uri->params, &uri->headers)) {
+
+		if (0 == uri_decode_hostport(&hostport, &uri->host, &port))
+			goto out;
+	}
+
+	memset(uri, 0, sizeof(*uri));
+	err = re_regex(pl->p, pl->l, "[^:]+:[^;? ]+[^?]*[^]*",
+		       &uri->scheme, &hostport, &uri->params, &uri->headers);
+	if (0 == err) {
+		err = uri_decode_hostport(&hostport, &uri->host, &port);
+		if (0 == err)
+			goto out;
+	}
+
+	return err;
+
+ out:
+	/* Cache host address family */
+	if (0 == sa_set(&addr, &uri->host, 0))
+		uri->af = sa_af(&addr);
+	else
+		uri->af = AF_UNSPEC;
+
+	if (pl_isset(&port))
+		uri->port = (uint16_t)pl_u32(&port);
+
+	return 0;
+}
+
+
+/**
+ * Get a URI parameter and possibly the value of it
+ *
+ * @param pl     Pointer-length string containing parameters
+ * @param pname  URI Parameter name
+ * @param pvalue Returned URI Parameter value
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_param_get(const struct pl *pl, const struct pl *pname,
+		  struct pl *pvalue)
+{
+	char expr[128];
+
+	if (!pl || !pname || !pvalue)
+		return EINVAL;
+
+	(void)re_snprintf(expr, sizeof(expr), ";%r[=]*[^;]*", pname);
+
+	return re_regex(pl->p, pl->l, expr, NULL, pvalue);
+}
+
+
+/**
+ * Call the apply handler for each URI Parameter
+ *
+ * @param pl  Pointer-length string containing parameters
+ * @param ah  Apply handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode (returned from handler)
+ */
+int uri_params_apply(const struct pl *pl, uri_apply_h *ah, void *arg)
+{
+	struct pl plr, pname, eq, pvalue;
+	int err = 0;
+
+	if (!pl || !ah)
+		return EINVAL;
+
+	plr = *pl;
+
+	while (plr.l > 0) {
+
+		err = re_regex(plr.p, plr.l, ";[^;=]+[=]*[^;]*",
+			       &pname, &eq, &pvalue);
+		if (err)
+			break;
+
+		pl_advance(&plr, 1 + pname.l + eq.l + pvalue.l);
+
+		err = ah(&pname, &pvalue, arg);
+		if (err)
+			break;
+	}
+
+	return err;
+}
+
+
+/**
+ * Get a URI header and possibly the value of it
+ *
+ * @param pl     Pointer-length string containing URI Headers
+ * @param hname  URI Header name
+ * @param hvalue Returned URI Header value
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_header_get(const struct pl *pl, const struct pl *hname,
+		   struct pl *hvalue)
+{
+	char expr[128];
+
+	if (!pl || !hname || !hvalue)
+		return EINVAL;
+
+	(void)re_snprintf(expr, sizeof(expr), "[?&]1%r=[^&]+", hname);
+
+	return re_regex(pl->p, pl->l, expr, NULL, hvalue);
+}
+
+
+/**
+ * Call the apply handler for each URI Header
+ *
+ * @param pl  Pointer-length string containing URI Headers
+ * @param ah  Apply handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode (returned from handler)
+ */
+int uri_headers_apply(const struct pl *pl, uri_apply_h *ah, void *arg)
+{
+	struct pl plr, sep, hname, hvalue;
+	int err = 0;
+
+	if (!pl || !ah)
+		return EINVAL;
+
+	plr = *pl;
+
+	while (plr.l > 0) {
+
+		err = re_regex(plr.p, plr.l, "[?&]1[^=]+=[^&]+",
+			       &sep, &hname, &hvalue);
+		if (err)
+			break;
+
+		pl_advance(&plr, sep.l + hname.l + 1 + hvalue.l);
+
+		err = ah(&hname, &hvalue, arg);
+		if (err)
+			break;
+	}
+
+	return err;
+}
diff --git a/src/uri/uric.c b/src/uri/uric.c
new file mode 100644
index 0000000..1d97dfe
--- /dev/null
+++ b/src/uri/uric.c
@@ -0,0 +1,306 @@
+/**
+ * @file uric.c  URI component escaping/unescaping
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <ctype.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_uri.h>
+
+
+#define DEBUG_MODULE "uric"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/** Defines the URI escape handler */
+typedef bool (esc_h)(char c);
+
+
+static bool is_mark(int c)
+{
+	switch (c) {
+
+	case '-':
+	case '_':
+	case '.':
+	case '!':
+	case '~':
+	case '*':
+	case '\'':
+	case '(':
+	case ')':
+		return true;
+	default:
+		return false;
+	}
+}
+
+
+static bool is_unreserved(char c)
+{
+	return isalnum(c) || is_mark(c);
+}
+
+
+static bool is_user_unreserved(int c)
+{
+	switch (c) {
+
+	case '&':
+	case '=':
+	case '+':
+	case '$':
+	case ',':
+	case ';':
+	case '?':
+	case '/':
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool is_hnv_unreserved(char c)
+{
+	switch (c) {
+
+	case '[':
+	case ']':
+	case '/':
+	case '?':
+	case ':':
+	case '+':
+	case '$':
+		return true;
+	default:
+		return false;
+	}
+}
+
+
+static bool is_user(char c)
+{
+	return is_unreserved(c) || is_user_unreserved(c);
+}
+
+
+static bool is_password(char c)
+{
+	switch (c) {
+
+	case '&':
+	case '=':
+	case '+':
+	case '$':
+	case ',':
+		return true;
+	default:
+		return is_unreserved(c);
+	}
+}
+
+
+static bool is_param_unreserved(char c)
+{
+	switch (c) {
+
+	case '[':
+	case ']':
+	case '/':
+	case ':':
+	case '&':
+	case '+':
+	case '$':
+		return true;
+	default:
+		return false;
+	}
+}
+
+
+static bool is_paramchar(char c)
+{
+	return is_param_unreserved(c) || is_unreserved(c);
+}
+
+
+static bool is_hvalue(char c)
+{
+	return is_hnv_unreserved(c) || is_unreserved(c);
+}
+
+
+static int comp_escape(struct re_printf *pf, const struct pl *pl, esc_h *eh)
+{
+	size_t i;
+	int err = 0;
+
+	if (!pf || !pl || !eh)
+		return EINVAL;
+
+	for (i=0; i<pl->l && !err; i++) {
+		const char c = pl->p[i];
+
+		if (eh(c)) {
+			err = pf->vph(&c, 1, pf->arg);
+		}
+		else {
+			err = re_hprintf(pf, "%%%02X", c);
+		}
+	}
+
+	return err;
+}
+
+
+static int comp_unescape(struct re_printf *pf, const struct pl *pl, esc_h *eh)
+{
+	size_t i;
+	int err = 0;
+
+	if (!pf || !pl || !eh)
+		return EINVAL;
+
+	for (i=0; i<pl->l && !err; i++) {
+		const char c = pl->p[i];
+
+		if (eh(c)) {
+			err = pf->vph(&c, 1, pf->arg);
+			continue;
+		}
+
+		if ('%' == c) {
+			if (i+2 < pl->l) {
+				const uint8_t hi = ch_hex(pl->p[++i]);
+				const uint8_t lo = ch_hex(pl->p[++i]);
+				const char b = hi<<4 | lo;
+				err = pf->vph(&b, 1, pf->arg);
+			}
+			else {
+				DEBUG_WARNING("unescape: short uri (%u)\n", i);
+				return EBADMSG;
+			}
+		}
+		else {
+			DEBUG_WARNING("unescape: illegal '%c' in %r\n",
+				      c, pl);
+			return EINVAL;
+		}
+	}
+
+	return err;
+}
+
+
+/**
+ * Escape a URI user component
+ *
+ * @param pf Print function
+ * @param pl String to escape
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_user_escape(struct re_printf *pf, const struct pl *pl)
+{
+	return comp_escape(pf, pl, is_user);
+}
+
+
+/**
+ * Unescape a URI user component
+ *
+ * @param pf Print function
+ * @param pl String to unescape
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_user_unescape(struct re_printf *pf, const struct pl *pl)
+{
+	return comp_unescape(pf, pl, is_user);
+}
+
+
+/**
+ * Escape a URI password component
+ *
+ * @param pf Print function
+ * @param pl String to escape
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_password_escape(struct re_printf *pf, const struct pl *pl)
+{
+	return comp_escape(pf, pl, is_password);
+}
+
+
+/**
+ * Unescape a URI password component
+ *
+ * @param pf Print function
+ * @param pl String to unescape
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_password_unescape(struct re_printf *pf, const struct pl *pl)
+{
+	return comp_unescape(pf, pl, is_password);
+}
+
+
+/**
+ * Escape one URI Parameter value
+ *
+ * @param pf Print function
+ * @param pl String to escape
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_param_escape(struct re_printf *pf, const struct pl *pl)
+{
+	return comp_escape(pf, pl, is_paramchar);
+}
+
+
+/**
+ * Unescape one URI Parameter value
+ *
+ * @param pf Print function
+ * @param pl String to unescape
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_param_unescape(struct re_printf *pf, const struct pl *pl)
+{
+	return comp_unescape(pf, pl, is_paramchar);
+}
+
+
+/**
+ * Escape one URI Header name/value
+ *
+ * @param pf Print function
+ * @param pl String to escape
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_header_escape(struct re_printf *pf, const struct pl *pl)
+{
+	return comp_escape(pf, pl, is_hvalue);
+}
+
+
+/**
+ * Unescape one URI Header name/value
+ *
+ * @param pf Print function
+ * @param pl String to unescape
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int uri_header_unescape(struct re_printf *pf, const struct pl *pl)
+{
+	return comp_unescape(pf, pl, is_hvalue);
+}