url construction helpers

This commit is contained in:
Florian Dold 2018-01-31 17:47:01 +01:00
parent 6f9354cac6
commit 3859a40f24
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 369 additions and 2 deletions

View File

@ -22,6 +22,7 @@
#define TALER_UTIL_H
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
#include "taler_amount_lib.h"
#include "taler_crypto_lib.h"
@ -134,4 +135,62 @@ const struct GNUNET_OS_ProjectData *
TALER_project_data_default (void);
/**
* 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);
/**
* 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,
* only 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,
...);
/**
* 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 ... 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,
...);
/**
* 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,
...);
#endif

View File

@ -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

View File

@ -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 */