Merge pull request #20172 from poettering/import-util-tweaks

Import-util: URL processing tweaks
This commit is contained in:
Lennart Poettering
2021-07-30 19:05:06 +02:00
committed by GitHub
4 changed files with 169 additions and 25 deletions
+85 -24
View File
@@ -14,53 +14,114 @@
#include "string-table.h"
#include "string-util.h"
int import_url_last_component(const char *url, char **ret) {
const char *e, *p;
char *s;
static const char *skip_protocol_and_hostname(const char *url) {
const char *d;
size_t n;
e = strchrnul(url, '?');
/* A very very lenient implementation of RFC3986 Section 3.2 */
while (e > url && e[-1] == '/')
/* Find colon separating protocol and hostname */
d = strchr(url, ':');
if (!d || url == d)
return NULL;
d++;
/* Skip slashes after colon */
d += strspn(d, "/");
/* Skip everything till next slash or end */
n = strcspn(d, "/?#");
if (n == 0)
return NULL;
return d + n;
}
int import_url_last_component(
const char *url,
char **ret) {
const char *e, *p, *h;
/* This extracts the last path component of the specified URI, i.e. the last non-empty substrings
* between two "/" characters. This ignores "Query" and "Fragment" suffixes (as per RFC3986). */
h = skip_protocol_and_hostname(url);
if (!h)
return -EINVAL;
e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
while (e > h && e[-1] == '/') /* Eat trailing slashes */
e--;
p = e;
while (p > url && p[-1] != '/')
while (p > h && p[-1] != '/') /* Find component before that */
p--;
if (e <= p)
return -EINVAL;
if (e <= p) /* Empty component? */
return -EADDRNOTAVAIL;
s = strndup(p, e - p);
if (!s)
return -ENOMEM;
if (ret) {
char *s;
s = strndup(p, e - p);
if (!s)
return -ENOMEM;
*ret = s;
}
*ret = s;
return 0;
}
int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
const char *e;
int import_url_change_suffix(
const char *url,
size_t n_drop_components,
const char *suffix,
char **ret) {
const char *e, *h;
char *s;
assert(url);
assert(ret);
e = strchrnul(url, '?');
/* This drops the specified number of path components of the specified URI, i.e. the specified number
* of non-empty substring between two "/" characters from the end of the string, and then append the
* specified suffix instead. Before doing all this it chops off the "Query" and "Fragment" suffixes
* (they are *not* readded to the final URL). Note that n_drop_components may be 0 (in which case the
* component are simply added to the end). The suffix may be specified as NULL or empty string in
* which case nothing is appended, only the specified number of components chopped off. Note that the
* function may be called with n_drop_components == 0 and suffix == NULL, in which case the "Query"
* and "Fragment" is chopped off, and ensured the URL ends in a single "/", and that's it. */
while (e > url && e[-1] == '/')
e--;
while (e > url && e[-1] != '/')
e--;
if (e <= url)
h = skip_protocol_and_hostname(url);
if (!h)
return -EINVAL;
s = new(char, (e - url) + strlen(suffix) + 1);
e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
while (e > h && e[-1] == '/') /* Eat trailing slashes */
e--;
/* Drop the specified number of components from the end. Note that this is pretty lenient: if there
* are less component we silently drop those and then append the suffix to the top. */
while (n_drop_components > 0) {
while (e > h && e[-1] != '/') /* Eat last word (we don't mind if empty) */
e--;
while (e > h && e[-1] == '/') /* Eat slashes before the last word */
e--;
n_drop_components--;
}
s = new(char, (e - url) + 1 + strlen_ptr(suffix) + 1);
if (!s)
return -ENOMEM;
strcpy(mempcpy(s, url, e - url), suffix);
strcpy(stpcpy(mempcpy(s, url, e - url), "/"), strempty(suffix));
*ret = s;
return 0;
}
+10 -1
View File
@@ -14,7 +14,16 @@ typedef enum ImportVerify {
} ImportVerify;
int import_url_last_component(const char *url, char **ret);
int import_url_change_last_component(const char *url, const char *suffix, char **ret);
int import_url_change_suffix(const char *url, size_t n_drop_components, const char *suffix, char **ret);
static inline int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
return import_url_change_suffix(url, 1, suffix, ret);
}
static inline int import_url_append_component(const char *url, const char *suffix, char **ret) {
return import_url_change_suffix(url, 0, suffix, ret);
}
const char* import_verify_to_string(ImportVerify v) _const_;
ImportVerify import_verify_from_string(const char *s) _pure_;
+2
View File
@@ -250,6 +250,8 @@ tests += [
[['src/test/test-sysctl-util.c']],
[['src/test/test-import-util.c']],
[['src/test/test-user-record.c']],
[['src/test/test-user-util.c']],
+72
View File
@@ -0,0 +1,72 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
#include "import-util.h"
#include "log.h"
#include "string-util.h"
#include "tests.h"
static void test_import_url_last_component_one(const char *input, const char *output, int ret) {
_cleanup_free_ char *s = NULL;
assert_se(import_url_last_component(input, &s) == ret);
assert_se(streq_ptr(output, s));
}
static void test_import_url_last_component(void) {
test_import_url_last_component_one("https://foobar/waldo/quux", "quux", 0);
test_import_url_last_component_one("https://foobar/waldo/quux/", "quux", 0);
test_import_url_last_component_one("https://foobar/waldo/", "waldo", 0);
test_import_url_last_component_one("https://foobar/", NULL, -EADDRNOTAVAIL);
test_import_url_last_component_one("https://foobar", NULL, -EADDRNOTAVAIL);
test_import_url_last_component_one("https://foobar/waldo/quux?foo=bar", "quux", 0);
test_import_url_last_component_one("https://foobar/waldo/quux/?foo=bar", "quux", 0);
test_import_url_last_component_one("https://foobar/waldo/quux/?foo=bar#piep", "quux", 0);
test_import_url_last_component_one("https://foobar/waldo/quux/#piep", "quux", 0);
test_import_url_last_component_one("https://foobar/waldo/quux#piep", "quux", 0);
test_import_url_last_component_one("https://", NULL, -EINVAL);
test_import_url_last_component_one("", NULL, -EINVAL);
test_import_url_last_component_one(":", NULL, -EINVAL);
test_import_url_last_component_one(":/", NULL, -EINVAL);
test_import_url_last_component_one("x:/", NULL, -EINVAL);
test_import_url_last_component_one("x:y", NULL, -EADDRNOTAVAIL);
test_import_url_last_component_one("x:y/z", "z", 0);
}
static void test_import_url_change_suffix_one(const char *input, size_t n, const char *suffix, const char *output, int ret) {
_cleanup_free_ char *s = NULL;
assert_se(import_url_change_suffix(input, n, suffix, &s) == ret);
assert_se(streq_ptr(output, s));
}
static void test_import_url_change_suffix(void) {
test_import_url_change_suffix_one("https://foobar/waldo/quux", 1, "wuff", "https://foobar/waldo/wuff", 0);
test_import_url_change_suffix_one("https://foobar/waldo/quux/", 1, "wuff", "https://foobar/waldo/wuff", 0);
test_import_url_change_suffix_one("https://foobar/waldo/quux///?mief", 1, "wuff", "https://foobar/waldo/wuff", 0);
test_import_url_change_suffix_one("https://foobar/waldo/quux///?mief#opopo", 1, "wuff", "https://foobar/waldo/wuff", 0);
test_import_url_change_suffix_one("https://foobar/waldo/quux/quff", 2, "wuff", "https://foobar/waldo/wuff", 0);
test_import_url_change_suffix_one("https://foobar/waldo/quux/quff/", 2, "wuff", "https://foobar/waldo/wuff", 0);
test_import_url_change_suffix_one("https://foobar/waldo/quux/quff", 0, "wuff", "https://foobar/waldo/quux/quff/wuff", 0);
test_import_url_change_suffix_one("https://foobar/waldo/quux/quff?aa?bb##4", 0, "wuff", "https://foobar/waldo/quux/quff/wuff", 0);
test_import_url_change_suffix_one("https://", 0, "wuff", NULL, -EINVAL);
test_import_url_change_suffix_one("", 0, "wuff", NULL, -EINVAL);
test_import_url_change_suffix_one(":", 0, "wuff", NULL, -EINVAL);
test_import_url_change_suffix_one(":/", 0, "wuff", NULL, -EINVAL);
test_import_url_change_suffix_one("x:/", 0, "wuff", NULL, -EINVAL);
test_import_url_change_suffix_one("x:y", 0, "wuff", "x:y/wuff", 0);
test_import_url_change_suffix_one("x:y/z", 0, "wuff", "x:y/z/wuff", 0);
test_import_url_change_suffix_one("x:y/z/", 0, "wuff", "x:y/z/wuff", 0);
test_import_url_change_suffix_one("x:y/z/", 1, "wuff", "x:y/wuff", 0);
test_import_url_change_suffix_one("x:y/z/", 2, "wuff", "x:y/wuff", 0);
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
test_import_url_last_component();
test_import_url_change_suffix();
return 0;
}