/src/open5gs/lib/sbi/yuarel.c
Line | Count | Source |
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 <stdlib.h> |
23 | | #include <stdio.h> |
24 | | #include <string.h> |
25 | | #include "yuarel.h" |
26 | | |
27 | | /** |
28 | | * Parse a non null terminated string into an integer. |
29 | | * |
30 | | * str: the string containing the number. |
31 | | * len: Number of characters to parse. |
32 | | */ |
33 | | static inline int |
34 | | natoi(const char *str, size_t len) |
35 | 0 | { |
36 | 0 | int i, r = 0; |
37 | 0 | for (i = 0; i < len; i++) { |
38 | 0 | r *= 10; |
39 | 0 | r += str[i] - '0'; |
40 | 0 | } |
41 | |
|
42 | 0 | return r; |
43 | 0 | } |
44 | | |
45 | | /** |
46 | | * Check if a URL is relative (no scheme and hostname). |
47 | | * |
48 | | * url: the string containing the URL to check. |
49 | | * |
50 | | * Returns 1 if relative, otherwise 0. |
51 | | */ |
52 | | static inline int |
53 | | is_relative(const char *url) |
54 | 0 | { |
55 | 0 | return (*url == '/') ? 1 : 0; |
56 | 0 | } |
57 | | |
58 | | /** |
59 | | * Parse the scheme of a URL by inserting a null terminator after the scheme. |
60 | | * |
61 | | * str: the string containing the URL to parse. Will be modified. |
62 | | * |
63 | | * Returns a pointer to the hostname on success, otherwise NULL. |
64 | | */ |
65 | | static inline char * |
66 | | parse_scheme(char *str) |
67 | 0 | { |
68 | 0 | char *s; |
69 | | |
70 | | /* If not found or first in string, return error */ |
71 | 0 | s = strchr(str, ':'); |
72 | 0 | if (s == NULL || s == str) { |
73 | 0 | return NULL; |
74 | 0 | } |
75 | | |
76 | | /* If not followed by two slashes, return error */ |
77 | 0 | if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') { |
78 | 0 | return NULL; |
79 | 0 | } |
80 | | |
81 | 0 | *s = '\0'; // Replace ':' with NULL |
82 | |
|
83 | 0 | return s + 3; |
84 | 0 | } |
85 | | |
86 | | /** |
87 | | * Find a character in a string, replace it with '\0' and return the next |
88 | | * character in the string. |
89 | | * |
90 | | * str: the string to search in. |
91 | | * find: the character to search for. |
92 | | * |
93 | | * Returns a pointer to the character after the one to search for. If not |
94 | | * found, NULL is returned. |
95 | | */ |
96 | | static inline char * |
97 | | find_and_terminate(char *str, char find) |
98 | 0 | { |
99 | 0 | str = strchr(str, find); |
100 | 0 | if (NULL == str) { |
101 | 0 | return NULL; |
102 | 0 | } |
103 | | |
104 | 0 | *str = '\0'; |
105 | 0 | return str + 1; |
106 | 0 | } |
107 | | |
108 | | /* Yes, the following functions could be implemented as preprocessor macros |
109 | | instead of inline functions, but I think that this approach will be more |
110 | | clean in this case. */ |
111 | | static inline char * |
112 | | find_fragment(char *str) |
113 | 0 | { |
114 | 0 | return find_and_terminate(str, '#'); |
115 | 0 | } |
116 | | |
117 | | static inline char * |
118 | | find_query(char *str) |
119 | 0 | { |
120 | 0 | return find_and_terminate(str, '?'); |
121 | 0 | } |
122 | | |
123 | | static inline char * |
124 | | find_path(char *str) |
125 | 0 | { |
126 | 0 | return find_and_terminate(str, '/'); |
127 | 0 | } |
128 | | |
129 | | /** |
130 | | * Parse a URL string to a struct. |
131 | | * |
132 | | * url: pointer to the struct where to store the parsed URL parts. |
133 | | * u: the string containing the URL to be parsed. |
134 | | * |
135 | | * Returns 0 on success, otherwise -1. |
136 | | */ |
137 | | int |
138 | | yuarel_parse(struct yuarel *url, char *u) |
139 | 0 | { |
140 | 0 | if (NULL == url || NULL == u) { |
141 | 0 | return -1; |
142 | 0 | } |
143 | | |
144 | 0 | memset(url, 0, sizeof (struct yuarel)); |
145 | | |
146 | | /* (Fragment) */ |
147 | 0 | url->fragment = find_fragment(u); |
148 | | |
149 | | /* (Query) */ |
150 | 0 | url->query = find_query(u); |
151 | | |
152 | | /* Relative URL? Parse scheme and hostname */ |
153 | 0 | if (!is_relative(u)) { |
154 | | /* Scheme */ |
155 | 0 | url->scheme = u; |
156 | 0 | u = parse_scheme(u); |
157 | 0 | if (u == NULL) { |
158 | 0 | return -1; |
159 | 0 | } |
160 | | |
161 | | /* Host */ |
162 | 0 | if ('\0' == *u) { |
163 | 0 | return -1; |
164 | 0 | } |
165 | 0 | url->host = u; |
166 | | |
167 | | /* (Path) */ |
168 | 0 | url->path = find_path(u); |
169 | | |
170 | | /* (Credentials) */ |
171 | 0 | u = strchr(url->host, '@'); |
172 | 0 | if (NULL != u) { |
173 | | /* Missing credentials? */ |
174 | 0 | if (u == url->host) { |
175 | 0 | return -1; |
176 | 0 | } |
177 | | |
178 | 0 | url->username = url->host; |
179 | 0 | url->host = u + 1; |
180 | 0 | *u = '\0'; |
181 | |
|
182 | 0 | u = strchr(url->username, ':'); |
183 | 0 | if (NULL == u) { |
184 | 0 | return -1; |
185 | 0 | } |
186 | | |
187 | 0 | url->password = u + 1; |
188 | 0 | *u = '\0'; |
189 | 0 | } |
190 | | |
191 | | /* Missing hostname? */ |
192 | 0 | if ('\0' == *url->host) { |
193 | 0 | return -1; |
194 | 0 | } |
195 | | |
196 | | /* (Port) */ |
197 | | // If hostname starts with square bracket, it is IPv6 literal |
198 | | // example: |
199 | | // http://[1080:0:0:0:8:800:200C:417A]:80/index.html |
200 | | // http://[3ffe:2a00:100:7031::1] |
201 | | // http://[::192.9.5.5]/ipng |
202 | 0 | if ('[' == url->host[0]) { |
203 | 0 | u = strchr(url->host, ']'); |
204 | 0 | if (NULL != u) { |
205 | 0 | url->host++; |
206 | 0 | *(u++) = '\0'; |
207 | 0 | if ('\0' == *u) { |
208 | 0 | u = NULL; |
209 | 0 | } else if (':' != *u) { |
210 | 0 | return -1; |
211 | 0 | } |
212 | 0 | } else { |
213 | 0 | return -1; |
214 | 0 | } |
215 | 0 | } else { |
216 | 0 | u = strchr(url->host, ':'); |
217 | 0 | } |
218 | 0 | if (NULL != u && (NULL == url->path || u < url->path)) { |
219 | 0 | *(u++) = '\0'; |
220 | 0 | if ('\0' == *u) { |
221 | 0 | return -1; |
222 | 0 | } |
223 | | |
224 | 0 | if (url->path) { |
225 | 0 | url->port = natoi(u, url->path - u - 1); |
226 | 0 | } else { |
227 | 0 | url->port = atoi(u); |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | | /* Missing hostname? */ |
232 | 0 | if ('\0' == *url->host) { |
233 | 0 | return -1; |
234 | 0 | } |
235 | 0 | } else { |
236 | | /* (Path) */ |
237 | 0 | url->path = find_path(u); |
238 | 0 | } |
239 | | |
240 | 0 | return 0; |
241 | 0 | } |
242 | | |
243 | | /** |
244 | | * Split a path into several strings. |
245 | | * |
246 | | * No data is copied, the slashed are used as null terminators and then |
247 | | * pointers to each path part will be stored in **parts. Double slashes will be |
248 | | * treated as one. |
249 | | * |
250 | | * path: the path to split. |
251 | | * parts: a pointer to an array of (char *) where to store the result. |
252 | | * max_parts: max number of parts to parse. |
253 | | */ |
254 | | int |
255 | | yuarel_split_path(char *path, char **parts, int max_parts) |
256 | 0 | { |
257 | 0 | int i = 0; |
258 | |
|
259 | 0 | if (NULL == path || '\0' == *path) { |
260 | 0 | return -1; |
261 | 0 | } |
262 | | |
263 | 0 | do { |
264 | | /* Forward to after slashes */ |
265 | 0 | while (*path == '/') path++; |
266 | |
|
267 | 0 | if ('\0' == *path) { |
268 | 0 | break; |
269 | 0 | } |
270 | | |
271 | 0 | parts[i++] = path; |
272 | |
|
273 | 0 | path = strchr(path, '/'); |
274 | 0 | if (NULL == path) { |
275 | 0 | break; |
276 | 0 | } |
277 | | |
278 | 0 | *(path++) = '\0'; |
279 | 0 | } while (i < max_parts); |
280 | |
|
281 | 0 | return i; |
282 | 0 | } |
283 | | |
284 | | int |
285 | | yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params) |
286 | 0 | { |
287 | 0 | int i = 0; |
288 | |
|
289 | 0 | if (NULL == query || '\0' == *query) { |
290 | 0 | return -1; |
291 | 0 | } |
292 | | |
293 | 0 | params[i++].key = query; |
294 | 0 | while (i < max_params && NULL != (query = strchr(query, delimiter))) { |
295 | 0 | *query = '\0'; |
296 | 0 | params[i].key = ++query; |
297 | 0 | params[i].val = NULL; |
298 | | |
299 | | /* Go back and split previous param */ |
300 | 0 | if (i > 0) { |
301 | 0 | if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) { |
302 | 0 | *(params[i - 1].val)++ = '\0'; |
303 | 0 | } |
304 | 0 | } |
305 | 0 | i++; |
306 | 0 | } |
307 | | |
308 | | /* Go back and split last param */ |
309 | 0 | if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) { |
310 | 0 | *(params[i - 1].val)++ = '\0'; |
311 | 0 | } |
312 | |
|
313 | 0 | return i; |
314 | 0 | } |