|
|
|
@@ -64,6 +64,7 @@ typedef struct { |
|
|
|
int is_akamai; |
|
|
|
int rw_timeout; |
|
|
|
char *mime_type; |
|
|
|
char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name) |
|
|
|
} HTTPContext; |
|
|
|
|
|
|
|
#define OFFSET(x) offsetof(HTTPContext, x) |
|
|
|
@@ -80,6 +81,7 @@ static const AVOption options[] = { |
|
|
|
{"post_data", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E }, |
|
|
|
{"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, |
|
|
|
{"mime_type", "set MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 }, |
|
|
|
{"cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 }, |
|
|
|
{NULL} |
|
|
|
}; |
|
|
|
#define HTTP_CLASS(flavor)\ |
|
|
|
@@ -359,11 +361,117 @@ static int process_line(URLContext *h, char *line, int line_count, |
|
|
|
s->is_akamai = 1; |
|
|
|
} else if (!av_strcasecmp (tag, "Content-Type")) { |
|
|
|
av_free(s->mime_type); s->mime_type = av_strdup(p); |
|
|
|
} else if (!av_strcasecmp (tag, "Set-Cookie")) { |
|
|
|
if (!s->cookies) { |
|
|
|
if (!(s->cookies = av_strdup(p))) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
} else { |
|
|
|
char *tmp = s->cookies; |
|
|
|
size_t str_size = strlen(tmp) + strlen(p) + 2; |
|
|
|
if (!(s->cookies = av_malloc(str_size))) { |
|
|
|
s->cookies = tmp; |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
} |
|
|
|
snprintf(s->cookies, str_size, "%s\n%s", tmp, p); |
|
|
|
av_free(tmp); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Create a string containing cookie values for use as a HTTP cookie header |
|
|
|
* field value for a particular path and domain from the cookie values stored in |
|
|
|
* the HTTP protocol context. The cookie string is stored in *cookies. |
|
|
|
* |
|
|
|
* @return a negative value if an error condition occurred, 0 otherwise |
|
|
|
*/ |
|
|
|
static int get_cookies(HTTPContext *s, char **cookies, const char *path, |
|
|
|
const char *domain) |
|
|
|
{ |
|
|
|
// cookie strings will look like Set-Cookie header field values. Multiple |
|
|
|
// Set-Cookie fields will result in multiple values delimited by a newline |
|
|
|
int ret = 0; |
|
|
|
char *next, *cookie, *set_cookies = av_strdup(s->cookies), *cset_cookies = set_cookies; |
|
|
|
|
|
|
|
if (!set_cookies) return AVERROR(EINVAL); |
|
|
|
|
|
|
|
*cookies = NULL; |
|
|
|
while ((cookie = av_strtok(set_cookies, "\n", &next))) { |
|
|
|
int domain_offset = 0; |
|
|
|
char *param, *next_param, *cdomain = NULL, *cpath = NULL, *cvalue = NULL; |
|
|
|
set_cookies = NULL; |
|
|
|
|
|
|
|
while ((param = av_strtok(cookie, "; ", &next_param))) { |
|
|
|
cookie = NULL; |
|
|
|
if (!av_strncasecmp("path=", param, 5)) { |
|
|
|
cpath = av_strdup(¶m[5]); |
|
|
|
} else if (!av_strncasecmp("domain=", param, 7)) { |
|
|
|
cdomain = av_strdup(¶m[7]); |
|
|
|
} else if (!av_strncasecmp("secure", param, 6) || |
|
|
|
!av_strncasecmp("comment", param, 7) || |
|
|
|
!av_strncasecmp("max-age", param, 7) || |
|
|
|
!av_strncasecmp("version", param, 7)) { |
|
|
|
// ignore Comment, Max-Age, Secure and Version |
|
|
|
} else { |
|
|
|
cvalue = av_strdup(param); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ensure all of the necessary values are valid |
|
|
|
if (!cdomain || !cpath || !cvalue) { |
|
|
|
av_log(s, AV_LOG_WARNING, |
|
|
|
"Invalid cookie found, no value, path or domain specified\n"); |
|
|
|
goto done_cookie; |
|
|
|
} |
|
|
|
|
|
|
|
// check if the request path matches the cookie path |
|
|
|
if (av_strncasecmp(path, cpath, strlen(cpath))) |
|
|
|
goto done_cookie; |
|
|
|
|
|
|
|
// the domain should be at least the size of our cookie domain |
|
|
|
domain_offset = strlen(domain) - strlen(cdomain); |
|
|
|
if (domain_offset < 0) |
|
|
|
goto done_cookie; |
|
|
|
|
|
|
|
// match the cookie domain |
|
|
|
if (av_strcasecmp(&domain[domain_offset], cdomain)) |
|
|
|
goto done_cookie; |
|
|
|
|
|
|
|
// cookie parameters match, so copy the value |
|
|
|
if (!*cookies) { |
|
|
|
if (!(*cookies = av_strdup(cvalue))) { |
|
|
|
ret = AVERROR(ENOMEM); |
|
|
|
goto done_cookie; |
|
|
|
} |
|
|
|
} else { |
|
|
|
char *tmp = *cookies; |
|
|
|
size_t str_size = strlen(cvalue) + strlen(*cookies) + 3; |
|
|
|
if (!(*cookies = av_malloc(str_size))) { |
|
|
|
ret = AVERROR(ENOMEM); |
|
|
|
goto done_cookie; |
|
|
|
} |
|
|
|
snprintf(*cookies, str_size, "%s; %s", tmp, cvalue); |
|
|
|
av_free(tmp); |
|
|
|
} |
|
|
|
|
|
|
|
done_cookie: |
|
|
|
av_free(cdomain); |
|
|
|
av_free(cpath); |
|
|
|
av_free(cvalue); |
|
|
|
if (ret < 0) { |
|
|
|
if (*cookies) av_freep(cookies); |
|
|
|
av_free(cset_cookies); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
av_free(cset_cookies); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static inline int has_header(const char *str, const char *header) |
|
|
|
{ |
|
|
|
/* header + 2 to skip over CRLF prefix. (make sure you have one!) */ |
|
|
|
@@ -460,6 +568,14 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, |
|
|
|
if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type) |
|
|
|
len += av_strlcatf(headers + len, sizeof(headers) - len, |
|
|
|
"Content-Type: %s\r\n", s->content_type); |
|
|
|
if (!has_header(s->headers, "\r\nCookie: ") && s->cookies) { |
|
|
|
char *cookies = NULL; |
|
|
|
if (!get_cookies(s, &cookies, path, hoststr)) { |
|
|
|
len += av_strlcatf(headers + len, sizeof(headers) - len, |
|
|
|
"Cookie: %s\r\n", cookies); |
|
|
|
av_free(cookies); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* now add in custom headers */ |
|
|
|
if (s->headers) |
|
|
|
|