/* $NetBSD: uri.c,v 1.4 2026/07/09 16:54:11 mlelstv Exp $ */ /*- * Copyright (c) 2026 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #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) { /* A */ 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; } } /* B */ 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; } } /* C */ 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; } } /* D */ if (p[0] == '.') { if (len == 1) { p += 1; len -= 1; continue; } else if (len == 2 && p[1] == '.') { p += 2; len -= 2; continue; } } /* E */ 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; }