aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2018-01-31 17:47:01 +0100
committerFlorian Dold <florian.dold@gmail.com>2018-01-31 17:47:01 +0100
commit3859a40f24e27ae6b825f0f4163d43bc95cc10d3 (patch)
tree33836fa86d89fc47d74d6d108bdca45aa159c707 /src/util
parent6f9354cac6a4c3d015475777a2249dfeff0fa641 (diff)
url construction helpers
Diffstat (limited to 'src/util')
-rw-r--r--src/util/Makefile.am12
-rw-r--r--src/util/util.c300
2 files changed, 310 insertions, 2 deletions
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index c05f756f2..4f9a6367d 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -57,11 +57,13 @@ libtalerutil_la_LDFLAGS = \
TESTS = \
test_amount \
- test_crypto
+ test_crypto \
+ test_url
check_PROGRAMS = \
test_amount \
- test_crypto
+ test_crypto \
+ test_url
test_amount_SOURCES = \
@@ -75,3 +77,9 @@ test_crypto_SOURCES = \
test_crypto_LDADD = \
-lgnunetutil \
libtalerutil.la
+
+test_url_SOURCES = \
+ test_url.c
+test_url_LDADD = \
+ -lgnunetutil \
+ libtalerutil.la
diff --git a/src/util/util.c b/src/util/util.c
index 8976b0a3d..00b67e25e 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -159,4 +159,304 @@ TALER_getopt_get_amount (char shortName,
}
+/**
+ * Check if a character is reserved and should
+ * be urlencoded.
+ *
+ * @param c character to look at
+ * @return #GNUNET_YES if @a c needs to be urlencoded,
+ * #GNUNET_NO otherwise
+ */
+static bool
+is_reserved(char c)
+{
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case '-': case '.': case '_': case '~':
+ return GNUNET_NO;
+ default:
+ break;
+ }
+ return GNUNET_YES;
+}
+
+
+/**
+ * URL-encode a string according to rfc3986.
+ *
+ * @param s string to encode
+ * @returns the urlencoded string, the caller must free it with GNUNET_free
+ */
+char *
+TALER_urlencode (const char *s)
+{
+ unsigned int new_size;
+ unsigned int i;
+ unsigned int t;
+ char *out;
+
+ new_size = strlen (s);
+ for (i = 0; i < strlen (s); i++)
+ if (GNUNET_YES == is_reserved (s[i]))
+ new_size += 2;
+ out = GNUNET_malloc (new_size + 1);
+ for (i = 0, t = 0; i < strlen (s); i++, t++)
+ {
+ if (GNUNET_YES == is_reserved (s[i]))
+ {
+ snprintf(&out[t], 4, "%%%02X", s[i]);
+ t += 2;
+ continue;
+ }
+ out[t] = s[i];
+ }
+ return out;
+}
+
+
+/**
+ * Grow a string in a buffer with the given size.
+ * The buffer is re-allocated if necessary.
+ *
+ * @param s string buffer
+ * @param p the string to append
+ * @param n pointer to the allocated size of n
+ * @returns pointer to the resulting buffer,
+ * might differ from @a s (!!)
+ */
+static char *
+grow_string (char *s, const char *p, size_t *n)
+{
+ for (; strlen (s) + strlen (p) >= *n; *n *= 2);
+ s = GNUNET_realloc (s, *n);
+ GNUNET_assert (NULL != s);
+ strncat (s, p, *n);
+ return s;
+}
+
+
+/**
+ * Grow a string in a buffer with the given size.
+ * The buffer is re-allocated if necessary.
+ *
+ * Ensures that slashes are removed or added when joining paths.
+ *
+ * @param s string buffer
+ * @param p the string to append
+ * @param n pointer to the allocated size of n
+ * @returns pointer to the resulting buffer,
+ * might differ from @a s (!!)
+ */
+static char *
+grow_string_path (char *s, const char *p, size_t *n)
+{
+ char a = (0 == strlen (s)) ? '\0' : s[strlen (s) - 1];
+ char b = (0 == strlen (p)) ? '\0' : p[0];
+
+ if ( (a == '/') && (b == '/'))
+ {
+ p++;
+ }
+ else if ( (a != '/') && (b != '/'))
+ {
+ if (NULL == (s = grow_string (s, "/", n)))
+ return NULL;
+ }
+ return grow_string (s, p, n);
+}
+
+
+/**
+ * Make an absolute URL with query parameters.
+ *
+ * @param base_url absolute base URL to use
+ * @param path path of the url
+ * @param ... NULL-terminated key-value pairs (char *) for query parameters,
+ * the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_join (const char *base_url,
+ const char *path,
+ ...)
+{
+ size_t n = 256;
+ char *res = GNUNET_malloc (n);
+ unsigned int iparam = 0;
+ char *enc;
+ va_list args;
+
+ GNUNET_assert (NULL != res);
+
+ grow_string (res, base_url, &n);
+
+ grow_string_path (res, path, &n);
+
+ va_start (args, path);
+
+ while (1) {
+ char *key;
+ char *value;
+ key = va_arg (args, char *);
+ if (NULL == key)
+ break;
+ value = va_arg (args, char *);
+ if (NULL == value)
+ continue;
+ grow_string (res, (0 == iparam) ? "?" : "&", &n);
+ iparam++;
+ grow_string (res, key, &n);
+ grow_string (res, "=", &n);
+ enc = TALER_urlencode (value);
+ grow_string (res, enc, &n);
+ GNUNET_free (enc);
+ }
+
+ va_end (args);
+
+ return res;
+}
+
+
+/**
+ * Make an absolute URL for the given parameters.
+ *
+ * @param proto protocol for the URL (typically https)
+ * @param host hostname for the URL
+ * @param prefix prefix for the URL
+ * @param path path for the URL
+ * @param args NULL-terminated key-value pairs (char *) for query parameters,
+ * the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+url_absolute_raw_va (const char *proto,
+ const char *host,
+ const char *prefix,
+ const char *path,
+ va_list args)
+{
+ size_t n = 256;
+ char *res = GNUNET_malloc (n);
+ char *enc;
+ unsigned int iparam = 0;
+
+ grow_string (res, proto, &n);
+ grow_string (res, "://", &n);
+ grow_string (res, host, &n);
+
+ grow_string_path (res, prefix, &n);
+
+ grow_string_path (res, path, &n);
+
+ while (1) {
+ char *key;
+ char *value;
+ key = va_arg (args, char *);
+ if (NULL == key)
+ break;
+ value = va_arg (args, char *);
+ if (NULL == value)
+ continue;
+ grow_string (res, (0 == iparam) ? "?" : "&", &n);
+ iparam++;
+ grow_string (res, key, &n);
+ grow_string (res, "=", &n);
+ enc = TALER_urlencode (value);
+ grow_string (res, enc, &n);
+ GNUNET_free (enc);
+ }
+
+ return res;
+}
+
+
+/**
+ * Make an absolute URL for the given parameters.
+ *
+ * @param proto protocol for the URL (typically https)
+ * @param host hostname for the URL
+ * @param prefix prefix for the URL
+ * @param path path for the URL
+ * @param args NULL-terminated key-value pairs (char *) for query parameters,
+ * the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_absolute_raw (const char *proto,
+ const char *host,
+ const char *prefix,
+ const char *path,
+ ...)
+{
+ char *result;
+ va_list args;
+
+ va_start (args, path);
+ result = url_absolute_raw_va (proto, host, prefix, path, args);
+ va_end (args);
+ return result;
+}
+
+
+/**
+ * Make an absolute URL for a given MHD connection.
+ *
+ * @param path path of the url
+ * @param ... NULL-terminated key-value pairs (char *) for query parameters,
+ * the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_absolute_mhd (struct MHD_Connection *connection,
+ const char *path,
+ ...)
+{
+ /* By default we assume we're running under HTTPS */
+ const char *proto = "https";
+ const char *forwarded_proto = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "X-Forwarded-Proto");
+ const char *host;
+ const char *forwarded_host;
+ const char *prefix;
+ va_list args;
+ char *result;
+
+ if (NULL != forwarded_proto)
+ proto = forwarded_proto;
+
+ host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Host");
+ forwarded_host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "X-Forwarded-Host");
+
+ prefix = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "X-Forwarded-Prefix");
+ if (NULL == prefix)
+ prefix = "";
+
+ if (NULL != forwarded_host)
+ host = forwarded_host;
+
+ if (NULL == host)
+ {
+ /* Should never happen, at last the host header should be defined */
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ va_start (args, path);
+ result = TALER_url_absolute_raw (proto, host, prefix, path, args);
+ va_end (args);
+ return result;
+}
+
+
/* end of util.c */