cosmopolitan/net/http/encodeurl.c
2024-08-04 12:52:25 -07:00

150 lines
4.7 KiB
C

/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2021 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/str/tab.h"
#include "net/http/escape.h"
#include "net/http/url.h"
static size_t DimensionUrl(struct Url *h) {
size_t i, n;
n = 0;
n += h->scheme.n;
n += 1;
n += 2;
n += h->user.n * 3;
n += 1;
n += h->pass.n * 3;
n += 1;
n += 1;
n += h->host.n * 3;
n += 1;
n += 1;
n += h->port.n * 3;
n += 1;
n += h->path.n * 3;
n += 1;
n += h->params.n;
for (i = 0; i < h->params.n; ++i) {
n += h->params.p[i].key.n * 3;
n += 1;
n += h->params.p[i].val.n * 3;
}
n += 1;
n += h->fragment.n * 3;
n += 1;
return n;
}
static bool NeedsSquareBrackets(struct Url *h) {
int c;
size_t i;
if (!memchr(h->host.p, ':', h->host.n))
return false;
if (h->pass.p)
return true;
if (h->host.n >= 4 && h->host.p[0] == 'v' && h->host.p[2] == '.' &&
kHexToInt[h->host.p[1] & 0xFF] != -1) {
for (i = 3; i < h->host.n; ++i) {
if (kEscapeIp[h->host.p[i] & 0xFF]) {
return false;
}
}
} else {
for (i = 0; i < h->host.n; ++i) {
c = h->host.p[i] & 0xFF;
if (!(kHexToInt[c] || c == '.' || c == ':')) {
return false;
}
}
}
return true;
}
/**
* Encodes URL.
*
* @param z if not null receives string length of result
* @return nul-terminated url string needing free
* @see ParseUrl()
*/
char *EncodeUrl(struct Url *h, size_t *z) {
size_t i, n;
char *m, *p;
if ((p = m = malloc(DimensionUrl(h)))) {
if (h->scheme.n) {
p = mempcpy(p, h->scheme.p, h->scheme.n);
*p++ = ':';
}
if (h->host.p) {
*p++ = '/';
*p++ = '/';
if (h->user.p) {
p = EscapeUrlView(p, &h->user, kEscapeAuthority);
if (h->pass.p) {
*p++ = ':';
p = EscapeUrlView(p, &h->pass, kEscapeAuthority);
}
*p++ = '@';
}
if (h->host.p) {
if (NeedsSquareBrackets(h)) {
*p++ = '[';
p = EscapeUrlView(p, &h->host, kEscapeIp);
*p++ = ']';
} else {
p = EscapeUrlView(p, &h->host, kEscapeAuthority);
}
if (h->port.p) {
*p++ = ':';
p = EscapeUrlView(p, &h->port, kEscapeAuthority);
}
}
if (h->path.n && h->path.p[0] != '/') {
*p++ = '/';
}
}
p = EscapeUrlView(p, &h->path, kEscapePath);
if (h->params.p) {
*p++ = '?';
for (i = 0; i < h->params.n; ++i) {
if (i)
*p++ = '&';
p = EscapeUrlView(p, &h->params.p[i].key, kEscapeParam);
if (h->params.p[i].val.p) {
*p++ = '=';
p = EscapeUrlView(p, &h->params.p[i].val, kEscapeParam);
}
}
}
if (h->fragment.p) {
*p++ = '#';
p = EscapeUrlView(p, &h->fragment, kEscapeFragment);
}
n = p - m;
*p++ = '\0';
if ((p = realloc(m, p - m)))
m = p;
} else {
n = 0;
}
if (z)
*z = n;
return m;
}