Blame
Date:
Mon Jul 4 09:48:39 2022 UTC
Message:
copyright years
001
2021-01-11
op
/*
002
2022-07-04
op
* Copyright (c) 2020, 2022 Omar Polo <op@omarpolo.com>
003
2021-01-11
op
*
004
2021-01-11
op
* Permission to use, copy, modify, and distribute this software for any
005
2021-01-11
op
* purpose with or without fee is hereby granted, provided that the above
006
2021-01-11
op
* copyright notice and this permission notice appear in all copies.
007
2021-01-11
op
*
008
2021-01-11
op
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
009
2021-01-11
op
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
010
2021-01-11
op
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
011
2021-01-11
op
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
012
2021-01-11
op
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
013
2021-01-11
op
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
014
2021-01-11
op
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
015
2021-01-11
op
*/
016
2021-02-12
op
017
2021-02-12
op
#include "gmid.h"
018
2021-01-11
op
019
2021-01-11
op
#include <ctype.h>
020
2021-01-11
op
#include <string.h>
021
2021-01-11
op
022
2021-01-11
op
static inline int
023
2021-01-11
op
unreserved(int p)
024
2021-01-11
op
{
025
2021-01-11
op
return isalnum(p)
026
2021-01-11
op
|| p == '-'
027
2021-01-11
op
|| p == '.'
028
2021-01-11
op
|| p == '_'
029
2021-01-11
op
|| p == '~';
030
2021-01-11
op
}
031
2021-01-11
op
032
2021-01-11
op
static inline int
033
2021-01-11
op
sub_delimiters(int p)
034
2021-01-11
op
{
035
2021-01-11
op
return p == '!'
036
2021-01-11
op
|| p == '$'
037
2021-01-11
op
|| p == '&'
038
2021-01-11
op
|| p == '\''
039
2021-01-11
op
|| p == '('
040
2021-01-11
op
|| p == ')'
041
2021-01-11
op
|| p == '*'
042
2021-01-11
op
|| p == '+'
043
2021-01-11
op
|| p == ','
044
2021-01-11
op
|| p == ';'
045
2021-01-11
op
|| p == '=';
046
2021-02-05
op
}
047
2021-02-05
op
048
2021-02-05
op
static int
049
2021-02-05
op
valid_pct_enc_string(char *s)
050
2021-02-05
op
{
051
2021-02-05
op
if (*s != '%')
052
2021-02-05
op
return 1;
053
2021-02-05
op
054
2021-02-05
op
if (!isxdigit(s[1]) || !isxdigit(s[2]))
055
2021-02-05
op
return 0;
056
2021-02-05
op
057
2021-02-05
op
if (s[1] == '0' && s[2] == '0')
058
2021-02-05
op
return 0;
059
2021-02-05
op
060
2021-02-05
op
return 1;
061
2021-02-05
op
}
062
2021-02-05
op
063
2021-02-05
op
static int
064
2021-02-05
op
valid_pct_encoded(struct parser *p)
065
2021-02-05
op
{
066
2021-02-05
op
if (p->iri[0] != '%')
067
2021-02-05
op
return 0;
068
2021-02-05
op
069
2021-02-05
op
if (!valid_pct_enc_string(p->iri)) {
070
2021-02-05
op
p->err = "illegal percent-encoding";
071
2021-02-05
op
return 0;
072
2021-02-05
op
}
073
2021-02-05
op
074
2021-02-05
op
p->iri += 2;
075
2021-02-05
op
return 1;
076
2021-01-11
op
}
077
2021-01-11
op
078
2021-02-07
op
static void
079
2021-02-07
op
pct_decode(char *s)
080
2021-02-07
op
{
081
2021-02-07
op
sscanf(s+1, "%2hhx", s);
082
2021-02-07
op
memmove(s+1, s+3, strlen(s+3)+1);
083
2021-02-07
op
}
084
2021-02-07
op
085
2021-01-11
op
static int
086
2021-01-11
op
parse_pct_encoded(struct parser *p)
087
2021-01-11
op
{
088
2021-01-28
op
if (p->iri[0] != '%')
089
2021-01-11
op
return 0;
090
2021-01-11
op
091
2021-02-05
op
if (!valid_pct_enc_string(p->iri)) {
092
2021-01-11
op
p->err = "illegal percent-encoding";
093
2021-01-11
op
return 0;
094
2021-01-11
op
}
095
2021-01-11
op
096
2021-02-07
op
pct_decode(p->iri);
097
2021-01-11
op
if (*p->iri == '\0') {
098
2021-01-11
op
p->err = "illegal percent-encoding";
099
2021-01-11
op
return 0;
100
2021-01-11
op
}
101
2021-01-11
op
102
2021-01-11
op
return 1;
103
2021-01-11
op
}
104
2021-01-11
op
105
2021-01-11
op
/* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) "://" */
106
2021-01-11
op
static int
107
2021-01-11
op
parse_scheme(struct parser *p)
108
2021-01-11
op
{
109
2021-01-11
op
p->parsed->schema = p->iri;
110
2021-01-11
op
111
2021-01-11
op
if (!isalpha(*p->iri)) {
112
2021-01-11
op
p->err = "illegal character in scheme";
113
2021-01-11
op
return 0;
114
2021-01-11
op
}
115
2021-01-11
op
116
2021-01-13
op
do {
117
2021-07-07
op
/*
118
2021-07-07
op
* normalize the scheme (i.e. lowercase it)
119
2021-01-13
op
*
120
2021-01-13
op
* XXX: since we cannot have good things, tolower
121
2021-01-16
op
* behaviour depends on the LC_CTYPE locale. The good
122
2021-01-16
op
* news is that we're sure p->iri points to something
123
2021-01-16
op
* that's in the ASCII range, so tolower can't
124
2021-07-07
op
* mis-behave on some systems due to the locale.
125
2021-07-07
op
*/
126
2021-01-13
op
*p->iri = tolower(*p->iri);
127
2021-01-13
op
p->iri++;
128
2021-01-13
op
} while (isalnum(*p->iri)
129
2021-01-11
op
|| *p->iri == '+'
130
2021-01-11
op
|| *p->iri == '-'
131
2021-01-13
op
|| *p->iri == '.');
132
2021-01-11
op
133
2021-01-11
op
if (*p->iri != ':') {
134
2021-01-11
op
p->err = "illegal character in scheme";
135
2021-01-11
op
return 0;
136
2021-01-11
op
}
137
2021-01-11
op
138
2021-01-11
op
*p->iri = '\0';
139
2021-01-15
op
if (p->iri[1] != '/' || p->iri[2] != '/') {
140
2021-01-11
op
p->err = "invalid marker after scheme";
141
2021-01-11
op
return 0;
142
2021-01-11
op
}
143
2021-01-11
op
144
2021-01-15
op
p->iri += 3;
145
2021-01-11
op
return 1;
146
2021-01-11
op
}
147
2021-01-11
op
148
2021-01-11
op
/* *DIGIT */
149
2021-01-11
op
static int
150
2021-01-11
op
parse_port(struct parser *p)
151
2021-01-11
op
{
152
2021-01-11
op
uint32_t i = 0;
153
2021-01-11
op
154
2021-01-11
op
p->parsed->port = p->iri;
155
2021-01-11
op
156
2021-01-11
op
for (; isdigit(*p->iri); p->iri++) {
157
2021-01-11
op
i = i * 10 + *p->iri - '0';
158
2021-01-11
op
if (i > UINT16_MAX) {
159
2021-01-11
op
p->err = "port number too large";
160
2021-01-11
op
return 0;
161
2021-01-11
op
}
162
2021-01-11
op
}
163
2021-01-11
op
164
2021-01-11
op
if (*p->iri != '/' && *p->iri != '\0') {
165
2021-01-11
op
p->err = "illegal character in port number";
166
2021-01-11
op
return 0;
167
2021-01-11
op
}
168
2021-01-11
op
169
2021-01-11
op
p->parsed->port_no = i;
170
2021-01-11
op
171
2021-01-11
op
if (*p->iri != '\0') {
172
2021-01-11
op
*p->iri = '\0';
173
2021-01-11
op
p->iri++;
174
2021-01-11
op
}
175
2021-01-11
op
176
2021-01-11
op
return 1;
177
2021-01-11
op
}
178
2021-01-11
op
179
2021-01-11
op
/* TODO: add support for ip-literal and ipv4addr ? */
180
2021-01-11
op
/* *( unreserved / sub-delims / pct-encoded ) */
181
2021-01-11
op
static int
182
2021-01-11
op
parse_authority(struct parser *p)
183
2021-01-11
op
{
184
2021-01-11
op
p->parsed->host = p->iri;
185
2021-01-11
op
186
2021-01-11
op
while (unreserved(*p->iri)
187
2021-01-11
op
|| sub_delimiters(*p->iri)
188
2021-01-29
op
|| parse_pct_encoded(p)
189
2021-01-29
op
|| valid_multibyte_utf8(p)) {
190
2021-01-15
op
/* normalize the host name. */
191
2021-01-15
op
if (*p->iri < 0x7F)
192
2021-01-15
op
*p->iri = tolower(*p->iri);
193
2021-01-11
op
p->iri++;
194
2021-01-15
op
}
195
2021-01-11
op
196
2021-01-11
op
if (p->err != NULL)
197
2021-01-11
op
return 0;
198
2021-01-11
op
199
2021-01-11
op
if (*p->iri == ':') {
200
2021-01-11
op
*p->iri = '\0';
201
2021-01-11
op
p->iri++;
202
2021-01-11
op
return parse_port(p);
203
2021-01-15
op
} else
204
2021-01-15
op
p->parsed->port_no = 1965;
205
2021-01-11
op
206
2021-01-11
op
if (*p->iri == '/') {
207
2021-01-11
op
*p->iri = '\0';
208
2021-01-11
op
p->iri++;
209
2021-01-11
op
return 1;
210
2021-01-11
op
}
211
2021-01-11
op
212
2021-01-11
op
if (*p->iri == '\0')
213
2021-01-11
op
return 1;
214
2021-01-11
op
215
2021-01-11
op
p->err = "illegal character in authority section";
216
2021-01-11
op
return 0;
217
2021-01-11
op
}
218
2021-01-11
op
219
2021-07-07
op
/*
220
2021-07-07
op
* Routine for path_clean. Elide the pointed .. with the preceding
221
2021-01-11
op
* element. Return 0 if it's not possible. incr is the length of
222
2021-07-07
op
* the increment, 3 for ../ and 2 for ..
223
2021-07-07
op
*/
224
2021-01-11
op
static int
225
2021-01-11
op
path_elide_dotdot(char *path, char *i, int incr)
226
2021-01-11
op
{
227
2021-01-11
op
char *j;
228
2021-01-11
op
229
2021-01-11
op
if (i == path)
230
2021-01-11
op
return 0;
231
2021-01-11
op
for (j = i-2; j != path && *j != '/'; j--)
232
2021-10-18
op
/* noop */ ;
233
2021-01-11
op
if (*j == '/')
234
2021-01-11
op
j++;
235
2021-01-11
op
i += incr;
236
2021-01-11
op
memmove(j, i, strlen(i)+1);
237
2021-01-11
op
return 1;
238
2021-01-11
op
}
239
2021-01-11
op
240
2021-01-11
op
/*
241
2021-01-11
op
* Use an algorithm similar to the one implemented in go' path.Clean:
242
2021-01-11
op
*
243
2021-01-11
op
* 1. Replace multiple slashes with a single slash
244
2021-01-11
op
* 2. Eliminate each . path name element
245
2021-01-11
op
* 3. Eliminate each inner .. along with the non-.. element that precedes it
246
2021-01-11
op
* 4. Eliminate trailing .. if possible or error (go would only discard)
247
2021-01-11
op
*
248
2021-01-11
op
* Unlike path.Clean, this function return the empty string if the
249
2021-01-11
op
* original path is equivalent to "/".
250
2021-01-11
op
*/
251
2021-01-11
op
static int
252
2021-01-11
op
path_clean(char *path)
253
2021-01-11
op
{
254
2021-01-11
op
char *i;
255
2021-01-11
op
256
2021-01-11
op
/* 1. replace multiple slashes with a single one */
257
2021-01-11
op
for (i = path; *i; ++i) {
258
2021-01-11
op
if (*i == '/' && *(i+1) == '/') {
259
2021-01-11
op
memmove(i, i+1, strlen(i)); /* move also the \0 */
260
2021-01-11
op
i--;
261
2021-01-11
op
}
262
2021-01-11
op
}
263
2021-01-11
op
264
2021-01-11
op
/* 2. eliminate each . path name element */
265
2021-01-11
op
for (i = path; *i; ++i) {
266
2021-01-11
op
if ((i == path || *i == '/') &&
267
2021-01-11
op
*i != '.' && i[1] == '.' && i[2] == '/') {
268
2021-01-11
op
/* move also the \0 */
269
2021-01-11
op
memmove(i, i+2, strlen(i)-1);
270
2021-01-11
op
i--;
271
2021-01-11
op
}
272
2021-01-11
op
}
273
2021-01-11
op
if (!strcmp(path, ".") || !strcmp(path, "/.")) {
274
2021-01-11
op
*path = '\0';
275
2021-01-11
op
return 1;
276
2021-01-11
op
}
277
2021-01-11
op
278
2021-01-11
op
/* 3. eliminate each inner .. along with the preceding non-.. */
279
2021-04-12
op
for (i = strstr(path, "../"); i != NULL; i = strstr(path, "..")) {
280
2021-04-12
op
/* break if we've found a trailing .. */
281
2021-04-12
op
if (i[2] == '\0')
282
2021-04-12
op
break;
283
2021-01-11
op
if (!path_elide_dotdot(path, i, 3))
284
2021-01-11
op
return 0;
285
2021-04-12
op
}
286
2021-01-11
op
287
2021-01-11
op
/* 4. eliminate trailing ..*/
288
2021-01-11
op
if ((i = strstr(path, "..")) != NULL)
289
2021-01-11
op
if (!path_elide_dotdot(path, i, 2))
290
2021-01-11
op
return 0;
291
2021-01-11
op
292
2021-01-11
op
return 1;
293
2021-01-11
op
}
294
2021-01-11
op
295
2021-01-11
op
static int
296
2021-01-11
op
parse_query(struct parser *p)
297
2021-01-11
op
{
298
2021-01-11
op
p->parsed->query = p->iri;
299
2021-01-11
op
if (*p->iri == '\0')
300
2021-01-11
op
return 1;
301
2021-01-11
op
302
2021-01-11
op
while (unreserved(*p->iri)
303
2021-01-11
op
|| sub_delimiters(*p->iri)
304
2021-01-11
op
|| *p->iri == '/'
305
2021-01-11
op
|| *p->iri == '?'
306
2021-02-06
op
|| *p->iri == ':'
307
2021-02-06
op
|| *p->iri == '@'
308
2021-02-05
op
|| valid_pct_encoded(p)
309
2021-01-11
op
|| valid_multibyte_utf8(p))
310
2021-01-11
op
p->iri++;
311
2021-01-11
op
312
2021-01-11
op
if (p->err != NULL)
313
2021-01-11
op
return 0;
314
2021-01-11
op
315
2021-01-11
op
if (*p->iri != '\0' && *p->iri != '#') {
316
2021-01-11
op
p->err = "illegal character in query";
317
2021-01-11
op
return 0;
318
2021-01-11
op
}
319
2021-01-11
op
320
2021-01-11
op
if (*p->iri != '\0') {
321
2021-01-11
op
*p->iri = '\0';
322
2021-01-11
op
p->iri++;
323
2021-01-11
op
}
324
2021-01-11
op
325
2021-01-11
op
return 1;
326
2021-01-11
op
}
327
2021-01-11
op
328
2021-01-11
op
/* don't even bother */
329
2021-01-11
op
static int
330
2021-01-11
op
parse_fragment(struct parser *p)
331
2021-01-11
op
{
332
2021-01-11
op
p->parsed->fragment = p->iri;
333
2021-01-11
op
return 1;
334
2021-01-11
op
}
335
2021-01-11
op
336
2021-01-11
op
/* XXX: is it too broad? */
337
2021-01-11
op
/* *(pchar / "/") */
338
2021-01-11
op
static int
339
2021-01-11
op
parse_path(struct parser *p)
340
2021-01-11
op
{
341
2021-01-11
op
char c;
342
2021-01-21
op
343
2021-01-21
op
/* trim initial slashes */
344
2021-01-21
op
while (*p->iri == '/')
345
2021-01-21
op
p->iri++;
346
2021-01-11
op
347
2021-01-11
op
p->parsed->path = p->iri;
348
2021-01-11
op
if (*p->iri == '\0') {
349
2021-01-11
op
p->parsed->query = p->parsed->fragment = p->iri;
350
2021-01-11
op
return 1;
351
2021-01-11
op
}
352
2021-01-11
op
353
2021-01-11
op
while (unreserved(*p->iri)
354
2021-01-11
op
|| sub_delimiters(*p->iri)
355
2022-07-04
op
|| *p->iri == '@'
356
2022-07-04
op
|| *p->iri == ':'
357
2021-01-11
op
|| *p->iri == '/'
358
2021-01-11
op
|| parse_pct_encoded(p)
359
2021-01-11
op
|| valid_multibyte_utf8(p))
360
2021-01-11
op
p->iri++;
361
2021-01-11
op
362
2021-01-11
op
if (p->err != NULL)
363
2021-01-11
op
return 0;
364
2021-01-11
op
365
2021-01-11
op
if (*p->iri != '\0' && *p->iri != '?' && *p->iri != '#') {
366
2021-01-11
op
p->err = "illegal character in path";
367
2021-01-11
op
return 0;
368
2021-01-11
op
}
369
2021-01-11
op
370
2021-01-11
op
if (*p->iri != '\0') {
371
2021-01-11
op
c = *p->iri;
372
2021-01-11
op
*p->iri = '\0';
373
2021-01-11
op
p->iri++;
374
2021-01-11
op
375
2021-01-11
op
if (c == '#') {
376
2021-01-11
op
if (!parse_fragment(p))
377
2021-01-11
op
return 0;
378
2021-01-11
op
} else
379
2021-01-11
op
if (!parse_query(p) || !parse_fragment(p))
380
2021-01-11
op
return 0;
381
2021-01-11
op
}
382
2021-01-11
op
383
2021-01-11
op
if (!path_clean(p->parsed->path)) {
384
2021-01-11
op
p->err = "illegal path";
385
2021-01-11
op
return 0;
386
2021-01-11
op
}
387
2021-01-11
op
388
2021-01-11
op
return 1;
389
2021-01-11
op
}
390
2021-01-11
op
391
2021-01-11
op
int
392
2021-01-11
op
parse_iri(char *iri, struct iri *ret, const char **err_ret)
393
2021-01-11
op
{
394
2021-01-11
op
char *end;
395
2021-09-24
op
struct parser p = {
396
2021-09-24
op
.iri = iri,
397
2021-09-24
op
.parsed = ret,
398
2021-09-24
op
.err = NULL,
399
2021-09-24
op
};
400
2021-01-11
op
401
2021-09-24
op
memset(ret, 0, sizeof(*ret));
402
2021-01-11
op
403
2021-01-11
op
/* initialize optional stuff to the empty string */
404
2021-01-11
op
end = iri + strlen(iri);
405
2021-01-31
op
p.parsed->host = end;
406
2021-01-11
op
p.parsed->port = end;
407
2021-01-11
op
p.parsed->path = end;
408
2021-01-11
op
p.parsed->query = end;
409
2021-01-11
op
p.parsed->fragment = end;
410
2021-01-11
op
411
2021-01-11
op
if (!parse_scheme(&p) || !parse_authority(&p) || !parse_path(&p)) {
412
2021-01-11
op
*err_ret = p.err;
413
2021-01-11
op
return 0;
414
2021-01-11
op
}
415
2021-01-11
op
416
2021-01-11
op
*err_ret = NULL;
417
2021-01-11
op
return 1;
418
2021-01-11
op
}
419
2021-01-11
op
420
2021-01-11
op
int
421
2021-02-01
op
serialize_iri(struct iri *i, char *buf, size_t len)
422
2021-02-01
op
{
423
2021-06-16
op
size_t l = 0;
424
2021-02-01
op
425
2021-02-01
op
/* in ex.c we receive empty "" strings as NULL */
426
2021-02-01
op
if (i->schema == NULL || i->host == NULL) {
427
2021-02-01
op
memset(buf, 0, len);
428
2021-02-01
op
return 0;
429
2021-02-01
op
}
430
2021-02-01
op
431
2021-02-01
op
strlcpy(buf, i->schema, len);
432
2021-02-01
op
strlcat(buf, "://", len);
433
2021-10-18
op
strlcat(buf, i->host, len);
434
2021-02-01
op
strlcat(buf, "/", len);
435
2021-02-01
op
436
2021-02-01
op
if (i->path != NULL)
437
2021-02-01
op
l = strlcat(buf, i->path, len);
438
2021-02-01
op
439
2021-02-01
op
if (i->query != NULL && *i->query != '\0') {
440
2021-02-01
op
strlcat(buf, "?", len);
441
2021-02-01
op
l = strlcat(buf, i->query, len);
442
2021-02-01
op
}
443
2021-02-01
op
444
2021-02-01
op
return l < len;
445
2021-01-11
op
}
446
2022-07-04
op
447
2022-07-04
op
int
448
2022-07-04
op
encode_path(char *buf, size_t len, const char *path)
449
2022-07-04
op
{
450
2022-07-04
op
char *p = buf;
451
2022-07-04
op
int a, b;
452
2022-07-04
op
453
2022-07-04
op
memset(buf, 0, len);
454
2022-07-04
op
while (*path != '\0') {
455
2022-07-04
op
if (len == 1) /* NUL */
456
2022-07-04
op
return -1;
457
2021-02-07
op
458
2022-07-04
op
if (unreserved(*path) ||
459
2022-07-04
op
sub_delimiters(*path) ||
460
2022-07-04
op
*path == '@' ||
461
2022-07-04
op
*path == ':' ||
462
2022-07-04
op
*path == '/') {
463
2022-07-04
op
*p++ = *path++;
464
2022-07-04
op
len--;
465
2022-07-04
op
} else if (len < 4)
466
2022-07-04
op
return -1;
467
2022-07-04
op
else {
468
2022-07-04
op
a = (*path & 0xF0) >> 4;
469
2022-07-04
op
b = (*path & 0x0F);
470
2022-07-04
op
471
2022-07-04
op
p[0] = '%';
472
2022-07-04
op
p[1] = a <= 9 ? ('0' + a) : ('7' + a);
473
2022-07-04
op
p[2] = b <= 9 ? ('0' + b) : ('7' + b);
474
2022-07-04
op
475
2022-07-04
op
path++;
476
2022-07-04
op
p += 3;
477
2022-07-04
op
len -= 3;
478
2022-07-04
op
}
479
2022-07-04
op
}
480
2022-07-04
op
481
2022-07-04
op
return 0;
482
2022-07-04
op
}
483
2022-07-04
op
484
2021-02-07
op
char *
485
2021-02-07
op
pct_decode_str(char *s)
486
2021-02-07
op
{
487
2021-02-07
op
char *t;
488
2021-02-07
op
489
2021-02-07
op
for (t = s; *t; ++t) {
490
2021-02-07
op
if (*t == '%' && valid_pct_enc_string(t))
491
2021-02-07
op
pct_decode(t);
492
2021-02-07
op
}
493
2021-02-07
op
494
2021-02-07
op
return s;
495
2021-02-07
op
}
Omar Polo