]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/webfs/url.c
snoopy(8): avoid extra spaces in dhcp filter output
[plan9front.git] / sys / src / cmd / webfs / url.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <9p.h>
7
8 #include "dat.h"
9 #include "fns.h"
10
11 static char reserved[] = "%:/?#[]@!$&'()*+,;=";
12
13 static int
14 dhex(char c)
15 {
16         if('0' <= c && c <= '9')
17                 return c-'0';
18         if('a' <= c && c <= 'f')
19                 return c-'a'+10;
20         if('A' <= c && c <= 'F')
21                 return c-'A'+10;
22         return 0;
23 }
24
25 static char*
26 unescape(char *s, char *spec)
27 {
28         char *r, *w;
29         uchar x;
30
31         if(s == nil)
32                 return s;
33         for(r=w=s; x = *r; r++){
34                 if(x == '%' && isxdigit(r[1]) && isxdigit(r[2])){
35                         x = (dhex(r[1])<<4)|dhex(r[2]);
36                         if(spec && strchr(spec, x)){
37                                 *w++ = '%';
38                                 *w++ = toupper(r[1]);
39                                 *w++ = toupper(r[2]);
40                         }
41                         else
42                                 *w++ = x;
43                         r += 2;
44                         continue;
45                 }
46                 *w++ = x;
47         }
48         *w = 0;
49         return s;
50 }
51
52 int
53 Efmt(Fmt *f)
54 {
55         char *s, *spec;
56         Str2 s2;
57
58         s2 = va_arg(f->args, Str2);
59         s = s2.s1;
60         spec = s2.s2;
61         for(; *s; s++)
62                 if(*s == '%' && isxdigit(s[1]) && isxdigit(s[2])){
63                         fmtprint(f, "%%%c%c", toupper(s[1]), toupper(s[2]));
64                         s += 2;
65                 }
66                 else if(isalnum(*s) || strchr(".-_~!$&'()*,;=", *s) || strchr(spec, *s))
67                         fmtprint(f, "%c", *s);
68                 else
69                         fmtprint(f, "%%%.2X", *s & 0xff);
70         return 0;
71 }
72
73 int
74 Nfmt(Fmt *f)
75 {
76         char d[Domlen], *s;
77
78         s = va_arg(f->args, char*);
79         if(utf2idn(s, d, sizeof(d)) >= 0)
80                 s = d;
81         fmtprint(f, "%s", s);
82         return 0;
83 }
84
85 int
86 Mfmt(Fmt *f)
87 {
88         char *s = va_arg(f->args, char*);
89         fmtprint(f, (*s != '[' && strchr(s, ':') != nil)? "[%s]" : "%s", s);
90         return 0;
91 }
92
93 int
94 Ufmt(Fmt *f)
95 {
96         char *s;
97         Url *u;
98
99         if((u = va_arg(f->args, Url*)) == nil)
100                 return fmtprint(f, "nil");
101         if(u->scheme)
102                 fmtprint(f, "%s:", u->scheme);
103         if(u->user || u->host)
104                 fmtprint(f, "//");
105         if(u->user){
106                 fmtprint(f, "%E", (Str2){u->user, ""});
107                 if(u->pass)
108                         fmtprint(f, ":%E", (Str2){u->pass, ""});
109                 fmtprint(f, "@");
110         }
111         if(u->host){
112                 fmtprint(f, "%]", u->host);
113                 if(u->port)
114                         fmtprint(f, ":%s", u->port);
115         }
116         if(s = Upath(u))
117                 fmtprint(f, "%E", (Str2){s, "/:@+"});
118         if(u->query)
119                 fmtprint(f, "?%E", (Str2){u->query, "/:@"});
120         if(u->fragment)
121                 fmtprint(f, "#%E", (Str2){u->fragment, "/:@?+"});
122         return 0;
123 }
124
125 char*
126 Upath(Url *u)
127 {
128         if(u){
129                 if(u->path)
130                         return u->path;
131                 if(u->user || u->host)
132                         return "/";
133         }
134         return nil;
135 }
136
137 static char*
138 remdot(char *s)
139 {
140         char *b, *d, *p;
141         int dir, n;
142
143         dir = 1;
144         b = d = s;
145         if(*s == '/')
146                 s++;
147         for(; s; s = p){
148                 if(p = strchr(s, '/'))
149                         *p++ = 0;
150                 if(*s == '.' && ((s[1] == 0) || (s[1] == '.' && s[2] == 0))){
151                         if(s[1] == '.')
152                                 while(d > b)
153                                         if(*--d == '/')
154                                                 break;
155                         dir = 1;
156                         continue;
157                 } else
158                         dir = (p != nil);
159                 if((n = strlen(s)) > 0)
160                         memmove(d+1, s, n);
161                 *d++ = '/';
162                 d += n;
163         }
164         if(dir)
165                 *d++ = '/';
166         *d = 0;
167         return b;
168 }
169
170 static char*
171 abspath(char *s, char *b)
172 {
173         char *x, *a;
174
175         if(b && *b){
176                 if(s == nil || *s == 0)
177                         return estrdup(b);
178                 if(*s != '/' && (x = strrchr(b, '/'))){
179                         a = emalloc((x - b) + strlen(s) + 4);
180                         sprint(a, "%.*s/%s", utfnlen(b, x - b), b, s);
181                         return remdot(a);
182                 }
183         }
184         if(s && *s){
185                 if(*s != '/')
186                         return estrdup(s);
187                 a = emalloc(strlen(s) + 4);
188                 sprint(a, "%s", s);
189                 return remdot(a);
190         }
191         return nil;
192 }
193
194 static void
195 pstrdup(char **p)
196 {
197         if(p == nil || *p == nil)
198                 return;
199         if(**p == 0){
200                 *p = nil;
201                 return;
202         }
203         *p = estrdup(*p);
204 }
205
206 static char*
207 mklowcase(char *s)
208 {
209         char *cp;
210         Rune r;
211         
212         if(s == nil)
213                 return s;
214         cp = s;
215         while(*cp != 0){
216                 chartorune(&r, cp);
217                 r = tolowerrune(r);
218                 cp += runetochar(cp, &r);
219         }
220         return s;
221 }
222
223 Url*
224 url(char *s, Url *b)
225 {
226         char *t, *p, *x, *y;
227         Url *u;
228
229         if(s == nil)
230                 s = "";
231         t = nil;
232         s = p = estrdup(s);
233         u = emalloc(sizeof(*u));
234         for(; *p; p++){
235                 if(*p == ':'){
236                         if(p == s)
237                                 break;
238                         *p++ = 0;
239                         u->scheme = s;
240                         b = nil;
241                         goto Abs;
242                 }
243                 if(!isalpha(*p))
244                         if((p == s) || ((!isdigit(*p) && strchr("+-.", *p) == nil)))
245                                 break;
246         }
247         p = s;
248         if(b){
249                 switch(*p){
250                 case 0:
251                         memmove(u, b, sizeof(*u));
252                         goto Out;
253                 case '#':
254                         memmove(u, b, sizeof(*u));
255                         u->fragment = p+1;
256                         goto Out;
257                 case '?':
258                         memmove(u, b, sizeof(*u));
259                         u->fragment = u->query = nil;
260                         break;
261                 case '/':
262                         if(p[1] == '/'){
263                                 u->scheme = b->scheme;
264                                 b = nil;
265                                 break;
266                         }
267                 default:
268                         memmove(u, b, sizeof(*u));
269                         u->fragment = u->query = u->path = nil;
270                         break;
271                 }
272         }
273 Abs:
274         if(x = strchr(p, '#')){
275                 *x = 0;
276                 u->fragment = x+1;
277         }
278         if(x = strchr(p, '?')){
279                 *x = 0;
280                 u->query = x+1;
281         }
282         if(p[0] == '/' && p[1] == '/'){
283                 p += 2;
284                 if(x = strchr(p, '/')){
285                         u->path = t = abspath(x, Upath(b));
286                         *x = 0;
287                 }
288                 if(x = strchr(p, '@')){
289                         *x = 0;
290                         if(y = strchr(p, ':')){
291                                 *y = 0;
292                                 u->pass = y+1;
293                         }
294                         u->user = p;
295                         p = x+1;
296                 }
297                 if((x = strrchr(p, ']')) == nil)
298                         x = p;
299                 if(x = strrchr(x, ':')){
300                         *x = 0;
301                         u->port = x+1;
302                 }
303                 if(x = strchr(p, '[')){
304                         p = x+1;
305                         if(y = strchr(p, ']'))
306                                 *y = 0;
307                 }
308                 u->host = p;
309         } else {
310                 u->path = t = abspath(p, Upath(b));
311         }
312 Out:
313         pstrdup(&u->scheme);
314         pstrdup(&u->user);
315         pstrdup(&u->pass);
316         pstrdup(&u->host);
317         pstrdup(&u->port);
318         pstrdup(&u->path);
319         pstrdup(&u->query);
320         pstrdup(&u->fragment);
321         free(s);
322         free(t);
323
324         /* the + character encodes space only in query part */
325         if(s = u->query)
326                 while(s = strchr(s, '+'))
327                         *s++ = ' ';
328
329         if(s = u->host){
330                 t = emalloc(Domlen);
331                 if(idn2utf(s, t, Domlen) >= 0){
332                         u->host = estrdup(t);
333                         free(s);
334                 }
335                 free(t);
336         }
337
338         unescape(u->user, nil);
339         unescape(u->pass, nil);
340         unescape(u->path, reserved);
341         unescape(u->query, reserved);
342         unescape(u->fragment, reserved);
343         mklowcase(u->scheme);
344         mklowcase(u->host);
345         mklowcase(u->port);
346
347         return u;
348 }
349
350 Url*
351 saneurl(Url *u)
352 {
353         if(u == nil || u->scheme == nil || u->host == nil || Upath(u) == nil){
354                 freeurl(u);
355                 return nil;
356         }
357         if(u->port){
358                 /* remove default ports */
359                 switch(atoi(u->port)){
360                 case 21:        if(!strcmp(u->scheme, "ftp"))   goto Defport; break;
361                 case 70:        if(!strcmp(u->scheme, "gopher"))goto Defport; break;
362                 case 80:        if(!strcmp(u->scheme, "http"))  goto Defport; break;
363                 case 443:       if(!strcmp(u->scheme, "https")) goto Defport; break;
364                 default:        if(!strcmp(u->scheme, u->port)) goto Defport; break;
365                 Defport:
366                         free(u->port);
367                         u->port = nil;
368                 }
369         }
370         return u;
371 }
372
373 int
374 matchurl(Url *u, Url *s)
375 {
376         if(u){
377                 char *a, *b;
378
379                 if(s == nil)
380                         return 0;
381                 if(u->scheme && (s->scheme == nil || strcmp(u->scheme, s->scheme)))
382                         return 0;
383                 if(u->user && (s->user == nil || strcmp(u->user, s->user)))
384                         return 0;
385                 if(u->host && (s->host == nil || strcmp(u->host, s->host)))
386                         return 0;
387                 if(u->port && (s->port == nil || strcmp(u->port, s->port)))
388                         return 0;
389                 if(a = Upath(u)){
390                         b = Upath(s);
391                         if(b == nil || strncmp(a, b, strlen(a)))
392                                 return 0;
393                 }
394         }
395         return 1;
396 }
397
398 void
399 freeurl(Url *u)
400 {
401         if(u == nil)
402                 return;
403         free(u->scheme);
404         free(u->user);
405         free(u->pass);
406         free(u->host);
407         free(u->port);
408         free(u->path);
409         free(u->query);
410         free(u->fragment);
411         free(u);
412 }