X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fhttp.c;h=3d25d652d3196a2300271928b894fbddb96b0baf;hb=4106013523f46824d32fd5b469ea264fbdfdb591;hp=ee415667febe5757e35fc08fdc4a750cdae46310;hpb=86b2fe941126fd4fee86f2aca91b43019b7b0c5f;p=ffmpeg diff --git a/libavformat/http.c b/libavformat/http.c index ee415667feb..3d25d652d31 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -46,7 +46,7 @@ /* The IO buffer size is unrelated to the max URL size in itself, but needs * to be large enough to fit the full request headers (including long * path names). */ -#define BUFFER_SIZE MAX_URL_SIZE +#define BUFFER_SIZE (MAX_URL_SIZE + HTTP_HEADERS_SIZE) #define MAX_REDIRECTS 8 #define HTTP_SINGLE 1 #define HTTP_MUTLI 2 @@ -577,7 +577,7 @@ static int http_open(URLContext *h, const char *uri, int flags, "No trailing CRLF found in HTTP header. Adding it.\n"); ret = av_reallocp(&s->headers, len + 3); if (ret < 0) - return ret; + goto bail_out; s->headers[len] = '\r'; s->headers[len + 1] = '\n'; s->headers[len + 2] = '\0'; @@ -588,6 +588,7 @@ static int http_open(URLContext *h, const char *uri, int flags, return http_listen(h, uri, flags, options); } ret = http_open_cnx(h, options); +bail_out: if (ret < 0) av_dict_free(&s->chained_options); return ret; @@ -786,6 +787,7 @@ static int parse_set_cookie_expiry_time(const char *exp_str, struct tm *buf) static int parse_set_cookie(const char *set_cookie, AVDictionary **dict) { char *param, *next_param, *cstr, *back; + char *saveptr = NULL; if (!set_cookie[0]) return 0; @@ -803,8 +805,9 @@ static int parse_set_cookie(const char *set_cookie, AVDictionary **dict) } next_param = cstr; - while ((param = av_strtok(next_param, ";", &next_param))) { + while ((param = av_strtok(next_param, ";", &saveptr))) { char *name, *value; + next_param = NULL; param += strspn(param, WHITESPACES); if ((name = av_strtok(param, "=", &value))) { if (av_dict_set(dict, name, value, 0) < 0) { @@ -1064,6 +1067,7 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, // Set-Cookie fields will result in multiple values delimited by a newline int ret = 0; char *cookie, *set_cookies, *next; + char *saveptr = NULL; // destroy any cookies in the dictionary. av_dict_free(&s->cookie_dict); @@ -1076,10 +1080,11 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, return AVERROR(ENOMEM); *cookies = NULL; - while ((cookie = av_strtok(next, "\n", &next)) && !ret) { + while ((cookie = av_strtok(next, "\n", &saveptr)) && !ret) { AVDictionary *cookie_params = NULL; AVDictionaryEntry *cookie_entry, *e; + next = NULL; // store the cookie in a dict in case it is updated in the response if (parse_cookie(s, cookie, &s->cookie_dict)) av_log(s, AV_LOG_WARNING, "Unable to parse '%s'\n", cookie); @@ -1179,6 +1184,37 @@ static int http_read_header(URLContext *h, int *new_location) return err; } +/** + * Escape unsafe characters in path in order to pass them safely to the HTTP + * request. Insipred by the algorithm in GNU wget: + * - escape "%" characters not followed by two hex digits + * - escape all "unsafe" characters except which are also "reserved" + * - pass through everything else + */ +static void bprint_escaped_path(AVBPrint *bp, const char *path) +{ +#define NEEDS_ESCAPE(ch) \ + ((ch) <= ' ' || (ch) >= '\x7f' || \ + (ch) == '"' || (ch) == '%' || (ch) == '<' || (ch) == '>' || (ch) == '\\' || \ + (ch) == '^' || (ch) == '`' || (ch) == '{' || (ch) == '}' || (ch) == '|') + while (*path) { + char buf[1024]; + char *q = buf; + while (*path && q - buf < sizeof(buf) - 4) { + if (path[0] == '%' && av_isxdigit(path[1]) && av_isxdigit(path[2])) { + *q++ = *path++; + *q++ = *path++; + *q++ = *path++; + } else if (NEEDS_ESCAPE(*path)) { + q += snprintf(q, 4, "%%%02X", (uint8_t)*path++); + } else { + *q++ = *path++; + } + } + av_bprint_append_data(bp, buf, q - buf); + } +} + static int http_connect(URLContext *h, const char *path, const char *local_path, const char *hoststr, const char *auth, const char *proxyauth, int *new_location) @@ -1235,7 +1271,10 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, } #endif - av_bprintf(&request, "%s %s HTTP/1.1\r\n", method, path); + av_bprintf(&request, "%s ", method); + bprint_escaped_path(&request, path); + av_bprintf(&request, " HTTP/1.1\r\n"); + if (post && s->chunked_post) av_bprintf(&request, "Transfer-Encoding: chunked\r\n"); /* set default headers if needed */