/** * 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 #include #include #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; }