diff --git a/src/link.c b/src/link.c index 0119f7a..36f1727 100644 --- a/src/link.c +++ b/src/link.c @@ -77,10 +77,15 @@ static CURL *Link_to_curl(Link *link) if (ret) { lprintf(error, "%s", curl_easy_strerror(ret)); } - ret = curl_easy_setopt(curl, CURLOPT_URL, link->f_url); + + char *escaped_spaces = escape_char(link->f_url, ESCAPED_CHAR_SPACE); + ret = curl_easy_setopt(curl, CURLOPT_URL, + escaped_spaces ? escaped_spaces : link->f_url); if (ret) { lprintf(error, "%s", curl_easy_strerror(ret)); } + free(escaped_spaces); + ret = curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1); if (ret) { lprintf(error, "%s", curl_easy_strerror(ret)); diff --git a/src/util.c b/src/util.c index 1e868f6..179be6a 100644 --- a/src/util.c +++ b/src/util.c @@ -49,6 +49,61 @@ char *path_append(const char *path, const char *filename) return str; } +char *escape_char(const char *url, const ESCAPE_CHAR c) +{ + char escape_me; + + /* A space, for example, becomes thrice bigger as '%20' after escaping */ + int how_bigger = 3; + + switch (c) { + case ESCAPED_CHAR_SPACE: + escape_me = ' '; + break; + default: + lprintf(error, "unknown character to escape: %d\n", c); + return NULL; + } + + if (!strchr(url, escape_me)) { + return NULL; + } + + int len = strnlen(url, MAX_PATH_LEN); + + /* Best case scenario of only one character to escape */ + if (len + 3 > MAX_PATH_LEN) { + lprintf(fatal, "URL too long: %s\n", url); + } + + char *escaped = CALLOC(MAX_PATH_LEN, sizeof(char)); + + int j = 0, k = 0; + for (int i = 0; i < len; i++) { + /* Precaution against writing beyond the buffer, since we do not count + * all spaces beforehand to know the exact amount of memory to calloc + * for the escaped URL. + */ + if (k > MAX_PATH_LEN) { + lprintf(fatal, "URL too long: %s\n", url); + } + if (url[i] == escape_me) { + switch (c) { + case ESCAPED_CHAR_SPACE: + strncpy(escaped + j, "%20", how_bigger); + break; + } + k = j + how_bigger; + j = k; + + } else { + escaped[j++] = url[i]; + } + } + return escaped; +} + + int64_t round_div(int64_t a, int64_t b) { return (a + (b / 2)) / b; diff --git a/src/util.h b/src/util.h index 5010de4..f66a925 100644 --- a/src/util.h +++ b/src/util.h @@ -9,6 +9,13 @@ #include #include +/** + * \brief URL characters that can be escaped + */ +typedef enum { + ESCAPED_CHAR_SPACE, +} ESCAPE_CHAR; + /** * \brief append a path * \details This function appends a path with the next level, while taking the @@ -17,6 +24,16 @@ */ char *path_append(const char *path, const char *filename); +/** + * \brief escape a single character in a URL + * \details This function escapes a character a URL and returns a pointer to + * the string with the escaped character. We don't use curl_easy_escape() on + * the entire URL because it would break the URL. For example, 'http://a c' + * becomes 'http:%2F%2Fa%20c', escaping more characters than we want. + * \note You need to free the char * after use. + */ +char *escape_char(const char *s, ESCAPE_CHAR c); + /** * \brief division, but rounded to the nearest integer rather than truncating */