]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/webfs/url.c
webfs: don't handle unknown status codes as continuations, reset status when handling...
[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, *s;
77
78         s = va_arg(f->args, char*);
79         d = emalloc(Domlen);
80         if(utf2idn(s, d, Domlen) == nil)
81                 d = s;
82         fmtprint(f, "%s", d);
83         if(d != s)
84                 free(d);
85         return 0;
86 }
87
88 int
89 Ufmt(Fmt *f)
90 {
91         char *s;
92         Url *u;
93
94         if((u = va_arg(f->args, Url*)) == nil)
95                 return fmtprint(f, "nil");
96         if(u->scheme)
97                 fmtprint(f, "%s:", u->scheme);
98         if(u->user || u->host)
99                 fmtprint(f, "//");
100         if(u->user){
101                 fmtprint(f, "%E", (Str2){u->user, ""});
102                 if(u->pass)
103                         fmtprint(f, ":%E", (Str2){u->pass, ""});
104                 fmtprint(f, "@");
105         }
106         if(u->host){
107                 fmtprint(f, strchr(u->host, ':') ? "[%s]" : "%s", u->host);
108                 if(u->port)
109                         fmtprint(f, ":%s", u->port);
110         }
111         if(s = Upath(u))
112                 fmtprint(f, "%E", (Str2){s, "/:@+"});
113         if(u->query)
114                 fmtprint(f, "?%E", (Str2){u->query, "/:@"});
115         if(u->fragment)
116                 fmtprint(f, "#%E", (Str2){u->fragment, "/:@?+"});
117         return 0;
118 }
119
120 char*
121 Upath(Url *u)
122 {
123         if(u){
124                 if(u->path)
125                         return u->path;
126                 if(u->user || u->host)
127                         return "/";
128         }
129         return nil;
130 }
131
132 static char*
133 remdot(char *s)
134 {
135         char *b, *d, *p;
136         int dir, n;
137
138         dir = 1;
139         b = d = s;
140         if(*s == '/')
141                 s++;
142         for(; s; s = p){
143                 if(p = strchr(s, '/'))
144                         *p++ = 0;
145                 if(*s == '.' && ((s[1] == 0) || (s[1] == '.' && s[2] == 0))){
146                         if(s[1] == '.')
147                                 while(d > b)
148                                         if(*--d == '/')
149                                                 break;
150                         dir = 1;
151                         continue;
152                 } else
153                         dir = (p != nil);
154                 if((n = strlen(s)) > 0)
155                         memmove(d+1, s, n);
156                 *d++ = '/';
157                 d += n;
158         }
159         if(dir)
160                 *d++ = '/';
161         *d = 0;
162         return b;
163 }
164
165 static char*
166 abspath(char *s, char *b)
167 {
168         char *x, *a;
169
170         if(b && *b){
171                 if(s == nil || *s == 0)
172                         return estrdup(b);
173                 if(*s != '/' && (x = strrchr(b, '/'))){
174                         a = emalloc((x - b) + strlen(s) + 4);
175                         sprint(a, "%.*s/%s", (int)(x - b), b, s);
176                         return remdot(a);
177                 }
178         }
179         if(s && *s){
180                 if(*s != '/')
181                         return estrdup(s);
182                 a = emalloc(strlen(s) + 4);
183                 sprint(a, "%s", s);
184                 return remdot(a);
185         }
186         return nil;
187 }
188
189 static void
190 pstrdup(char **p)
191 {
192         if(p == nil || *p == nil)
193                 return;
194         if(**p == 0){
195                 *p = nil;
196                 return;
197         }
198         *p = estrdup(*p);
199 }
200
201 static char*
202 mklowcase(char *s)
203 {
204         char *cp;
205         Rune r;
206         
207         if(s == nil)
208                 return s;
209         cp = s;
210         while(*cp != 0){
211                 chartorune(&r, cp);
212                 r = tolowerrune(r);
213                 cp += runetochar(cp, &r);
214         }
215         return s;
216 }
217
218 Url*
219 url(char *s, Url *b)
220 {
221         char *t, *p, *x, *y;
222         Url *u;
223
224         if(s == nil)
225                 s = "";
226         t = nil;
227         s = p = estrdup(s);
228         u = emalloc(sizeof(*u));
229         for(; *p; p++){
230                 if(*p == ':'){
231                         if(p == s)
232                                 break;
233                         *p++ = 0;
234                         u->scheme = s;
235                         b = nil;
236                         goto Abs;
237                 }
238                 if(!isalpha(*p))
239                         if((p == s) || ((!isdigit(*p) && strchr("+-.", *p) == nil)))
240                                 break;
241         }
242         p = s;
243         if(b){
244                 switch(*p){
245                 case 0:
246                         memmove(u, b, sizeof(*u));
247                         goto Out;
248                 case '#':
249                         memmove(u, b, sizeof(*u));
250                         u->fragment = p+1;
251                         goto Out;
252                 case '?':
253                         memmove(u, b, sizeof(*u));
254                         u->fragment = u->query = nil;
255                         break;
256                 case '/':
257                         if(p[1] == '/'){
258                                 u->scheme = b->scheme;
259                                 b = nil;
260                                 break;
261                         }
262                 default:
263                         memmove(u, b, sizeof(*u));
264                         u->fragment = u->query = u->path = nil;
265                         break;
266                 }
267         }
268 Abs:
269         if(x = strchr(p, '#')){
270                 *x = 0;
271                 u->fragment = x+1;
272         }
273         if(x = strchr(p, '?')){
274                 *x = 0;
275                 u->query = x+1;
276         }
277         if(p[0] == '/' && p[1] == '/'){
278                 p += 2;
279                 if(x = strchr(p, '/')){
280                         u->path = t = abspath(x, Upath(b));
281                         *x = 0;
282                 }
283                 if(x = strchr(p, '@')){
284                         *x = 0;
285                         if(y = strchr(p, ':')){
286                                 *y = 0;
287                                 u->pass = y+1;
288                         }
289                         u->user = p;
290                         p = x+1;
291                 }
292                 if((x = strrchr(p, ']')) == nil)
293                         x = p;
294                 if(x = strrchr(x, ':')){
295                         *x = 0;
296                         u->port = x+1;
297                 }
298                 if(x = strchr(p, '[')){
299                         p = x+1;
300                         if(y = strchr(p, ']'))
301                                 *y = 0;
302                 }
303                 u->host = p;
304         } else {
305                 u->path = t = abspath(p, Upath(b));
306         }
307 Out:
308         pstrdup(&u->scheme);
309         pstrdup(&u->user);
310         pstrdup(&u->pass);
311         pstrdup(&u->host);
312         pstrdup(&u->port);
313         pstrdup(&u->path);
314         pstrdup(&u->query);
315         pstrdup(&u->fragment);
316         free(s);
317         free(t);
318
319         /* the + character encodes space only in query part */
320         if(s = u->query)
321                 while(s = strchr(s, '+'))
322                         *s++ = ' ';
323
324         if(s = u->host){
325                 t = emalloc(Domlen);
326                 if(idn2utf(s, t, Domlen)){
327                         u->host = estrdup(t);
328                         free(s);
329                 }
330                 free(t);
331         }
332
333         unescape(u->user, nil);
334         unescape(u->pass, nil);
335         unescape(u->path, reserved);
336         unescape(u->query, reserved);
337         unescape(u->fragment, reserved);
338         mklowcase(u->scheme);
339         mklowcase(u->host);
340         mklowcase(u->port);
341
342         return u;
343 }
344
345 Url*
346 saneurl(Url *u)
347 {
348         if(u == nil || u->scheme == nil || u->host == nil || Upath(u) == nil){
349                 freeurl(u);
350                 return nil;
351         }
352         if(u->port){
353                 /* remove default ports */
354                 switch(atoi(u->port)){
355                 case 21:        if(!strcmp(u->scheme, "ftp"))   goto Defport; break;
356                 case 70:        if(!strcmp(u->scheme, "gopher"))goto Defport; break;
357                 case 80:        if(!strcmp(u->scheme, "http"))  goto Defport; break;
358                 case 443:       if(!strcmp(u->scheme, "https")) goto Defport; break;
359                 default:        if(!strcmp(u->scheme, u->port)) goto Defport; break;
360                 Defport:
361                         free(u->port);
362                         u->port = nil;
363                 }
364         }
365         return u;
366 }
367
368 int
369 matchurl(Url *u, Url *s)
370 {
371         if(u){
372                 char *a, *b;
373
374                 if(s == nil)
375                         return 0;
376                 if(u->scheme && (s->scheme == nil || strcmp(u->scheme, s->scheme)))
377                         return 0;
378                 if(u->user && (s->user == nil || strcmp(u->user, s->user)))
379                         return 0;
380                 if(u->host && (s->host == nil || strcmp(u->host, s->host)))
381                         return 0;
382                 if(u->port && (s->port == nil || strcmp(u->port, s->port)))
383                         return 0;
384                 if(a = Upath(u)){
385                         b = Upath(s);
386                         if(b == nil || strncmp(a, b, strlen(a)))
387                                 return 0;
388                 }
389         }
390         return 1;
391 }
392
393 void
394 freeurl(Url *u)
395 {
396         if(u == nil)
397                 return;
398         free(u->scheme);
399         free(u->user);
400         free(u->pass);
401         free(u->host);
402         free(u->port);
403         free(u->path);
404         free(u->query);
405         free(u->fragment);
406         free(u);
407 }