re/src/uri/uri.c
2011-03-18 10:45:57 +00:00

263 lines
5.1 KiB
C

/**
* @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)
*
* @return 0 if success, otherwise errorcode
*/
static int decode_hostport(const struct pl *hostport, struct pl *host,
struct pl *port)
{
/* 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 == 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 = 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;
}