/** * @file uri.c Uniform Resource Identifier (URI) module * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include /** * 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; }