2011-03-30 12:46:40 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <fcall.h>
|
2012-01-11 15:17:54 +00:00
|
|
|
#include <thread.h>
|
2011-03-30 12:46:40 +00:00
|
|
|
#include <9p.h>
|
2012-01-11 15:17:54 +00:00
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
|
|
|
|
static int
|
2012-01-11 15:17:54 +00:00
|
|
|
dhex(char c)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
if('0' <= c && c <= '9')
|
|
|
|
return c-'0';
|
|
|
|
if('a' <= c && c <= 'f')
|
|
|
|
return c-'a'+10;
|
|
|
|
if('A' <= c && c <= 'F')
|
|
|
|
return c-'A'+10;
|
|
|
|
return 0;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
static char*
|
|
|
|
unescape(char *s, char *spec)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
char *r, *w;
|
|
|
|
uchar x;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
if(s == nil)
|
|
|
|
return s;
|
|
|
|
for(r=w=s; x = *r; r++){
|
|
|
|
if(x == '%' && isxdigit(r[1]) && isxdigit(r[2])){
|
|
|
|
x = (dhex(r[1])<<4)|dhex(r[2]);
|
|
|
|
if(x == 0 || (x > 0x1F && x < 0x7F && strchr(spec, x))){
|
|
|
|
*w++ = '%';
|
|
|
|
*w++ = toupper(r[1]);
|
|
|
|
*w++ = toupper(r[2]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*w++ = x;
|
|
|
|
r += 2;
|
|
|
|
continue;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
if(x == '+')
|
|
|
|
x = ' ';
|
|
|
|
*w++ = x;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
*w = 0;
|
|
|
|
return s;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
int
|
|
|
|
Efmt(Fmt *f)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
char *s, *spec;
|
|
|
|
Str2 s2;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
s2 = va_arg(f->args, Str2);
|
|
|
|
s = s2.s1;
|
|
|
|
spec = s2.s2;
|
|
|
|
for(; *s; s++)
|
|
|
|
if(*s == '%' && isxdigit(s[1]) && isxdigit(s[2])){
|
|
|
|
fmtprint(f, "%%%c%c", toupper(s[1]), toupper(s[2]));
|
|
|
|
s += 2;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
else if(isalnum(*s) || strchr(".-_~!$&'()*,;=", *s) || strchr(spec, *s))
|
|
|
|
fmtprint(f, "%c", *s);
|
|
|
|
else
|
|
|
|
fmtprint(f, "%%%.2X", *s & 0xff);
|
2011-03-30 12:46:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2012-01-11 15:17:54 +00:00
|
|
|
Ufmt(Fmt *f)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
char *s;
|
|
|
|
Url *u;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
if((u = va_arg(f->args, Url*)) == nil)
|
|
|
|
return fmtprint(f, "nil");
|
|
|
|
if(u->scheme)
|
|
|
|
fmtprint(f, "%s:", u->scheme);
|
|
|
|
if(u->user || u->host)
|
|
|
|
fmtprint(f, "//");
|
|
|
|
if(u->user){
|
|
|
|
fmtprint(f, "%E", (Str2){u->user, ""});
|
|
|
|
if(u->pass)
|
|
|
|
fmtprint(f, ":%E", (Str2){u->pass, ""});
|
|
|
|
fmtprint(f, "@");
|
|
|
|
}
|
|
|
|
if(u->host){
|
|
|
|
fmtprint(f, strchr(u->host, ':') ? "[%s]" : "%s", u->host);
|
|
|
|
if(u->port)
|
|
|
|
fmtprint(f, ":%s", u->port);
|
|
|
|
}
|
|
|
|
if(s = Upath(u))
|
|
|
|
fmtprint(f, "%E", (Str2){s, "/:@"});
|
|
|
|
if(u->query)
|
|
|
|
fmtprint(f, "?%E", (Str2){u->query, "/:@"});
|
|
|
|
if(u->fragment)
|
|
|
|
fmtprint(f, "#%E", (Str2){u->fragment, "/:@?"});
|
2011-03-30 12:46:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
char*
|
|
|
|
Upath(Url *u)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
if(u){
|
|
|
|
if(u->path)
|
|
|
|
return u->path;
|
|
|
|
if(u->user || u->host)
|
|
|
|
return "/";
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
return nil;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
static char*
|
|
|
|
remdot(char *s)
|
|
|
|
{
|
|
|
|
char *b, *d, *p;
|
|
|
|
int dir, n;
|
|
|
|
|
|
|
|
dir = 1;
|
|
|
|
b = d = s;
|
|
|
|
while(*s == '/')
|
|
|
|
s++;
|
|
|
|
for(; s; s = p){
|
|
|
|
if(p = strchr(s, '/'))
|
|
|
|
while(*p == '/')
|
|
|
|
*p++ = 0;
|
|
|
|
if(*s == '.' && ((s[1] == 0) || (s[1] == '.' && s[2] == 0))){
|
|
|
|
if(s[1] == '.')
|
|
|
|
while(d > b)
|
|
|
|
if(*--d == '/')
|
|
|
|
break;
|
|
|
|
dir = 1;
|
|
|
|
continue;
|
|
|
|
} else
|
|
|
|
dir = (p != nil);
|
|
|
|
n = strlen(s);
|
|
|
|
memmove(d+1, s, n);
|
|
|
|
*d = '/';
|
|
|
|
d += n+1;
|
|
|
|
}
|
|
|
|
if(dir)
|
|
|
|
*d++ = '/';
|
|
|
|
*d = 0;
|
|
|
|
return b;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
static char*
|
|
|
|
abspath(char *s, char *b)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
char *x, *a;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
if(b && *b){
|
|
|
|
if(s == nil || *s == 0)
|
|
|
|
return estrdup(b);
|
|
|
|
if(*s != '/' && (x = strrchr(b, '/'))){
|
|
|
|
a = emalloc((x - b) + strlen(s) + 4);
|
|
|
|
sprint(a, "/%.*s/%s", (int)(x - b), b, s);
|
|
|
|
return remdot(a);
|
|
|
|
}
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
if(s && *s){
|
|
|
|
if(*s != '/')
|
|
|
|
return estrdup(s);
|
|
|
|
a = emalloc(strlen(s) + 4);
|
|
|
|
sprint(a, "/%s", s);
|
|
|
|
return remdot(a);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
return nil;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
static void
|
|
|
|
pstrdup(char **p)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
if(p == nil || *p == nil)
|
|
|
|
return;
|
|
|
|
if(**p == 0){
|
|
|
|
*p = nil;
|
|
|
|
return;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
*p = estrdup(*p);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
static char*
|
|
|
|
mklowcase(char *s)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
char *p;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
if(s == nil)
|
|
|
|
return s;
|
|
|
|
for(p = s; *p; p++)
|
|
|
|
*p = tolower(*p);
|
|
|
|
return s;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
Url*
|
|
|
|
url(char *s, Url *b)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
char *t, *p, *x, *y;
|
|
|
|
Url *u;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
if(s == nil)
|
|
|
|
s = "";
|
|
|
|
t = nil;
|
|
|
|
s = p = estrdup(s);
|
|
|
|
u = emalloc(sizeof(*u));
|
|
|
|
for(; *p; p++){
|
|
|
|
if(*p == ':'){
|
|
|
|
if(p == s)
|
|
|
|
break;
|
|
|
|
*p++ = 0;
|
|
|
|
u->scheme = s;
|
|
|
|
b = nil;
|
|
|
|
goto Abs;
|
|
|
|
}
|
|
|
|
if(!isalpha(*p))
|
|
|
|
if((p == s) || ((!isdigit(*p) && strchr("+-.", *p) == nil)))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p = s;
|
|
|
|
if(b){
|
|
|
|
switch(*p){
|
|
|
|
case 0:
|
|
|
|
memmove(u, b, sizeof(*u));
|
|
|
|
goto Out;
|
|
|
|
case '#':
|
|
|
|
memmove(u, b, sizeof(*u));
|
|
|
|
u->fragment = p+1;
|
|
|
|
goto Out;
|
|
|
|
case '?':
|
|
|
|
memmove(u, b, sizeof(*u));
|
|
|
|
u->fragment = u->query = nil;
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
if(p[1] == '/'){
|
|
|
|
u->scheme = b->scheme;
|
|
|
|
b = nil;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
memmove(u, b, sizeof(*u));
|
|
|
|
u->fragment = u->query = u->path = nil;
|
|
|
|
break;
|
|
|
|
}
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
Abs:
|
|
|
|
if(x = strchr(p, '#')){
|
|
|
|
*x = 0;
|
|
|
|
u->fragment = x+1;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
if(x = strchr(p, '?')){
|
|
|
|
*x = 0;
|
|
|
|
u->query = x+1;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
if(p[0] == '/' && p[1] == '/'){
|
|
|
|
p += 2;
|
|
|
|
if(x = strchr(p, '/')){
|
|
|
|
u->path = t = abspath(x, Upath(b));
|
|
|
|
*x = 0;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
if(x = strchr(p, '@')){
|
|
|
|
*x = 0;
|
|
|
|
if(y = strchr(p, ':')){
|
|
|
|
*y = 0;
|
|
|
|
u->pass = y+1;
|
|
|
|
}
|
|
|
|
u->user = p;
|
|
|
|
p = x+1;
|
|
|
|
}
|
|
|
|
if((x = strrchr(p, ']')) == nil)
|
|
|
|
x = p;
|
|
|
|
if(x = strrchr(x, ':')){
|
|
|
|
*x = 0;
|
|
|
|
u->port = x+1;
|
|
|
|
}
|
|
|
|
if(x = strchr(p, '[')){
|
|
|
|
p = x+1;
|
|
|
|
if(y = strchr(p, ']'))
|
|
|
|
*y = 0;
|
|
|
|
}
|
|
|
|
u->host = p;
|
|
|
|
} else {
|
|
|
|
u->path = t = abspath(p, Upath(b));
|
|
|
|
}
|
|
|
|
Out:
|
|
|
|
pstrdup(&u->scheme);
|
|
|
|
pstrdup(&u->user);
|
|
|
|
pstrdup(&u->pass);
|
|
|
|
pstrdup(&u->host);
|
|
|
|
pstrdup(&u->port);
|
|
|
|
pstrdup(&u->path);
|
|
|
|
pstrdup(&u->query);
|
|
|
|
pstrdup(&u->fragment);
|
|
|
|
free(s);
|
|
|
|
free(t);
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
unescape(u->user, "");
|
|
|
|
unescape(u->pass, "");
|
|
|
|
unescape(u->path, "/");
|
|
|
|
unescape(u->query, "&;=/?#");
|
|
|
|
unescape(u->fragment, "");
|
|
|
|
mklowcase(u->scheme);
|
|
|
|
mklowcase(u->host);
|
|
|
|
mklowcase(u->port);
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
return u;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Url*
|
2012-01-11 15:17:54 +00:00
|
|
|
saneurl(Url *u)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-01-11 15:17:54 +00:00
|
|
|
if(u == nil || u->scheme == nil || u->host == nil || Upath(u) == nil){
|
2011-03-30 12:46:40 +00:00
|
|
|
freeurl(u);
|
|
|
|
return nil;
|
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
if(u->port){
|
|
|
|
/* remove default ports */
|
|
|
|
switch(atoi(u->port)){
|
|
|
|
case 21: if(!strcmp(u->scheme, "ftp")) goto Defport; break;
|
|
|
|
case 70: if(!strcmp(u->scheme, "gopher"))goto Defport; break;
|
|
|
|
case 80: if(!strcmp(u->scheme, "http")) goto Defport; break;
|
|
|
|
case 443: if(!strcmp(u->scheme, "https")) goto Defport; break;
|
|
|
|
default: if(!strcmp(u->scheme, u->port)) goto Defport; break;
|
|
|
|
Defport:
|
|
|
|
free(u->port);
|
|
|
|
u->port = nil;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
return u;
|
|
|
|
}
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
int
|
|
|
|
matchurl(Url *u, Url *s)
|
|
|
|
{
|
|
|
|
if(u){
|
|
|
|
char *a, *b;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-01-11 15:17:54 +00:00
|
|
|
if(s == nil)
|
|
|
|
return 0;
|
|
|
|
if(u->scheme && (s->scheme == nil || strcmp(u->scheme, s->scheme)))
|
|
|
|
return 0;
|
|
|
|
if(u->user && (s->user == nil || strcmp(u->user, s->user)))
|
|
|
|
return 0;
|
|
|
|
if(u->host && (s->host == nil || strcmp(u->host, s->host)))
|
|
|
|
return 0;
|
|
|
|
if(u->port && (s->port == nil || strcmp(u->port, s->port)))
|
|
|
|
return 0;
|
|
|
|
if(a = Upath(u)){
|
|
|
|
b = Upath(s);
|
|
|
|
if(b == nil || strncmp(a, b, strlen(a)))
|
|
|
|
return 0;
|
|
|
|
}
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-01-11 15:17:54 +00:00
|
|
|
return 1;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
freeurl(Url *u)
|
|
|
|
{
|
|
|
|
if(u == nil)
|
|
|
|
return;
|
|
|
|
free(u->scheme);
|
|
|
|
free(u->user);
|
2012-01-11 15:17:54 +00:00
|
|
|
free(u->pass);
|
2011-03-30 12:46:40 +00:00
|
|
|
free(u->host);
|
|
|
|
free(u->port);
|
|
|
|
free(u->path);
|
|
|
|
free(u->query);
|
|
|
|
free(u->fragment);
|
|
|
|
free(u);
|
|
|
|
}
|