1 /**
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  */
uri_encode(struct re_printf * pf,const struct uri * uri)23 int 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  * @return 0 if success, otherwise errorcode
78  */
decode_hostport(const struct pl * hostport,struct pl * host,struct pl * port)79 static int decode_hostport(const struct pl *hostport, struct pl *host,
80 			   struct pl *port)
81 {
82 	/* Try IPv6 first */
83 	if (!re_regex(hostport->p, hostport->l, "\\[[0-9a-f:]+\\][:]*[0-9]*",
84 		      host, NULL, port))
85 		return 0;
86 
87 	/* Then non-IPv6 host */
88 	return re_regex(hostport->p, hostport->l, "[^:]+[:]*[0-9]*",
89 			host, NULL, port);
90 }
91 
92 
93 /**
94  * Decode a pointer-length object into a URI object
95  *
96  * @param uri  URI object
97  * @param pl   Pointer-length object to decode from
98  *
99  * @return 0 if success, otherwise errorcode
100  */
uri_decode(struct uri * uri,const struct pl * pl)101 int uri_decode(struct uri *uri, const struct pl *pl)
102 {
103 	struct sa addr;
104 	struct pl port = PL_INIT;
105 	struct pl hostport;
106 	int err;
107 
108 	if (!uri || !pl)
109 		return EINVAL;
110 
111 	memset(uri, 0, sizeof(*uri));
112 	if (0 == re_regex(pl->p, pl->l,
113 			  "[^:]+:[^@:]*[:]*[^@]*@[^;? ]+[^?]*[^]*",
114 			  &uri->scheme, &uri->user, NULL, &uri->password,
115 			  &hostport, &uri->params, &uri->headers)) {
116 
117 		if (0 == decode_hostport(&hostport, &uri->host, &port))
118 			goto out;
119 	}
120 
121 	memset(uri, 0, sizeof(*uri));
122 	err = re_regex(pl->p, pl->l, "[^:]+:[^;? ]+[^?]*[^]*",
123 		       &uri->scheme, &hostport, &uri->params, &uri->headers);
124 	if (0 == err) {
125 		err = decode_hostport(&hostport, &uri->host, &port);
126 		if (0 == err)
127 			goto out;
128 	}
129 
130 	return err;
131 
132  out:
133 	/* Cache host address family */
134 	if (0 == sa_set(&addr, &uri->host, 0))
135 		uri->af = sa_af(&addr);
136 	else
137 		uri->af = AF_UNSPEC;
138 
139 	if (pl_isset(&port))
140 		uri->port = (uint16_t)pl_u32(&port);
141 
142 	return 0;
143 }
144 
145 
146 /**
147  * Get a URI parameter and possibly the value of it
148  *
149  * @param pl     Pointer-length string containing parameters
150  * @param pname  URI Parameter name
151  * @param pvalue Returned URI Parameter value
152  *
153  * @return 0 if success, otherwise errorcode
154  */
uri_param_get(const struct pl * pl,const struct pl * pname,struct pl * pvalue)155 int uri_param_get(const struct pl *pl, const struct pl *pname,
156 		  struct pl *pvalue)
157 {
158 	char expr[128];
159 
160 	if (!pl || !pname || !pvalue)
161 		return EINVAL;
162 
163 	(void)re_snprintf(expr, sizeof(expr), ";%r[=]*[^;]*", pname);
164 
165 	return re_regex(pl->p, pl->l, expr, NULL, pvalue);
166 }
167 
168 
169 /**
170  * Call the apply handler for each URI Parameter
171  *
172  * @param pl  Pointer-length string containing parameters
173  * @param ah  Apply handler
174  * @param arg Handler argument
175  *
176  * @return 0 if success, otherwise errorcode (returned from handler)
177  */
uri_params_apply(const struct pl * pl,uri_apply_h * ah,void * arg)178 int uri_params_apply(const struct pl *pl, uri_apply_h *ah, void *arg)
179 {
180 	struct pl plr, pname, eq, pvalue;
181 	int err = 0;
182 
183 	if (!pl || !ah)
184 		return EINVAL;
185 
186 	plr = *pl;
187 
188 	while (plr.l > 0) {
189 
190 		err = re_regex(plr.p, plr.l, ";[^;=]+[=]*[^;]*",
191 			       &pname, &eq, &pvalue);
192 		if (err)
193 			break;
194 
195 		pl_advance(&plr, 1 + pname.l + eq.l + pvalue.l);
196 
197 		err = ah(&pname, &pvalue, arg);
198 		if (err)
199 			break;
200 	}
201 
202 	return err;
203 }
204 
205 
206 /**
207  * Get a URI header and possibly the value of it
208  *
209  * @param pl     Pointer-length string containing URI Headers
210  * @param hname  URI Header name
211  * @param hvalue Returned URI Header value
212  *
213  * @return 0 if success, otherwise errorcode
214  */
uri_header_get(const struct pl * pl,const struct pl * hname,struct pl * hvalue)215 int uri_header_get(const struct pl *pl, const struct pl *hname,
216 		   struct pl *hvalue)
217 {
218 	char expr[128];
219 
220 	if (!pl || !hname || !hvalue)
221 		return EINVAL;
222 
223 	(void)re_snprintf(expr, sizeof(expr), "[?&]1%r=[^&]+", hname);
224 
225 	return re_regex(pl->p, pl->l, expr, NULL, hvalue);
226 }
227 
228 
229 /**
230  * Call the apply handler for each URI Header
231  *
232  * @param pl  Pointer-length string containing URI Headers
233  * @param ah  Apply handler
234  * @param arg Handler argument
235  *
236  * @return 0 if success, otherwise errorcode (returned from handler)
237  */
uri_headers_apply(const struct pl * pl,uri_apply_h * ah,void * arg)238 int uri_headers_apply(const struct pl *pl, uri_apply_h *ah, void *arg)
239 {
240 	struct pl plr, sep, hname, hvalue;
241 	int err = 0;
242 
243 	if (!pl || !ah)
244 		return EINVAL;
245 
246 	plr = *pl;
247 
248 	while (plr.l > 0) {
249 
250 		err = re_regex(plr.p, plr.l, "[?&]1[^=]+=[^&]+",
251 			       &sep, &hname, &hvalue);
252 		if (err)
253 			break;
254 
255 		pl_advance(&plr, sep.l + hname.l + 1 + hvalue.l);
256 
257 		err = ah(&hname, &hvalue, arg);
258 		if (err)
259 			break;
260 	}
261 
262 	return err;
263 }
264