summaryrefslogtreecommitdiff
path: root/src/util/uri.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/uri.c')
-rw-r--r--src/util/uri.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/src/util/uri.c b/src/util/uri.c
new file mode 100644
index 000000000..87101d7e1
--- /dev/null
+++ b/src/util/uri.c
@@ -0,0 +1,344 @@
+/**
+ * Copyright (C) 2016,2017 Jack Engqvist Johansson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "gnunet_uri_lib.h"
+
+
+/**
+ * Parse a non null terminated string into an integer.
+ *
+ * str: the string containing the number.
+ * len: Number of characters to parse.
+ */
+static inline int
+natoi (const char *str,
+ size_t len)
+{
+ int i, r = 0;
+ for (i = 0; i < len; i++) {
+ r *= 10;
+ r += str[i] - '0';
+ }
+
+ return r;
+}
+
+
+/**
+ * Check if a URL is relative (no scheme and hostname).
+ *
+ * url: the string containing the URL to check.
+ *
+ * Returns 1 if relative, otherwise 0.
+ */
+static inline int
+is_relative (const char *url)
+{
+ return (*url == '/') ? 1 : 0;
+}
+
+
+/**
+ * Parse the scheme of a URL by inserting a null terminator after the scheme.
+ *
+ * str: the string containing the URL to parse. Will be modified.
+ *
+ * Returns a pointer to the hostname on success, otherwise NULL.
+ */
+static inline char *
+parse_scheme (char *str)
+{
+ char *s;
+
+ /* If not found or first in string, return error */
+ s = strchr (str, ':');
+ if (s == NULL || s == str) {
+ return NULL;
+ }
+
+ /* If not followed by two slashes, return error */
+ if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') {
+ return NULL;
+ }
+
+ *s = '\0'; // Replace ':' with NULL
+
+ return s + 3;
+}
+
+
+/**
+ * Find a character in a string, replace it with '\0' and return the next
+ * character in the string.
+ *
+ * str: the string to search in.
+ * find: the character to search for.
+ *
+ * Returns a pointer to the character after the one to search for. If not
+ * found, NULL is returned.
+ */
+static inline char *
+find_and_terminate (char *str,
+ char find)
+{
+ str = strchr(str, find);
+ if (NULL == str) {
+ return NULL;
+ }
+
+ *str = '\0';
+ return str + 1;
+}
+
+
+/* Yes, the following functions could be implemented as preprocessor macros
+ instead of inline functions, but I think that this approach will be more
+ clean in this case. */
+static inline char *
+find_fragment (char *str)
+{
+ return find_and_terminate (str, '#');
+}
+
+
+static inline char *
+find_query (char *str)
+{
+ return find_and_terminate (str, '?');
+}
+
+
+static inline char *
+find_path (char *str)
+{
+ return find_and_terminate (str, '/');
+}
+
+
+/**
+ * Parse a URL to a struct.
+ *
+ * The URL string should be in one of the following formats:
+ *
+ * Absolute URL:
+ * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ] [ "#" fragment ]
+ *
+ * Relative URL:
+ * path [ "?" query ] [ "#" fragment ]
+ *
+ * The following parts will be parsed to the corresponding struct member.
+ *
+ * *url: a pointer to the struct where to store the parsed values.
+ * *url_str: a pointer to the url to be parsed (null terminated). The string
+ * will be modified.
+ *
+ * Returns 0 on success, otherwise -1.
+ */
+int
+GNUNET_uri_parse (struct GNUNET_Uri *url,
+ char *u)
+{
+ if (NULL == url || NULL == u) {
+ return -1;
+ }
+
+ memset(url, 0, sizeof (struct GNUNET_Uri));
+
+ /* (Fragment) */
+ url->fragment = find_fragment (u);
+
+ /* (Query) */
+ url->query = find_query (u);
+
+ /* Relative URL? Parse scheme and hostname */
+ if (!is_relative (u)) {
+ /* Scheme */
+ url->scheme = u;
+ u = parse_scheme (u);
+ if (u == NULL) {
+ return -1;
+ }
+
+ /* Host */
+ if ('\0' == *u) {
+ return -1;
+ }
+ url->host = u;
+
+ /* (Path) */
+ url->path = find_path (u);
+
+ /* (Credentials) */
+ u = strchr (url->host, '@');
+ if (NULL != u) {
+ /* Missing credentials? */
+ if (u == url->host) {
+ return -1;
+ }
+
+ url->username = url->host;
+ url->host = u + 1;
+ *u = '\0';
+
+ u = strchr (url->username, ':');
+ if (NULL == u) {
+ return -1;
+ }
+
+ url->password = u + 1;
+ *u = '\0';
+ }
+
+ /* Missing hostname? */
+ if ('\0' == *url->host) {
+ return -1;
+ }
+
+ /* (Port) */
+ u = strchr (url->host, ':');
+ if (NULL != u && (NULL == url->path || u < url->path)) {
+ *(u++) = '\0';
+ if ('\0' == *u) {
+ return -1;
+ }
+
+ if (url->path) {
+ url->port = natoi (u, url->path - u - 1);
+ } else {
+ url->port = atoi (u);
+ }
+ }
+
+ /* Missing hostname? */
+ if ('\0' == *url->host) {
+ return -1;
+ }
+ } else {
+ /* (Path) */
+ url->path = find_path (u);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Split a path into several strings.
+ *
+ * No data is copied, the slashed are used as null terminators and then
+ * pointers to each path part will be stored in **parts. Double slashes will be
+ * treated as one.
+ *
+ * *path: the path to split. The string will be modified.
+ * **parts: a pointer to an array of (char *) where to store the result.
+ * max_parts: max number of parts to parse.
+ *
+ * Returns the number of parsed items. -1 on error.
+ */
+int
+GNUNET_uri_split_path (char *path,
+ char **parts,
+ int max_parts)
+{
+ int i = 0;
+
+ if (NULL == path || '\0' == *path) {
+ return -1;
+ }
+
+ do {
+ /* Forward to after slashes */
+ while (*path == '/') path++;
+
+ if ('\0' == *path) {
+ break;
+ }
+
+ parts[i++] = path;
+
+ path = strchr (path, '/');
+ if (NULL == path) {
+ break;
+ }
+
+ *(path++) = '\0';
+ } while (i < max_parts);
+
+ return i;
+}
+
+
+/**
+ * Parse a query string into a key/value struct.
+ *
+ * The query string should be a null terminated string of parameters separated by
+ * a delimiter. Each parameter are checked for the equal sign character. If it
+ * appears in the parameter, it will be used as a null terminator and the part
+ * that comes after it will be the value of the parameter.
+ *
+ * No data are copied, the equal sign and delimiters are used as null
+ * terminators and then pointers to each parameter key and value will be stored
+ * in the yuarel_param struct.
+ *
+ * *query: the query string to parse. The string will be modified.
+ * delimiter: the character that separates the key/value pairs from eachother.
+ * *params: an array of (struct yuarel_param) where to store the result.
+ * max_values: max number of parameters to parse.
+ *
+ * Returns the number of parsed items. -1 on error.
+ */
+int
+GNUNET_uri_parse_query (char *query,
+ char delimiter,
+ struct GNUNET_UriParam *params,
+ int max_params)
+{
+ int i = 0;
+
+ if (NULL == query || '\0' == *query) {
+ return -1;
+ }
+
+ params[i++].key = query;
+ while (i < max_params && NULL != (query = strchr (query, delimiter))) {
+ *query = '\0';
+ params[i].key = ++query;
+ params[i].val = NULL;
+
+ /* Go back and split previous param */
+ if (i > 0) {
+ if ((params[i - 1].val = strchr (params[i - 1].key, '=')) != NULL) {
+ *(params[i - 1].val)++ = '\0';
+ }
+ }
+ i++;
+ }
+
+ /* Go back and split last param */
+ if ((params[i - 1].val = strchr (params[i - 1].key, '=')) != NULL) {
+ *(params[i - 1].val)++ = '\0';
+ }
+
+ return i;
+}