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/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;
+}