#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ssl.h"
#include "ftp_var.h"
#include "uri.h"
typedef struct {
const char *buf;
size_t scheme;
size_t authority;
size_t path;
size_t query;
size_t fragment;
} uriparts_t;
#define SCHEME(p) ((p)->buf)
#define AUTHORITY(p) (SCHEME(p) + (p)->scheme)
#define PATH(p) (AUTHORITY(p) + (p)->authority)
#define QUERY(p) (PATH(p) + (p)->path)
#define FRAGMENT(p) (QUERY(p) + (p)->query)
static void
split_uri(const char *uri, uriparts_t *parts)
{
char *colon, *slash, *query, *sharp;
parts->buf = uri;
parts->scheme = 0;
parts->authority = 0;
parts->path = 0;
parts->query = 0;
parts->fragment = 0;
colon = strchr(uri, ':');
slash = strchr(uri, '/');
if (colon && (slash == NULL || (colon - uri) < (slash - uri))) {
parts->scheme = colon + 1 - uri;
uri = colon + 1;
}
if (uri[0] == '/' && uri[1] == '/') {
slash = strchr(uri + 2, '/');
if (slash == NULL) {
parts->authority = strlen(uri);
return;
}
parts->authority = slash - uri;
uri = slash;
}
query = strchr(uri, '?');
sharp = strchr(uri, '#');
if (query && (sharp == NULL || (query - uri) < (sharp - uri))) {
parts->path = query - uri;
uri = query;
if (sharp != NULL) {
parts->query = sharp - query;
parts->fragment = strlen(sharp);
return;
}
parts->query = strlen(query);
return;
}
if (sharp != NULL) {
parts->path = sharp - uri;
parts->fragment = strlen(sharp);
return;
}
parts->path = strlen(uri);
}
static char *
build_uri(uriparts_t *s, uriparts_t *a, uriparts_t *p,
uriparts_t *q, uriparts_t *f)
{
size_t len;
char *buf, *out;
len = s->scheme + a->authority + p->path + q->query + f->fragment;
buf = ftp_malloc(len + 1);
out = buf;
memcpy(out, SCHEME(s), s->scheme);
out += s->scheme;
memcpy(out, AUTHORITY(a), a->authority);
out += a->authority;
memcpy(out, PATH(p), p->path);
out += p->path;
memcpy(out, QUERY(q), q->query);
out += q->query;
memcpy(out, FRAGMENT(f), f->fragment);
out += f->fragment;
*out = '\0';
return buf;
}
static char *
remove_dot_segments(const char *path, size_t len)
{
char *p;
char *pbuf, *buf, *out, *seg;
size_t n;
p = pbuf = ftp_malloc(len + 1);
memcpy(p, path, len);
p[len] = '\0';
buf = ftp_malloc(len + 1);
out = buf;
*out = '\0';
while (len > 0) {
if (p[0] == '.') {
if (len > 1 && p[1] == '/') {
p += 2;
len -= 2;
continue;
} else if (len > 2 && p[1] == '.' && p[2] == '/') {
p += 3;
len -= 3;
continue;
}
}
if (p[0] == '/') {
if (len > 2 && p[1] == '.' && p[2] == '/') {
p += 2;
len -= 2;
continue;
} else if (len == 2 && p[1] == '.') {
p += 1;
len -= 1;
p[0] = '/';
continue;
}
}
if (len > 2 && p[0] == '/' && p[1] == '.' && p[2] == '.') {
if (len > 3 && p[3] == '/') {
p += 3;
len -= 3;
seg = strrchr(buf, '/');
if (seg != NULL)
*seg = '\0';
continue;
} else if (len == 3) {
p += 2;
len -= 2;
p[0] = '/';
seg = strrchr(buf, '/');
if (seg != NULL)
*seg = '\0';
continue;
}
}
if (p[0] == '.') {
if (len == 1) {
p += 1;
len -= 1;
continue;
} else if (len == 2 && p[1] == '.') {
p += 2;
len -= 2;
continue;
}
}
if (p[0] == '/') {
seg = strchr(p+1, '/');
} else {
seg = strchr(p, '/');
}
if (seg != NULL) {
n = seg - p;
if (n > len)
n = len;
} else
n = len;
memcpy(out, p, n);
out += n;
*out = '\0';
p += n;
len -= n;
};
FREEPTR(pbuf);
return buf;
}
static char *
merge_paths(uriparts_t *base, uriparts_t *ref)
{
char *buf;
const char *path, *last;
size_t len;
if (base->authority && base->path == 0) {
len = 1 + ref->path;
buf = ftp_malloc(len + 1);
buf[0] = '/';
memcpy(buf + 1, PATH(ref), ref->path);
} else {
path = PATH(base);
last = strrchr(path, '/');
if (last == NULL || (size_t)(last - path) > base->path)
len = 0;
else
len = (size_t)(last - path) + 1;
buf = ftp_malloc(len + ref->path + 1);
memcpy(buf, path, len);
memcpy(buf + len, PATH(ref), ref->path);
len += ref->path;
}
buf[len] = '\0';
return buf;
}
char *
make_absurl(char *refurl, const char *baseurl)
{
uriparts_t base;
uriparts_t ref;
uriparts_t p;
char *buf, *tmp1, *tmp2;
split_uri(baseurl, &base);
split_uri(refurl, &ref);
p.scheme = p.authority = p.path = p.query = p.fragment = 0;
if (ref.scheme) {
tmp1 = remove_dot_segments(PATH(&ref), ref.path);
p.buf = tmp1;
p.path = strlen(tmp1);
buf = build_uri(&ref, &ref, &p, &ref, &ref);
FREEPTR(tmp1);
} else {
if (ref.authority) {
tmp1 = remove_dot_segments(PATH(&ref), ref.path);
p.buf = tmp1;
p.path = strlen(tmp1);
buf = build_uri(&base, &ref, &p, &ref, &ref);
FREEPTR(tmp1);
} else {
if (ref.path == 0) {
if (ref.query) {
buf = build_uri(&base, &base, &base, &ref, &ref);
} else {
buf = build_uri(&base, &base, &base, &base, &ref);
}
} else {
if (PATH(&ref)[0] == '/') {
tmp1 = remove_dot_segments(PATH(&ref), ref.path);
p.buf = tmp1;
p.path = strlen(tmp1);
buf = build_uri(&base, &base, &p, &ref, &ref);
FREEPTR(tmp1);
} else {
tmp2 = merge_paths(&base, &ref);
tmp1 = remove_dot_segments(tmp2, strlen(tmp2));
p.buf = tmp1;
p.path = strlen(tmp1);
FREEPTR(tmp2);
buf = build_uri(&base, &base, &p, &ref, &ref);
FREEPTR(tmp1);
}
}
}
}
FREEPTR(refurl);
return buf;
}