grub2/0143-Add-a-url-parser.patch
Javier Martinez Canillas afb0baacd6
Use BLS fragment filename as menu entry id and for sort criterion
The BLS config filenames are guaranteed to be unique, so they can be
used as GRUB2 entry id and can also be used to sort the menu entries.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
2018-07-02 17:33:09 +02:00

1025 lines
23 KiB
Diff

From 2f84b2e097ad9f6f7f06013f749251743eb32c2e Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Tue, 14 Jun 2016 16:18:44 -0400
Subject: [PATCH 143/250] Add a url parser.
This patch adds a url parser that can parse http, https, tftp, and tftps
urls, and is easily extensible to handle more types.
It's a little ugly in terms of the arguments it takes.
Signed-off-by: Peter Jones <pjones@redhat.com>
---
grub-core/Makefile.core.def | 1 +
grub-core/kern/misc.c | 13 +
grub-core/net/url.c | 861 ++++++++++++++++++++++++++++++++++++++++++++
include/grub/misc.h | 45 +++
include/grub/net/url.h | 28 ++
5 files changed, 948 insertions(+)
create mode 100644 grub-core/net/url.c
create mode 100644 include/grub/net/url.h
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 5ae70204cd2..4857a1e5f61 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2199,6 +2199,7 @@ module = {
common = net/ethernet.c;
common = net/arp.c;
common = net/netbuff.c;
+ common = net/url.c;
};
module = {
diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
index 8344526be7f..f1fab700048 100644
--- a/grub-core/kern/misc.c
+++ b/grub-core/kern/misc.c
@@ -296,6 +296,19 @@ grub_strrchr (const char *s, int c)
return p;
}
+char *
+grub_strchrnul (const char *s, int c)
+{
+ do
+ {
+ if (*s == c)
+ break;
+ }
+ while (*s++);
+
+ return (char *) s;
+}
+
int
grub_strword (const char *haystack, const char *needle)
{
diff --git a/grub-core/net/url.c b/grub-core/net/url.c
new file mode 100644
index 00000000000..146858284cd
--- /dev/null
+++ b/grub-core/net/url.c
@@ -0,0 +1,861 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2016 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef URL_TEST
+
+#define _GNU_SOURCE 1
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define N_(x) x
+
+#define grub_malloc(x) malloc(x)
+#define grub_free(x) ({if (x) free(x);})
+#define grub_error(a, fmt, args...) printf(fmt "\n", ## args)
+#define grub_dprintf(a, fmt, args...) printf(a ": " fmt, ## args)
+#define grub_strlen(x) strlen(x)
+#define grub_strdup(x) strdup(x)
+#define grub_strstr(x,y) strstr(x,y)
+#define grub_memcpy(x,y,z) memcpy(x,y,z)
+#define grub_strcmp(x,y) strcmp(x,y)
+#define grub_strncmp(x,y,z) strncmp(x,y,z)
+#define grub_strcasecmp(x,y) strcasecmp(x,y)
+#define grub_strchrnul(x,y) strchrnul(x,y)
+#define grub_strchr(x,y) strchr(x,y)
+#define grub_strndup(x,y) strndup(x,y)
+#define grub_strtoul(x,y,z) strtoul(x,y,z)
+#define grub_memmove(x,y,z) memmove(x,y,z)
+#define grub_size_t size_t
+#define grub_errno errno
+
+#else
+#include <grub/types.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/net/url.h>
+#endif
+
+static char *
+translate_slashes(char *str)
+{
+ int i, j;
+ if (str == NULL)
+ return str;
+
+ for (i = 0, j = 0; str[i] != '\0'; i++, j++)
+ {
+ if (str[i] == '\\')
+ {
+ str[j] = '/';
+ if (str[i+1] == '\\')
+ i++;
+ }
+ }
+
+ return str;
+}
+
+static inline int
+hex2int (char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ c |= 0x20;
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1;
+}
+
+static int
+url_unescape (char *buf, grub_size_t len)
+{
+ int c, rc;
+ unsigned int i;
+
+
+ if (len < 3)
+ {
+ for (i = 0; i < len; i++)
+ if (buf[i] == '%')
+ return -1;
+ return 0;
+ }
+
+ for (i = 0; len > 2 && i < len - 2; i++)
+ {
+ if (buf[i] == '%')
+ {
+ unsigned int j;
+ for (j = i+1; j < i+3; j++)
+ {
+ if (!(buf[j] >= '0' && buf[j] <= '9') &&
+ !(buf[j] >= 'a' && buf[j] <= 'f') &&
+ !(buf[j] >= 'A' && buf[j] <= 'F'))
+ return -1;
+ }
+ i += 2;
+ }
+ }
+ if (i == len - 2)
+ {
+ if (buf[i+1] == '%' || buf[i+2] == '%')
+ return -1;
+ }
+ for (i = 0; i < len - 2; i++)
+ {
+ if (buf[i] == '%')
+ {
+ rc = hex2int (buf[i+1]);
+ if (rc < 0)
+ return -1;
+ c = (rc & 0xf) << 4;
+ rc = hex2int (buf[i+2]);
+ if (rc < 0)
+ return -1;
+ c |= (rc & 0xf);
+
+ buf[i] = c;
+ grub_memmove (buf+i+1, buf+i+3, len-(i+2));
+ len -= 2;
+ }
+ }
+ return 0;
+}
+
+static int
+extract_http_url_info (char *url, int ssl,
+ char **userinfo, char **host, int *port,
+ char **file)
+{
+ char *colon, *slash, *query, *at = NULL, *separator, *auth_end;
+
+ char *userinfo_off = NULL;
+ char *userinfo_end;
+ char *host_off = NULL;
+ char *host_end;
+ char *port_off = NULL;
+ char *port_end;
+ char *file_off = NULL;
+
+ grub_size_t l;
+ int c;
+
+ if (!url || !userinfo || !host || !port || !file)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid argument");
+
+ *userinfo = *host = *file = NULL;
+ *port = -1;
+
+ userinfo_off = url;
+
+ slash = grub_strchrnul (userinfo_off, '/');
+ query = grub_strchrnul (userinfo_off, '?');
+ auth_end = slash < query ? slash : query;
+ /* auth_end here is one /past/ the last character in the auth section, i.e.
+ * it's the : or / or NUL */
+
+ separator = grub_strchrnul (userinfo_off, '@');
+ if (separator > auth_end)
+ {
+ host_off = userinfo_off;
+ userinfo_off = NULL;
+ userinfo_end = NULL;
+ }
+ else
+ {
+ at = separator;
+ *separator = '\0';
+ userinfo_end = separator;
+ host_off = separator + 1;
+ }
+
+ if (*host_off == '[')
+ {
+ separator = grub_strchrnul (host_off, ']');
+ if (separator >= auth_end)
+ goto fail;
+
+ separator += 1;
+ host_end = separator;
+ }
+ else
+ {
+ host_end = separator = colon = grub_strchrnul (host_off, ':');
+
+ if (colon > auth_end)
+ {
+ separator = NULL;
+ host_end = auth_end;
+ }
+ }
+
+ if (separator && separator < auth_end)
+ {
+ if (*separator == ':')
+ {
+ port_off = separator + 1;
+ port_end = auth_end;
+
+ if (auth_end - port_end > 0)
+ goto fail;
+ if (port_end - port_off < 1)
+ goto fail;
+ }
+ else
+ goto fail;
+ }
+
+ file_off = auth_end;
+ if (port_off)
+ {
+ unsigned long portul;
+
+ separator = NULL;
+ c = *port_end;
+ *port_end = '\0';
+
+ portul = grub_strtoul (port_off, &separator, 10);
+ *port_end = c;
+#ifdef URL_TEST
+ if (portul == ULONG_MAX && errno == ERANGE)
+ goto fail;
+#else
+ if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
+ goto fail;
+#endif
+ if (portul & ~0xfffful)
+ goto fail;
+ if (separator != port_end)
+ goto fail;
+
+ *port = portul & 0xfffful;
+ }
+ else if (ssl)
+ *port = 443;
+ else
+ *port = 80;
+
+ if (userinfo_off && *userinfo_off)
+ {
+ l = userinfo_end - userinfo_off + 1;
+
+ *userinfo = grub_strndup (userinfo_off, l);
+ if (!*userinfo)
+ goto fail;
+ (*userinfo)[l-1]= '\0';
+ }
+
+ l = host_end - host_off;
+
+ if (host_end == NULL)
+ goto fail;
+ else
+ c = *host_end;
+
+ *host_end = '\0';
+ *host = grub_strndup (host_off, l);
+ *host_end = c;
+ if (!*host)
+ goto fail;
+ (*host)[l] = '\0';
+
+ *file = grub_strdup (file_off);
+ if (!*file)
+ goto fail;
+
+ if (at)
+ *at = '@';
+ return 0;
+fail:
+ if (at)
+ *at = '@';
+ grub_free (*userinfo);
+ grub_free (*host);
+ grub_free (*file);
+
+ return -1;
+}
+
+static int
+extract_tftp_url_info (char *url, int ssl, char **host, char **file, int *port)
+{
+ char *slash, *semi;
+
+ char *host_off = url;
+ char *host_end;
+ char *file_off;
+
+ int c;
+
+ if (!url || !host || !file || !port)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid argument");
+
+ if (ssl)
+ *port = 3713;
+ else
+ *port = 69;
+
+ slash = grub_strchr (url, '/');
+ if (!slash)
+ return -1;
+
+ host_end = file_off = slash;
+
+ semi = grub_strchrnul (slash, ';');
+ if (!grub_strncmp (semi, ";mode=", 6) && grub_strcmp (semi+6, "octet"))
+ {
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("TFTP mode `%s' is not implemented."), semi+6);
+ return -1;
+ }
+
+ /*
+ * Maybe somebody added a new method, I dunno. Anyway, semi is a reserved
+ * character, so if it's there, it's the start of the mode block or it's
+ * invalid. So set it to \0 unconditionally, not just for ;mode=octet
+ */
+ *semi = '\0';
+
+ c = *host_end;
+ *host_end = '\0';
+ *host = grub_strdup (host_off);
+ *host_end = c;
+
+ *file = grub_strdup (file_off);
+
+ if (!*file || !*host)
+ {
+ grub_free (*file);
+ grub_free (*host);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+extract_url_info (const char *urlbuf, grub_size_t buflen,
+ char **scheme, char **userinfo,
+ char **host, int *port, char **file)
+{
+ char *url;
+ char *colon;
+
+ char *scheme_off;
+ char *specific_off;
+
+ int rc;
+ int c;
+
+ int https;
+
+ if (!urlbuf || !buflen || !scheme || !userinfo || !host || !port || !file)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid argument");
+
+ *scheme = *userinfo = *host = *file = NULL;
+ *port = -1;
+
+ /* make sure we have our own coherent grub_string. */
+ url = grub_malloc (buflen + 1);
+ if (!url)
+ return -1;
+
+ grub_memcpy (url, urlbuf, buflen);
+ url[buflen] = '\0';
+
+ grub_dprintf ("net", "dhcpv6 boot-file-url: `%s'\n", url);
+
+ /* get rid of any backslashes */
+ url = translate_slashes (url);
+
+ /* find the constituent parts */
+ colon = grub_strstr (url, "://");
+ if (!colon)
+ goto fail;
+
+ scheme_off = url;
+ c = *colon;
+ *colon = '\0';
+ specific_off = colon + 3;
+
+ https = !grub_strcasecmp (scheme_off, "https");
+
+ rc = 0;
+ if (!grub_strcasecmp (scheme_off, "tftp"))
+ {
+ rc = extract_tftp_url_info (specific_off, 0, host, file, port);
+ }
+#ifdef URL_TEST
+ else if (!grub_strcasecmp (scheme_off, "http") || https)
+#else
+ else if (!grub_strcasecmp (scheme_off, "http"))
+#endif
+ {
+ rc = extract_http_url_info (specific_off,
+ https, userinfo, host, port, file);
+ }
+#ifdef URL_TEST
+ else if (!grub_strcasecmp (scheme_off, "iscsi"))
+ {
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("Unimplemented URL scheme `%s'"), scheme_off);
+ *colon = c;
+ goto fail;
+ }
+ else if (!grub_strcasecmp (scheme_off, "tftps"))
+ {
+ rc = extract_tftp_url_info (specific_off, 1, host, file, port);
+ }
+#endif
+ else
+ {
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("Unimplemented URL scheme `%s'"), scheme_off);
+ *colon = c;
+ goto fail;
+ }
+
+ if (rc < 0)
+ {
+ *colon = c;
+ goto fail;
+ }
+
+ *scheme = grub_strdup (scheme_off);
+ *colon = c;
+ if (!*scheme)
+ goto fail;
+
+ if (*userinfo)
+ {
+ rc = url_unescape (*userinfo, grub_strlen (*userinfo));
+ if (rc < 0)
+ goto fail;
+ }
+
+ if (*host)
+ {
+ rc = url_unescape (*host, grub_strlen (*host));
+ if (rc < 0)
+ goto fail;
+ }
+
+ if (*file)
+ {
+ rc = url_unescape (*file, grub_strlen (*file));
+ if (rc < 0)
+ goto fail;
+ }
+
+ grub_free (url);
+ return 0;
+fail:
+ grub_free (*scheme);
+ grub_free (*userinfo);
+ grub_free (*host);
+ grub_free (*file);
+
+ if (!grub_errno)
+ grub_error (GRUB_ERR_NET_BAD_ADDRESS, N_("Invalid boot-file-url `%s'"),
+ url);
+ grub_free (url);
+ return -1;
+}
+
+#ifdef URL_TEST
+
+struct test {
+ char *url;
+ int rc;
+ char *scheme;
+ char *userinfo;
+ char *host;
+ int port;
+ char *file;
+} tests[] = {
+ {.url = "http://foo.example.com/",
+ .rc = 0,
+ .scheme = "http",
+ .host = "foo.example.com",
+ .port = 80,
+ .file = "/",
+ },
+ {.url = "http://foo.example.com/?foobar",
+ .rc = 0,
+ .scheme = "http",
+ .host = "foo.example.com",
+ .port = 80,
+ .file = "/?foobar",
+ },
+ {.url = "http://[foo.example.com/",
+ .rc = -1,
+ },
+ {.url = "http://[foo.example.com/?foobar",
+ .rc = -1,
+ },
+ {.url = "http://foo.example.com:/",
+ .rc = -1,
+ },
+ {.url = "http://foo.example.com:81/",
+ .rc = 0,
+ .scheme = "http",
+ .host = "foo.example.com",
+ .port = 81,
+ .file = "/",
+ },
+ {.url = "http://foo.example.com:81/?foobar",
+ .rc = 0,
+ .scheme = "http",
+ .host = "foo.example.com",
+ .port = 81,
+ .file = "/?foobar",
+ },
+ {.url = "http://[1234::1]/",
+ .rc = 0,
+ .scheme = "http",
+ .host = "[1234::1]",
+ .port = 80,
+ .file = "/",
+ },
+ {.url = "http://[1234::1]/?foobar",
+ .rc = 0,
+ .scheme = "http",
+ .host = "[1234::1]",
+ .port = 80,
+ .file = "/?foobar",
+ },
+ {.url = "http://[1234::1]:81/",
+ .rc = 0,
+ .scheme = "http",
+ .host = "[1234::1]",
+ .port = 81,
+ .file = "/",
+ },
+ {.url = "http://[1234::1]:81/?foobar",
+ .rc = 0,
+ .scheme = "http",
+ .host = "[1234::1]",
+ .port = 81,
+ .file = "/?foobar",
+ },
+ {.url = "http://foo@foo.example.com/",
+ .rc = 0,
+ .scheme = "http",
+ .userinfo = "foo",
+ .host = "foo.example.com",
+ .port = 80,
+ .file = "/",
+ },
+ {.url = "http://foo@foo.example.com/?foobar",
+ .rc = 0,
+ .scheme = "http",
+ .userinfo = "foo",
+ .host = "foo.example.com",
+ .port = 80,
+ .file = "/?foobar",
+ },
+ {.url = "http://foo@[foo.example.com/",
+ .rc = -1,
+ },
+ {.url = "http://foo@[foo.example.com/?foobar",
+ .rc = -1,
+ },
+ {.url = "http://foo@foo.example.com:81/",
+ .rc = 0,
+ .scheme = "http",
+ .userinfo = "foo",
+ .host = "foo.example.com",
+ .port = 81,
+ .file = "/",
+ },
+ {.url = "http://foo@foo.example.com:81/?foobar",
+ .rc = 0,
+ .scheme = "http",
+ .userinfo = "foo",
+ .host = "foo.example.com",
+ .port = 81,
+ .file = "/?foobar",
+ },
+ {.url = "http://foo@[1234::1]/",
+ .rc = 0,
+ .scheme = "http",
+ .userinfo = "foo",
+ .host = "[1234::1]",
+ .port = 80,
+ .file = "/",
+ },
+ {.url = "http://foo@[1234::1]/?foobar",
+ .rc = 0,
+ .scheme = "http",
+ .userinfo = "foo",
+ .host = "[1234::1]",
+ .port = 80,
+ .file = "/?foobar",
+ },
+ {.url = "http://foo@[1234::1]:81/",
+ .rc = 0,
+ .scheme = "http",
+ .userinfo = "foo",
+ .host = "[1234::1]",
+ .port = 81,
+ .file = "/",
+ },
+ {.url = "http://foo@[1234::1]:81/?foobar",
+ .rc = 0,
+ .scheme = "http",
+ .userinfo = "foo",
+ .host = "[1234::1]",
+ .port = 81,
+ .file = "/?foobar",
+ },
+ {.url = "https://foo.example.com/",
+ .rc = 0,
+ .scheme = "https",
+ .host = "foo.example.com",
+ .port = 443,
+ .file = "/",
+ },
+ {.url = "https://foo.example.com/?foobar",
+ .rc = 0,
+ .scheme = "https",
+ .host = "foo.example.com",
+ .port = 443,
+ .file = "/?foobar",
+ },
+ {.url = "https://[foo.example.com/",
+ .rc = -1,
+ },
+ {.url = "https://[foo.example.com/?foobar",
+ .rc = -1,
+ },
+ {.url = "https://foo.example.com:81/",
+ .rc = 0,
+ .scheme = "https",
+ .host = "foo.example.com",
+ .port = 81,
+ .file = "/",
+ },
+ {.url = "https://foo.example.com:81/?foobar",
+ .rc = 0,
+ .scheme = "https",
+ .host = "foo.example.com",
+ .port = 81,
+ .file = "/?foobar",
+ },
+ {.url = "https://[1234::1]/",
+ .rc = 0,
+ .scheme = "https",
+ .host = "[1234::1]",
+ .port = 443,
+ .file = "/",
+ },
+ {.url = "https://[1234::1]/?foobar",
+ .rc = 0,
+ .scheme = "https",
+ .host = "[1234::1]",
+ .port = 443,
+ .file = "/?foobar",
+ },
+ {.url = "https://[1234::1]:81/",
+ .rc = 0,
+ .scheme = "https",
+ .host = "[1234::1]",
+ .port = 81,
+ .file = "/",
+ },
+ {.url = "https://[1234::1]:81/?foobar",
+ .rc = 0,
+ .scheme = "https",
+ .host = "[1234::1]",
+ .port = 81,
+ .file = "/?foobar",
+ },
+ {.url = "https://foo@foo.example.com/",
+ .rc = 0,
+ .scheme = "https",
+ .userinfo = "foo",
+ .host = "foo.example.com",
+ .port = 443,
+ .file = "/",
+ },
+ {.url = "https://foo@foo.example.com/?foobar",
+ .rc = 0,
+ .scheme = "https",
+ .userinfo = "foo",
+ .host = "foo.example.com",
+ .port = 443,
+ .file = "/?foobar",
+ },
+ {.url = "https://foo@[foo.example.com/",
+ .rc = -1,
+ },
+ {.url = "https://f%6fo@[foo.example.com/?fooba%72",
+ .rc = -1,
+ },
+ {.url = "https://foo@foo.example.com:81/",
+ .rc = 0,
+ .scheme = "https",
+ .userinfo = "foo",
+ .host = "foo.example.com",
+ .port = 81,
+ .file = "/",
+ },
+ {.url = "https://foo@foo.example.com:81/?foobar",
+ .rc = 0,
+ .scheme = "https",
+ .userinfo = "foo",
+ .host = "foo.example.com",
+ .port = 81,
+ .file = "/?foobar",
+ },
+ {.url = "https://foo@[1234::1]/",
+ .rc = 0,
+ .scheme = "https",
+ .userinfo = "foo",
+ .host = "[1234::1]",
+ .port = 443,
+ .file = "/",
+ },
+ {.url = "https://foo@[1234::1]/?foobar",
+ .rc = 0,
+ .scheme = "https",
+ .userinfo = "foo",
+ .host = "[1234::1]",
+ .port = 443,
+ .file = "/?foobar",
+ },
+ {.url = "https://f%6fo@[12%334::1]:81/",
+ .rc = 0,
+ .scheme = "https",
+ .userinfo = "foo",
+ .host = "[1234::1]",
+ .port = 81,
+ .file = "/",
+ },
+ {.url = "https://foo@[1234::1]:81/?foobar",
+ .rc = 0,
+ .scheme = "https",
+ .userinfo = "foo",
+ .host = "[1234::1]",
+ .port = 81,
+ .file = "/?foobar",
+ },
+ {.url = "tftp://foo.e%78ample.com/foo/bar/b%61%7a",
+ .rc = 0,
+ .scheme = "tftp",
+ .host = "foo.example.com",
+ .port = 69,
+ .file = "/foo/bar/baz",
+ },
+ {.url = "tftp://foo.example.com/foo/bar/baz",
+ .rc = 0,
+ .scheme = "tftp",
+ .host = "foo.example.com",
+ .port = 69,
+ .file = "/foo/bar/baz",
+ },
+ {.url = "tftps://foo.example.com/foo/bar/baz",
+ .rc = 0,
+ .scheme = "tftps",
+ .host = "foo.example.com",
+ .port = 3713,
+ .file = "/foo/bar/baz",
+ },
+ {.url = "tftps://foo.example.com/foo/bar/baz;mode=netascii",
+ .rc = -1,
+ },
+ {.url = "tftps://foo.example.com/foo/bar/baz;mode=octet",
+ .rc = 0,
+ .scheme = "tftps",
+ .host = "foo.example.com",
+ .port = 3713,
+ .file = "/foo/bar/baz",
+ },
+ {.url = "tftps://foo.example.com/foo/bar/baz;mode=invalid",
+ .rc = -1,
+ },
+ {.url = "",
+ },
+};
+
+static int
+string_test (char *name, char *a, char *b)
+{
+ if ((a && !b) || (!a && b))
+ {
+ printf("expected %s \"%s\", got \"%s\"\n", name, a, b);
+ return -1;
+ }
+ if (a && b && strcmp(a, b))
+ {
+ printf("expected %s \"%s\", got \"%s\"\n", name, a, b);
+ return -1;
+ }
+ return 0;
+}
+
+int
+main(void)
+{
+ unsigned int i;
+ int rc;
+
+ for (i = 0; tests[i].url[0] != '\0'; i++)
+ {
+ char *scheme, *userinfo, *host, *file;
+ int port;
+
+ printf("======= url: \"%s\"\n", tests[i].url);
+ rc = extract_url_info(tests[i].url, strlen(tests[i].url) + 1,
+ &scheme, &userinfo, &host, &port, &file);
+ if (tests[i].rc != rc)
+ {
+ printf(" extract_url_info(...) = %d\n", rc);
+ exit(1);
+ }
+ else if (rc >= 0)
+ {
+ if (string_test("scheme", tests[i].scheme, scheme) < 0)
+ exit(1);
+ if (string_test("userinfo", tests[i].userinfo, userinfo) < 0)
+ exit(1);
+ if (string_test("host", tests[i].host, host) < 0)
+ exit(1);
+ if (port != tests[i].port)
+ {
+ printf(" bad port \"%d\" should have been \"%d\"\n",
+ port, tests[i].port);
+ exit(1);
+ }
+ if (string_test("file", tests[i].file, file) < 0)
+ exit(1);
+ }
+ free(scheme);
+ free(userinfo);
+ free(host);
+ free(file);
+ }
+ printf("everything worked?!?\n");
+}
+#endif
diff --git a/include/grub/misc.h b/include/grub/misc.h
index 06208143779..4737da1eaa9 100644
--- a/include/grub/misc.h
+++ b/include/grub/misc.h
@@ -85,6 +85,7 @@ int EXPORT_FUNC(grub_strncmp) (const char *s1, const char *s2, grub_size_t n);
char *EXPORT_FUNC(grub_strchr) (const char *s, int c);
char *EXPORT_FUNC(grub_strrchr) (const char *s, int c);
+char *EXPORT_FUNC(grub_strchrnul) (const char *s, int c);
int EXPORT_FUNC(grub_strword) (const char *s, const char *w);
/* Copied from gnulib.
@@ -207,6 +208,50 @@ grub_toupper (int c)
return c;
}
+static inline char *
+grub_strcasestr (const char *haystack, const char *needle)
+{
+ /* Be careful not to look at the entire extent of haystack or needle
+ until needed. This is useful because of these two cases:
+ - haystack may be very long, and a match of needle found early,
+ - needle may be very long, and not even a short initial segment of
+ needle may be found in haystack. */
+ if (*needle != '\0')
+ {
+ /* Speed up the following searches of needle by caching its first
+ character. */
+ char b = *needle++;
+
+ for (;; haystack++)
+ {
+ if (*haystack == '\0')
+ /* No match. */
+ return 0;
+ if (grub_tolower(*haystack) == grub_tolower(b))
+ /* The first character matches. */
+ {
+ const char *rhaystack = haystack + 1;
+ const char *rneedle = needle;
+
+ for (;; rhaystack++, rneedle++)
+ {
+ if (*rneedle == '\0')
+ /* Found a match. */
+ return (char *) haystack;
+ if (*rhaystack == '\0')
+ /* No match. */
+ return 0;
+ if (grub_tolower(*rhaystack) != grub_tolower(*rneedle))
+ /* Nothing in this round. */
+ break;
+ }
+ }
+ }
+ }
+ else
+ return (char *) haystack;
+}
+
static inline int
grub_strcasecmp (const char *s1, const char *s2)
{
diff --git a/include/grub/net/url.h b/include/grub/net/url.h
new file mode 100644
index 00000000000..a215fa27d0a
--- /dev/null
+++ b/include/grub/net/url.h
@@ -0,0 +1,28 @@
+/* url.h - prototypes for url parsing functions */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2016 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_URL_HEADER
+#define GRUB_URL_HEADER 1
+
+int
+EXPORT_FUNC(extract_url_info) (const char *urlbuf, grub_size_t buflen,
+ char **scheme, char **userinfo,
+ char **host, int *port, char **file);
+
+#endif /* GRUB_URL_HEADER */
--
2.17.1