aboutsummaryrefslogtreecommitdiff
path: root/src/lib/util/uri.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/uri.c')
-rw-r--r--src/lib/util/uri.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/src/lib/util/uri.c b/src/lib/util/uri.c
new file mode 100644
index 000000000..b09968581
--- /dev/null
+++ b/src/lib/util/uri.c
@@ -0,0 +1,345 @@
1/**
2 * Copyright (C) 2016,2017 Jack Engqvist Johansson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22#include "platform.h"
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include "gnunet_uri_lib.h"
27
28
29/**
30 * Parse a non null terminated string into an integer.
31 *
32 * str: the string containing the number.
33 * len: Number of characters to parse.
34 */
35static inline int
36natoi (const char *str,
37 size_t len)
38{
39 int i, r = 0;
40 for (i = 0; i < len; i++) {
41 r *= 10;
42 r += str[i] - '0';
43 }
44
45 return r;
46}
47
48
49/**
50 * Check if a URL is relative (no scheme and hostname).
51 *
52 * url: the string containing the URL to check.
53 *
54 * Returns 1 if relative, otherwise 0.
55 */
56static inline int
57is_relative (const char *url)
58{
59 return (*url == '/') ? 1 : 0;
60}
61
62
63/**
64 * Parse the scheme of a URL by inserting a null terminator after the scheme.
65 *
66 * str: the string containing the URL to parse. Will be modified.
67 *
68 * Returns a pointer to the hostname on success, otherwise NULL.
69 */
70static inline char *
71parse_scheme (char *str)
72{
73 char *s;
74
75 /* If not found or first in string, return error */
76 s = strchr (str, ':');
77 if (s == NULL || s == str) {
78 return NULL;
79 }
80
81 /* If not followed by two slashes, return error */
82 if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') {
83 return NULL;
84 }
85
86 *s = '\0'; // Replace ':' with NULL
87
88 return s + 3;
89}
90
91
92/**
93 * Find a character in a string, replace it with '\0' and return the next
94 * character in the string.
95 *
96 * str: the string to search in.
97 * find: the character to search for.
98 *
99 * Returns a pointer to the character after the one to search for. If not
100 * found, NULL is returned.
101 */
102static inline char *
103find_and_terminate (char *str,
104 char find)
105{
106 str = strchr(str, find);
107 if (NULL == str) {
108 return NULL;
109 }
110
111 *str = '\0';
112 return str + 1;
113}
114
115
116/* Yes, the following functions could be implemented as preprocessor macros
117 instead of inline functions, but I think that this approach will be more
118 clean in this case. */
119static inline char *
120find_fragment (char *str)
121{
122 return find_and_terminate (str, '#');
123}
124
125
126static inline char *
127find_query (char *str)
128{
129 return find_and_terminate (str, '?');
130}
131
132
133static inline char *
134find_path (char *str)
135{
136 return find_and_terminate (str, '/');
137}
138
139
140/**
141 * Parse a URL to a struct.
142 *
143 * The URL string should be in one of the following formats:
144 *
145 * Absolute URL:
146 * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ] [ "#" fragment ]
147 *
148 * Relative URL:
149 * path [ "?" query ] [ "#" fragment ]
150 *
151 * The following parts will be parsed to the corresponding struct member.
152 *
153 * *url: a pointer to the struct where to store the parsed values.
154 * *url_str: a pointer to the url to be parsed (null terminated). The string
155 * will be modified.
156 *
157 * Returns 0 on success, otherwise -1.
158 */
159int
160GNUNET_uri_parse (struct GNUNET_Uri *url,
161 char *u)
162{
163 if (NULL == url || NULL == u) {
164 return -1;
165 }
166
167 memset(url, 0, sizeof (struct GNUNET_Uri));
168
169 /* (Fragment) */
170 url->fragment = find_fragment (u);
171
172 /* (Query) */
173 url->query = find_query (u);
174
175 /* Relative URL? Parse scheme and hostname */
176 if (!is_relative (u)) {
177 /* Scheme */
178 url->scheme = u;
179 u = parse_scheme (u);
180 if (u == NULL) {
181 return -1;
182 }
183
184 /* Host */
185 if ('\0' == *u) {
186 return -1;
187 }
188 url->host = u;
189
190 /* (Path) */
191 url->path = find_path (u);
192
193 /* (Credentials) */
194 u = strchr (url->host, '@');
195 if (NULL != u) {
196 /* Missing credentials? */
197 if (u == url->host) {
198 return -1;
199 }
200
201 url->username = url->host;
202 url->host = u + 1;
203 *u = '\0';
204
205 u = strchr (url->username, ':');
206 if (NULL == u) {
207 return -1;
208 }
209
210 url->password = u + 1;
211 *u = '\0';
212 }
213
214 /* Missing hostname? */
215 if ('\0' == *url->host) {
216 return -1;
217 }
218
219 /* (Port) */
220 u = strchr (url->host, ':');
221 if (NULL != u && (NULL == url->path || u < url->path)) {
222 *(u++) = '\0';
223 if ('\0' == *u) {
224 return -1;
225 }
226
227 if (url->path) {
228 url->port = natoi (u, url->path - u - 1);
229 } else {
230 url->port = atoi (u);
231 }
232 }
233
234 /* Missing hostname? */
235 if ('\0' == *url->host) {
236 return -1;
237 }
238 } else {
239 /* (Path) */
240 url->path = find_path (u);
241 }
242
243 return 0;
244}
245
246
247/**
248 * Split a path into several strings.
249 *
250 * No data is copied, the slashed are used as null terminators and then
251 * pointers to each path part will be stored in **parts. Double slashes will be
252 * treated as one.
253 *
254 * *path: the path to split. The string will be modified.
255 * **parts: a pointer to an array of (char *) where to store the result.
256 * max_parts: max number of parts to parse.
257 *
258 * Returns the number of parsed items. -1 on error.
259 */
260int
261GNUNET_uri_split_path (char *path,
262 char **parts,
263 int max_parts)
264{
265 int i = 0;
266
267 if (NULL == path || '\0' == *path) {
268 return -1;
269 }
270
271 do {
272 /* Forward to after slashes */
273 while (*path == '/') path++;
274
275 if ('\0' == *path) {
276 break;
277 }
278
279 parts[i++] = path;
280
281 path = strchr (path, '/');
282 if (NULL == path) {
283 break;
284 }
285
286 *(path++) = '\0';
287 } while (i < max_parts);
288
289 return i;
290}
291
292
293/**
294 * Parse a query string into a key/value struct.
295 *
296 * The query string should be a null terminated string of parameters separated by
297 * a delimiter. Each parameter are checked for the equal sign character. If it
298 * appears in the parameter, it will be used as a null terminator and the part
299 * that comes after it will be the value of the parameter.
300 *
301 * No data are copied, the equal sign and delimiters are used as null
302 * terminators and then pointers to each parameter key and value will be stored
303 * in the yuarel_param struct.
304 *
305 * *query: the query string to parse. The string will be modified.
306 * delimiter: the character that separates the key/value pairs from each other.
307 * *params: an array of (struct yuarel_param) where to store the result.
308 * max_values: max number of parameters to parse.
309 *
310 * Returns the number of parsed items. -1 on error.
311 */
312int
313GNUNET_uri_parse_query (char *query,
314 char delimiter,
315 struct GNUNET_UriParam *params,
316 int max_params)
317{
318 int i = 0;
319
320 if (NULL == query || '\0' == *query) {
321 return -1;
322 }
323
324 params[i++].key = query;
325 while (i < max_params && NULL != (query = strchr (query, delimiter))) {
326 *query = '\0';
327 params[i].key = ++query;
328 params[i].val = NULL;
329
330 /* Go back and split previous param */
331 if (i > 0) {
332 if ((params[i - 1].val = strchr (params[i - 1].key, '=')) != NULL) {
333 *(params[i - 1].val)++ = '\0';
334 }
335 }
336 i++;
337 }
338
339 /* Go back and split last param */
340 if ((params[i - 1].val = strchr (params[i - 1].key, '=')) != NULL) {
341 *(params[i - 1].val)++ = '\0';
342 }
343
344 return i;
345}