Branch data Line data Source code
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 : 0 : natoi(const char *str, size_t len)
35 : : {
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 : : }
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 : 0 : is_relative(const char *url)
54 : : {
55 : 0 : return (*url == '/') ? 1 : 0;
56 : : }
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 : 0 : parse_scheme(char *str)
67 : : {
68 : : 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 : : }
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 : : }
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 : 0 : find_and_terminate(char *str, char find)
98 : : {
99 : 0 : str = strchr(str, find);
100 [ # # ]: 0 : if (NULL == str) {
101 : 0 : return NULL;
102 : : }
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 : 0 : find_fragment(char *str)
113 : : {
114 : 0 : return find_and_terminate(str, '#');
115 : : }
116 : :
117 : : static inline char *
118 : 0 : find_query(char *str)
119 : : {
120 : 0 : return find_and_terminate(str, '?');
121 : : }
122 : :
123 : : static inline char *
124 : 0 : find_path(char *str)
125 : : {
126 : 0 : return find_and_terminate(str, '/');
127 : : }
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 : 0 : yuarel_parse(struct yuarel *url, char *u)
139 : : {
140 [ # # # # ]: 0 : if (NULL == url || NULL == u) {
141 : 0 : return -1;
142 : : }
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 : : }
160 : :
161 : : /* Host */
162 [ # # ]: 0 : if ('\0' == *u) {
163 : 0 : return -1;
164 : : }
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 : : }
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 : url->password = u + 1;
185 : 0 : *u = '\0';
186 : 0 : }
187 : 0 : }
188 : :
189 : : /* Missing hostname? */
190 [ # # ]: 0 : if ('\0' == *url->host) {
191 : 0 : return -1;
192 : : }
193 : :
194 : : /* (Port) */
195 : 0 : u = strchr(url->host, ':');
196 [ # # # # : 0 : if (NULL != u && (NULL == url->path || u < url->path)) {
# # ]
197 : 0 : *(u++) = '\0';
198 [ # # ]: 0 : if ('\0' == *u) {
199 : 0 : return -1;
200 : : }
201 : :
202 [ # # ]: 0 : if (url->path) {
203 : 0 : url->port = natoi(u, url->path - u - 1);
204 : 0 : } else {
205 : 0 : url->port = atoi(u);
206 : : }
207 : 0 : }
208 : :
209 : : /* Missing hostname? */
210 [ # # ]: 0 : if ('\0' == *url->host) {
211 : 0 : return -1;
212 : : }
213 : 0 : } else {
214 : : /* (Path) */
215 : 0 : url->path = find_path(u);
216 : : }
217 : :
218 : 0 : return 0;
219 : 0 : }
220 : :
221 : : /**
222 : : * Split a path into several strings.
223 : : *
224 : : * No data is copied, the slashed are used as null terminators and then
225 : : * pointers to each path part will be stored in **parts. Double slashes will be
226 : : * treated as one.
227 : : *
228 : : * path: the path to split.
229 : : * parts: a pointer to an array of (char *) where to store the result.
230 : : * max_parts: max number of parts to parse.
231 : : */
232 : : int
233 : 0 : yuarel_split_path(char *path, char **parts, int max_parts)
234 : : {
235 : 0 : int i = 0;
236 : :
237 [ # # # # ]: 0 : if (NULL == path || '\0' == *path) {
238 : 0 : return -1;
239 : : }
240 : :
241 : 0 : do {
242 : : /* Forward to after slashes */
243 [ # # ]: 0 : while (*path == '/') path++;
244 : :
245 [ # # ]: 0 : if ('\0' == *path) {
246 : 0 : break;
247 : : }
248 : :
249 : 0 : parts[i++] = path;
250 : :
251 : 0 : path = strchr(path, '/');
252 [ # # ]: 0 : if (NULL == path) {
253 : 0 : break;
254 : : }
255 : :
256 : 0 : *(path++) = '\0';
257 [ # # ]: 0 : } while (i < max_parts);
258 : :
259 : 0 : return i;
260 : 0 : }
261 : :
262 : : int
263 : 0 : yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params)
264 : : {
265 : 0 : int i = 0;
266 : :
267 [ # # # # ]: 0 : if (NULL == query || '\0' == *query) {
268 : 0 : return -1;
269 : : }
270 : :
271 : 0 : params[i++].key = query;
272 [ # # # # : 0 : while (i < max_params && NULL != (query = strchr(query, delimiter))) {
# # ]
273 : 0 : *query = '\0';
274 : 0 : params[i].key = ++query;
275 : 0 : params[i].val = NULL;
276 : :
277 : : /* Go back and split previous param */
278 [ # # ]: 0 : if (i > 0) {
279 [ # # ]: 0 : if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
280 : 0 : *(params[i - 1].val)++ = '\0';
281 : 0 : }
282 : 0 : }
283 : 0 : i++;
284 : : }
285 : :
286 : : /* Go back and split last param */
287 [ # # ]: 0 : if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
288 : 0 : *(params[i - 1].val)++ = '\0';
289 : 0 : }
290 : :
291 : 0 : return i;
292 : 0 : }
|