blob: 54094631eccb431c95b298cee9b7b691f7ff1fb3 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file uri.c Uniform Resource Identifier (URI) module
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_fmt.h>
9#include <re_mem.h>
10#include <re_list.h>
11#include <re_sa.h>
12#include <re_uri.h>
13
14
15/**
16 * Encode a URI object
17 *
18 * @param pf Print function to encode into
19 * @param uri URI object
20 *
21 * @return 0 if success, otherwise errorcode
22 */
23int uri_encode(struct re_printf *pf, const struct uri *uri)
24{
25 int err;
26
27 if (!uri)
28 return 0;
29
30 if (!pl_isset(&uri->scheme) || !pl_isset(&uri->host))
31 return EINVAL;
32
33 err = re_hprintf(pf, "%r:", &uri->scheme);
34 if (err)
35 return err;
36
37 if (pl_isset(&uri->user)) {
38 err = re_hprintf(pf, "%r", &uri->user);
39
40 if (pl_isset(&uri->password))
41 err |= re_hprintf(pf, ":%r", &uri->password);
42
43 err |= pf->vph("@", 1, pf->arg);
44
45 if (err)
46 return err;
47 }
48
49 /* The IPv6 address is delimited by '[' and ']' */
50 switch (uri->af) {
51
52#ifdef HAVE_INET6
53 case AF_INET6:
54 err = re_hprintf(pf, "[%r]", &uri->host);
55 break;
56#endif
57
58 default:
59 err = re_hprintf(pf, "%r", &uri->host);
60 break;
61 }
62 if (err)
63 return err;
64
65 if (uri->port)
66 err = re_hprintf(pf, ":%u", uri->port);
67
68 err |= re_hprintf(pf, "%r%r", &uri->params, &uri->headers);
69
70 return err;
71}
72
73
74/**
75 * Decode host-port portion of a URI (if present)
76 *
77 * @param hostport Host and port input string
78 * @param host Decoded host portion
79 * @param port Decoded port portion
80 *
81 * @return 0 if success, otherwise errorcode
82 */
83int uri_decode_hostport(const struct pl *hostport, struct pl *host,
84 struct pl *port)
85{
86 if (!hostport || !host || !port)
87 return EINVAL;
88
89 /* Try IPv6 first */
90 if (!re_regex(hostport->p, hostport->l, "\\[[0-9a-f:]+\\][:]*[0-9]*",
91 host, NULL, port))
92 return 0;
93
94 /* Then non-IPv6 host */
95 return re_regex(hostport->p, hostport->l, "[^:]+[:]*[0-9]*",
96 host, NULL, port);
97}
98
99
100/**
101 * Decode a pointer-length object into a URI object
102 *
103 * @param uri URI object
104 * @param pl Pointer-length object to decode from
105 *
106 * @return 0 if success, otherwise errorcode
107 */
108int uri_decode(struct uri *uri, const struct pl *pl)
109{
110 struct sa addr;
111 struct pl port = PL_INIT;
112 struct pl hostport;
113 int err;
114
115 if (!uri || !pl)
116 return EINVAL;
117
118 memset(uri, 0, sizeof(*uri));
119 if (0 == re_regex(pl->p, pl->l,
120 "[^:]+:[^@:]*[:]*[^@]*@[^;? ]+[^?]*[^]*",
121 &uri->scheme, &uri->user, NULL, &uri->password,
122 &hostport, &uri->params, &uri->headers)) {
123
124 if (0 == uri_decode_hostport(&hostport, &uri->host, &port))
125 goto out;
126 }
127
128 memset(uri, 0, sizeof(*uri));
129 err = re_regex(pl->p, pl->l, "[^:]+:[^;? ]+[^?]*[^]*",
130 &uri->scheme, &hostport, &uri->params, &uri->headers);
131 if (0 == err) {
132 err = uri_decode_hostport(&hostport, &uri->host, &port);
133 if (0 == err)
134 goto out;
135 }
136
137 return err;
138
139 out:
140 /* Cache host address family */
141 if (0 == sa_set(&addr, &uri->host, 0))
142 uri->af = sa_af(&addr);
143 else
144 uri->af = AF_UNSPEC;
145
146 if (pl_isset(&port))
147 uri->port = (uint16_t)pl_u32(&port);
148
149 return 0;
150}
151
152
153/**
154 * Get a URI parameter and possibly the value of it
155 *
156 * @param pl Pointer-length string containing parameters
157 * @param pname URI Parameter name
158 * @param pvalue Returned URI Parameter value
159 *
160 * @return 0 if success, otherwise errorcode
161 */
162int uri_param_get(const struct pl *pl, const struct pl *pname,
163 struct pl *pvalue)
164{
165 char expr[128];
166
167 if (!pl || !pname || !pvalue)
168 return EINVAL;
169
170 (void)re_snprintf(expr, sizeof(expr), ";%r[=]*[^;]*", pname);
171
172 return re_regex(pl->p, pl->l, expr, NULL, pvalue);
173}
174
175
176/**
177 * Call the apply handler for each URI Parameter
178 *
179 * @param pl Pointer-length string containing parameters
180 * @param ah Apply handler
181 * @param arg Handler argument
182 *
183 * @return 0 if success, otherwise errorcode (returned from handler)
184 */
185int uri_params_apply(const struct pl *pl, uri_apply_h *ah, void *arg)
186{
187 struct pl plr, pname, eq, pvalue;
188 int err = 0;
189
190 if (!pl || !ah)
191 return EINVAL;
192
193 plr = *pl;
194
195 while (plr.l > 0) {
196
197 err = re_regex(plr.p, plr.l, ";[^;=]+[=]*[^;]*",
198 &pname, &eq, &pvalue);
199 if (err)
200 break;
201
202 pl_advance(&plr, 1 + pname.l + eq.l + pvalue.l);
203
204 err = ah(&pname, &pvalue, arg);
205 if (err)
206 break;
207 }
208
209 return err;
210}
211
212
213/**
214 * Get a URI header and possibly the value of it
215 *
216 * @param pl Pointer-length string containing URI Headers
217 * @param hname URI Header name
218 * @param hvalue Returned URI Header value
219 *
220 * @return 0 if success, otherwise errorcode
221 */
222int uri_header_get(const struct pl *pl, const struct pl *hname,
223 struct pl *hvalue)
224{
225 char expr[128];
226
227 if (!pl || !hname || !hvalue)
228 return EINVAL;
229
230 (void)re_snprintf(expr, sizeof(expr), "[?&]1%r=[^&]+", hname);
231
232 return re_regex(pl->p, pl->l, expr, NULL, hvalue);
233}
234
235
236/**
237 * Call the apply handler for each URI Header
238 *
239 * @param pl Pointer-length string containing URI Headers
240 * @param ah Apply handler
241 * @param arg Handler argument
242 *
243 * @return 0 if success, otherwise errorcode (returned from handler)
244 */
245int uri_headers_apply(const struct pl *pl, uri_apply_h *ah, void *arg)
246{
247 struct pl plr, sep, hname, hvalue;
248 int err = 0;
249
250 if (!pl || !ah)
251 return EINVAL;
252
253 plr = *pl;
254
255 while (plr.l > 0) {
256
257 err = re_regex(plr.p, plr.l, "[?&]1[^=]+=[^&]+",
258 &sep, &hname, &hvalue);
259 if (err)
260 break;
261
262 pl_advance(&plr, sep.l + hname.l + 1 + hvalue.l);
263
264 err = ah(&hname, &hvalue, arg);
265 if (err)
266 break;
267 }
268
269 return err;
270}