From 9405f733d90f64ee45f47a253056c09caa7bf838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Wed, 24 Mar 2010 22:32:05 +0000 Subject: [PATCH] Split out http authentication handling into a separate file This prepares for adding support for more authentication methods Originally committed as revision 22660 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavformat/Makefile | 2 +- libavformat/http.c | 35 +++++++---- libavformat/httpauth.c | 132 +++++++++++++++++++++++++++++++++++++++++ libavformat/httpauth.h | 54 +++++++++++++++++ 4 files changed, 212 insertions(+), 11 deletions(-) create mode 100644 libavformat/httpauth.c create mode 100644 libavformat/httpauth.h diff --git a/libavformat/Makefile b/libavformat/Makefile index b669dad495..0832155c32 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -272,7 +272,7 @@ OBJS+= avio.o aviobuf.o OBJS-$(CONFIG_FILE_PROTOCOL) += file.o OBJS-$(CONFIG_GOPHER_PROTOCOL) += gopher.o -OBJS-$(CONFIG_HTTP_PROTOCOL) += http.o +OBJS-$(CONFIG_HTTP_PROTOCOL) += http.o httpauth.o OBJS-$(CONFIG_PIPE_PROTOCOL) += file.o OBJS-$(CONFIG_RTMP_PROTOCOL) += rtmpproto.o rtmppkt.o OBJS-$(CONFIG_RTP_PROTOCOL) += rtpproto.o diff --git a/libavformat/http.c b/libavformat/http.c index 638102c815..e697578486 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -19,7 +19,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/base64.h" #include "libavutil/avstring.h" #include "avformat.h" #include @@ -27,6 +26,7 @@ #include "internal.h" #include "network.h" #include "os_support.h" +#include "httpauth.h" /* XXX: POST protocol is not completely implemented because ffmpeg uses only a subset of it. */ @@ -44,6 +44,7 @@ typedef struct { int64_t chunksize; /**< Used if "Transfer-Encoding: chunked" otherwise -1. */ int64_t off, filesize; char location[URL_SIZE]; + HTTPAuthState auth_state; } HTTPContext; static int http_connect(URLContext *h, const char *path, const char *hoststr, @@ -60,6 +61,7 @@ static int http_open_cnx(URLContext *h) char path1[1024]; char buf[1024]; int port, use_proxy, err, location_changed = 0, redirects = 0; + HTTPAuthType cur_auth_type; HTTPContext *s = h->priv_data; URLContext *hd = NULL; @@ -93,8 +95,16 @@ static int http_open_cnx(URLContext *h) goto fail; s->hd = hd; + cur_auth_type = s->auth_state.auth_type; if (http_connect(h, path, hoststr, auth, &location_changed) < 0) goto fail; + if (s->http_code == 401) { + if (cur_auth_type == HTTP_AUTH_NONE && s->auth_state.auth_type != HTTP_AUTH_NONE) { + url_close(hd); + goto redo; + } else + goto fail; + } if ((s->http_code == 302 || s->http_code == 303) && location_changed == 1) { /* url moved, get next */ url_close(hd); @@ -125,6 +135,7 @@ static int http_open(URLContext *h, const char *uri, int flags) s->filesize = -1; s->chunksize = -1; s->off = 0; + memset(&s->auth_state, 0, sizeof(s->auth_state)); av_strlcpy(s->location, uri, URL_SIZE); ret = http_open_cnx(h); @@ -193,8 +204,9 @@ static int process_line(URLContext *h, char *line, int line_count, dprintf(NULL, "http_code=%d\n", s->http_code); - /* error codes are 4xx and 5xx */ - if (s->http_code >= 400 && s->http_code < 600) + /* error codes are 4xx and 5xx, but regard 401 as a success, so we + * don't abort until all headers have been parsed. */ + if (s->http_code >= 400 && s->http_code < 600 && s->http_code != 401) return -1; } else { while (*p != '\0' && *p != ':') @@ -225,6 +237,10 @@ static int process_line(URLContext *h, char *line, int line_count, } else if (!strcmp (tag, "Transfer-Encoding") && !strncasecmp(p, "chunked", 7)) { s->filesize = -1; s->chunksize = 0; + } else if (!strcmp (tag, "WWW-Authenticate")) { + ff_http_auth_handle_header(&s->auth_state, tag, p); + } else if (!strcmp (tag, "Authentication-Info")) { + ff_http_auth_handle_header(&s->auth_state, tag, p); } } return 1; @@ -236,22 +252,21 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr, HTTPContext *s = h->priv_data; int post, err; char line[1024]; - char *auth_b64; - int auth_b64_len = (strlen(auth) + 2) / 3 * 4 + 1; + char *authstr = NULL; int64_t off = s->off; /* send http header */ post = h->flags & URL_WRONLY; - auth_b64 = av_malloc(auth_b64_len); - av_base64_encode(auth_b64, auth_b64_len, auth, strlen(auth)); + authstr = ff_http_auth_create_response(&s->auth_state, auth, path, + post ? "POST" : "GET"); snprintf(s->buffer, sizeof(s->buffer), "%s %s HTTP/1.1\r\n" "User-Agent: %s\r\n" "Accept: */*\r\n" "Range: bytes=%"PRId64"-\r\n" "Host: %s\r\n" - "Authorization: Basic %s\r\n" + "%s" "Connection: close\r\n" "%s" "\r\n", @@ -260,10 +275,10 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr, LIBAVFORMAT_IDENT, s->off, hoststr, - auth_b64, + authstr ? authstr : "", post ? "Transfer-Encoding: chunked\r\n" : ""); - av_freep(&auth_b64); + av_freep(&authstr); if (http_write(h, s->buffer, strlen(s->buffer)) < 0) return AVERROR(EIO); diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c new file mode 100644 index 0000000000..c272de4472 --- /dev/null +++ b/libavformat/httpauth.c @@ -0,0 +1,132 @@ +/* + * HTTP authentication + * Copyright (c) 2010 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "httpauth.h" +#include "libavutil/base64.h" +#include "libavutil/avstring.h" +#include "avformat.h" +#include + +static void parse_key_value(const char *params, + void (*callback_get_buf)(HTTPAuthState *state, + const char *key, int key_len, + char **dest, int *dest_len), HTTPAuthState *state) +{ + const char *ptr = params; + + /* Parse key=value pairs. */ + for (;;) { + const char *key; + char *dest = NULL, *dest_end; + int key_len, dest_len = 0; + + /* Skip whitespace and potential commas. */ + while (*ptr && (isspace(*ptr) || *ptr == ',')) + ptr++; + if (!*ptr) + break; + + key = ptr; + + if (!(ptr = strchr(key, '='))) + break; + ptr++; + key_len = ptr - key; + + callback_get_buf(state, key, key_len, &dest, &dest_len); + dest_end = dest + dest_len - 1; + + if (*ptr == '\"') { + ptr++; + while (*ptr && *ptr != '\"') { + if (*ptr == '\\') { + if (!ptr[1]) + break; + if (dest && dest < dest_end) + *dest++ = ptr[1]; + ptr += 2; + } else { + if (dest && dest < dest_end) + *dest++ = *ptr; + ptr++; + } + } + if (*ptr == '\"') + ptr++; + } else { + for (; *ptr && !(isspace(*ptr) || *ptr == ','); ptr++) + if (dest && dest < dest_end) + *dest++ = *ptr; + } + if (dest) + *dest = 0; + } +} + +static void handle_basic_params(HTTPAuthState *state, const char *key, + int key_len, char **dest, int *dest_len) +{ + if (!strncmp(key, "realm=", key_len)) { + *dest = state->realm; + *dest_len = sizeof(state->realm); + } +} + +void ff_http_auth_handle_header(HTTPAuthState *state, const char *key, + const char *value) +{ + if (!state) + return; + + if (!strcmp(key, "WWW-Authenticate")) { + const char *p; + if (av_stristart(value, "Basic ", &p) && + state->auth_type <= HTTP_AUTH_BASIC) { + state->auth_type = HTTP_AUTH_BASIC; + state->realm[0] = 0; + parse_key_value(p, handle_basic_params, state); + } + } +} + +char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth, + const char *path, const char *method) +{ + char *authstr = NULL; + + if (!auth || !strchr(auth, ':')) + return NULL; + + if (state->auth_type == HTTP_AUTH_BASIC) { + int auth_b64_len = (strlen(auth) + 2) / 3 * 4 + 1; + int len = auth_b64_len + 30; + char *ptr; + authstr = av_malloc(len); + if (!authstr) + return NULL; + snprintf(authstr, len, "Authorization: Basic "); + ptr = authstr + strlen(authstr); + av_base64_encode(ptr, auth_b64_len, auth, strlen(auth)); + av_strlcat(ptr, "\r\n", len); + } + return authstr; +} + diff --git a/libavformat/httpauth.h b/libavformat/httpauth.h new file mode 100644 index 0000000000..47d11af51a --- /dev/null +++ b/libavformat/httpauth.h @@ -0,0 +1,54 @@ +/* + * HTTP authentication + * Copyright (c) 2010 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_HTTPAUTH_H +#define AVFORMAT_HTTPAUTH_H + +/** + * Authentication types, ordered from weakest to strongest. + */ +typedef enum HTTPAuthType { + HTTP_AUTH_NONE = 0, /**< No authentication specified */ + HTTP_AUTH_BASIC, /**< HTTP 1.0 Basic auth from RFC 1945 + * (also in RFC 2617) */ +} HTTPAuthType; + +/** + * HTTP Authentication state structure. Must be zero-initialized + * before used with the functions below. + */ +typedef struct { + /** + * The currently chosen auth type. + */ + HTTPAuthType auth_type; + /** + * Authentication realm + */ + char realm[200]; +} HTTPAuthState; + +void ff_http_auth_handle_header(HTTPAuthState *state, const char *key, + const char *value); +char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth, + const char *path, const char *method); + +#endif /* AVFORMAT_HTTPAUTH_H */