From 24e30c883470beabbe88e84ead18ad0d24adc99a Mon Sep 17 00:00:00 2001 From: Ron McOuat Date: Thu, 27 Nov 2008 16:43:24 +0000 Subject: [PATCH] Add support for basic and digest authentication in souphttpsrc. Original commit message from CVS: Patch by: Ron McOuat * ext/soup/gstsouphttpsrc.c: (gst_soup_http_src_class_init), (gst_soup_http_src_init), (gst_soup_http_src_dispose), (gst_soup_http_src_set_property), (gst_soup_http_src_get_property), (gst_soup_http_src_authenticate_cb), (gst_soup_http_src_start): * ext/soup/gstsouphttpsrc.h: * tests/check/elements/souphttpsrc.c: (basic_auth_cb), (digest_auth_cb), (run_test), (GST_START_TEST), (souphttpsrc_suite), (run_server): Add support for basic and digest authentication in souphttpsrc. Fixes bug #561775. --- ChangeLog | 15 ++++ ext/soup/gstsouphttpsrc.c | 95 ++++++++++++++++++++++-- ext/soup/gstsouphttpsrc.h | 4 ++ tests/check/elements/souphttpsrc.c | 143 +++++++++++++++++++++++++++++++++++++ 4 files changed, 250 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index c1224b8..6f945ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,20 @@ 2008-11-27 Sebastian Dröge + Patch by: Ron McOuat + + * ext/soup/gstsouphttpsrc.c: (gst_soup_http_src_class_init), + (gst_soup_http_src_init), (gst_soup_http_src_dispose), + (gst_soup_http_src_set_property), (gst_soup_http_src_get_property), + (gst_soup_http_src_authenticate_cb), (gst_soup_http_src_start): + * ext/soup/gstsouphttpsrc.h: + * tests/check/elements/souphttpsrc.c: (basic_auth_cb), + (digest_auth_cb), (run_test), (GST_START_TEST), + (souphttpsrc_suite), (run_server): + Add support for basic and digest authentication in souphttpsrc. + Fixes bug #561775. + +2008-11-27 Sebastian Dröge + Patch by: Pepijn Van Eeckhoudt diff --git a/ext/soup/gstsouphttpsrc.c b/ext/soup/gstsouphttpsrc.c index 67219e0..139d333 100644 --- a/ext/soup/gstsouphttpsrc.c +++ b/ext/soup/gstsouphttpsrc.c @@ -121,6 +121,10 @@ enum PROP_USER_AGENT, PROP_AUTOMATIC_REDIRECT, PROP_PROXY, + PROP_USER_ID, + PROP_USER_PW, + PROP_PROXY_ID, + PROP_PROXY_PW, PROP_COOKIES, PROP_IRADIO_MODE, PROP_IRADIO_NAME, @@ -195,7 +199,9 @@ static void gst_soup_http_src_got_body_cb (SoupMessage * msg, GstSoupHTTPSrc * src); static void gst_soup_http_src_finished_cb (SoupMessage * msg, GstSoupHTTPSrc * src); - +static void gst_soup_http_src_authenticate_cb (SoupSession * session, + SoupMessage * msg, SoupAuth * auth, gboolean retrying, + GstSoupHTTPSrc * src); static void _do_init (GType type) @@ -262,12 +268,27 @@ gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass) g_param_spec_string ("proxy", "Proxy", "HTTP proxy server URI", "", G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, - PROP_COOKIES, g_param_spec_boxed ("cookies", "Cookies", - "HTTP request cookies", G_TYPE_STRV, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, - PROP_IS_LIVE, - g_param_spec_boolean ("is-live", "is-live", - "Act like a live source", FALSE, G_PARAM_READWRITE)); + PROP_USER_ID, + g_param_spec_string ("user-id", "user-id", + "HTTP location URI user id for authentication", "", + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_USER_PW, + g_param_spec_string ("user-pw", "user-pw", + "HTTP location URI user password for authentication", "", + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_PROXY_ID, + g_param_spec_string ("proxy-id", "proxy-id", + "HTTP proxy URI user id for authentication", "", G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_PROXY_PW, + g_param_spec_string ("proxy-pw", "proxy-pw", + "HTTP proxy URI user password for authentication", "", + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_COOKIES, + g_param_spec_boxed ("cookies", "Cookies", "HTTP request cookies", + G_TYPE_STRV, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_IS_LIVE, + g_param_spec_boolean ("is-live", "is-live", "Act like a live source", + FALSE, G_PARAM_READWRITE)); /* icecast stuff */ g_object_class_install_property (gobject_class, @@ -319,6 +340,10 @@ gst_soup_http_src_init (GstSoupHTTPSrc * src, GstSoupHTTPSrcClass * g_class) src->location = NULL; src->automatic_redirect = TRUE; src->user_agent = g_strdup (DEFAULT_USER_AGENT); + src->user_id = NULL; + src->user_pw = NULL; + src->proxy_id = NULL; + src->proxy_pw = NULL; src->cookies = NULL; src->icy_caps = NULL; src->iradio_mode = FALSE; @@ -358,6 +383,14 @@ gst_soup_http_src_dispose (GObject * gobject) soup_uri_free (src->proxy); src->proxy = NULL; } + g_free (src->user_id); + src->user_id = NULL; + g_free (src->user_pw); + src->user_pw = NULL; + g_free (src->proxy_id); + src->proxy_id = NULL; + g_free (src->proxy_pw); + src->proxy_pw = NULL; g_strfreev (src->cookies); g_free (src->iradio_name); src->iradio_name = NULL; @@ -435,6 +468,26 @@ gst_soup_http_src_set_property (GObject * object, guint prop_id, case PROP_IS_LIVE: gst_base_src_set_live (GST_BASE_SRC (src), g_value_get_boolean (value)); break; + case PROP_USER_ID: + if (src->user_id) + g_free (src->user_id); + src->user_id = g_value_dup_string (value); + break; + case PROP_USER_PW: + if (src->user_pw) + g_free (src->user_pw); + src->user_pw = g_value_dup_string (value); + break; + case PROP_PROXY_ID: + if (src->proxy_id) + g_free (src->proxy_id); + src->proxy_id = g_value_dup_string (value); + break; + case PROP_PROXY_PW: + if (src->proxy_pw) + g_free (src->proxy_pw); + src->proxy_pw = g_value_dup_string (value); + break; } done: return; @@ -487,6 +540,18 @@ gst_soup_http_src_get_property (GObject * object, guint prop_id, case PROP_IRADIO_TITLE: g_value_set_string (value, src->iradio_title); break; + case PROP_USER_ID: + g_value_set_string (value, src->user_id); + break; + case PROP_USER_PW: + g_value_set_string (value, src->user_pw); + break; + case PROP_PROXY_ID: + g_value_set_string (value, src->proxy_id); + break; + case PROP_PROXY_PW: + g_value_set_string (value, src->proxy_pw); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -564,6 +629,20 @@ gst_soup_http_src_session_close (GstSoupHTTPSrc * src) } static void +gst_soup_http_src_authenticate_cb (SoupSession * session, SoupMessage * msg, + SoupAuth * auth, gboolean retrying, GstSoupHTTPSrc * src) +{ + if (!retrying) { + /* First time authentication only, if we fail and are called again with retry true fall through */ + if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { + soup_auth_authenticate (auth, src->user_id, src->user_pw); + } else if (msg->status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) { + soup_auth_authenticate (auth, src->proxy_id, src->proxy_pw); + } + } +} + +static void gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) { const char *value; @@ -1063,6 +1142,8 @@ gst_soup_http_src_start (GstBaseSrc * bsrc) return FALSE; } + g_signal_connect (src->session, "authenticate", + G_CALLBACK (gst_soup_http_src_authenticate_cb), src); return TRUE; } diff --git a/ext/soup/gstsouphttpsrc.h b/ext/soup/gstsouphttpsrc.h index 464da34..38d670b 100644 --- a/ext/soup/gstsouphttpsrc.h +++ b/ext/soup/gstsouphttpsrc.h @@ -52,6 +52,10 @@ struct _GstSoupHTTPSrc { gchar *user_agent; /* User-Agent HTTP header. */ gboolean automatic_redirect; /* Follow redirects. */ SoupURI *proxy; /* HTTP proxy URI. */ + gchar *user_id; /* Authentication user id for location URI. */ + gchar *user_pw; /* Authentication user password for location URI. */ + gchar *proxy_id; /* Authentication user id for proxy URI. */ + gchar *proxy_pw; /* Authentication user password for proxy URI. */ gchar **cookies; /* HTTP request cookies. */ GMainContext *context; /* I/O context. */ GMainLoop *loop; /* Event loop. */ diff --git a/tests/check/elements/souphttpsrc.c b/tests/check/elements/souphttpsrc.c index 408e05e..2281b72 100644 --- a/tests/check/elements/souphttpsrc.c +++ b/tests/check/elements/souphttpsrc.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include static int http_port = 0, https_port = 0; @@ -36,6 +39,17 @@ gboolean redirect = TRUE; static const char **cookies = NULL; +/* Variables for authentication tests */ +static const char *user_id = NULL; +static const char *user_pw = NULL; +static const char *good_user = "good_user"; +static const char *bad_user = "bad_user"; +static const char *good_pw = "good_pw"; +static const char *bad_pw = "bad_pw"; +static const char *realm = "SOUPHTTPSRC_REALM"; +static const char *basic_auth_path = "/basic_auth"; +static const char *digest_auth_path = "/digest_auth"; + static int run_server (int *http_port, int *https_port); @@ -48,6 +62,26 @@ handoff_cb (GstElement * fakesink, GstBuffer * buf, GstPad * pad, *p_outbuf = gst_buffer_ref (buf); } +static gboolean +basic_auth_cb (SoupAuthDomain * domain, SoupMessage * msg, + const char *username, const char *password, gpointer user_data) +{ + /* There is only one good login for testing */ + return (strcmp (username, good_user) == 0) + && (strcmp (password, good_pw) == 0); +} + + +static char * +digest_auth_cb (SoupAuthDomain * domain, SoupMessage * msg, + const char *username, gpointer user_data) +{ + /* There is only one good login for testing */ + if (strcmp (username, good_user) == 0) + return soup_auth_domain_digest_encode_password (good_user, realm, good_pw); + return NULL; +} + int run_test (const char *format, ...) { @@ -94,6 +128,11 @@ run_test (const char *format, ...) g_object_set (sink, "signal-handoffs", TRUE, NULL); g_signal_connect (sink, "preroll-handoff", G_CALLBACK (handoff_cb), &buf); + if (user_id != NULL) + g_object_set (src, "user-id", user_id, NULL); + if (user_pw != NULL) + g_object_set (src, "user-pw", user_pw, NULL); + ret = gst_element_set_state (pipe, GST_STATE_PAUSED); if (ret != GST_STATE_CHANGE_ASYNC) { GST_DEBUG ("failed to start up soup http src, ret = %d", ret); @@ -114,6 +153,8 @@ run_test (const char *format, ...) rc = 404; else if (g_str_has_suffix (err->message, "Forbidden")) rc = 403; + else if (g_str_has_suffix (err->message, "Unauthorized")) + rc = 401; else if (g_str_has_suffix (err->message, "Found")) rc = 302; GST_INFO ("debug: %s", debug); @@ -210,6 +251,90 @@ GST_START_TEST (test_cookies) GST_END_TEST; +GST_START_TEST (test_good_user_basic_auth) +{ + int res; + + user_id = good_user; + user_pw = good_pw; + res = run_test ("http://127.0.0.1:%d%s", http_port, basic_auth_path); + GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res); + user_id = user_pw = NULL; + fail_unless (res == 0); +} + +GST_END_TEST; + +GST_START_TEST (test_bad_user_basic_auth) +{ + int res; + + user_id = bad_user; + user_pw = good_pw; + res = run_test ("http://127.0.0.1:%d%s", http_port, basic_auth_path); + GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res); + user_id = user_pw = NULL; + fail_unless (res == 401); +} + +GST_END_TEST; + +GST_START_TEST (test_bad_password_basic_auth) +{ + int res; + + user_id = good_user; + user_pw = bad_pw; + res = run_test ("http://127.0.0.1:%d%s", http_port, basic_auth_path); + GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res); + user_id = user_pw = NULL; + fail_unless (res == 401); +} + +GST_END_TEST; + +GST_START_TEST (test_good_user_digest_auth) +{ + int res; + + user_id = good_user; + user_pw = good_pw; + res = run_test ("http://127.0.0.1:%d%s", http_port, digest_auth_path); + GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res); + user_id = user_pw = NULL; + fail_unless (res == 0); +} + +GST_END_TEST; + +GST_START_TEST (test_bad_user_digest_auth) +{ + int res; + + user_id = bad_user; + user_pw = good_pw; + res = run_test ("http://127.0.0.1:%d%s", http_port, digest_auth_path); + GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res); + user_id = user_pw = NULL; + fail_unless (res == 401); +} + +GST_END_TEST; + +GST_START_TEST (test_bad_password_digest_auth) +{ + int res; + + user_id = good_user; + user_pw = bad_pw; + res = run_test ("http://127.0.0.1:%d%s", http_port, digest_auth_path); + GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res); + user_id = user_pw = NULL; + fail_unless (res == 401); +} + +GST_END_TEST; + static gboolean icy_caps = FALSE; static void @@ -331,6 +456,12 @@ souphttpsrc_suite (void) tcase_add_test (tc_chain, test_not_found); tcase_add_test (tc_chain, test_forbidden); tcase_add_test (tc_chain, test_cookies); + tcase_add_test (tc_chain, test_good_user_basic_auth); + tcase_add_test (tc_chain, test_bad_user_basic_auth); + tcase_add_test (tc_chain, test_bad_password_basic_auth); + tcase_add_test (tc_chain, test_good_user_digest_auth); + tcase_add_test (tc_chain, test_bad_user_digest_auth); + tcase_add_test (tc_chain, test_bad_password_digest_auth); suite_add_tcase (s, tc_internet); tcase_set_timeout (tc_internet, 250); @@ -440,6 +571,8 @@ run_server (int *http_port, int *https_port) static int server_running = 0; + SoupAuthDomain *domain = NULL; + if (server_running) return 0; server_running = 1; @@ -454,6 +587,16 @@ run_server (int *http_port, int *https_port) *http_port = soup_server_get_port (server); GST_INFO ("HTTP server listening on port %d", *http_port); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); + domain = soup_auth_domain_basic_new (SOUP_AUTH_DOMAIN_REALM, realm, + SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_cb, + SOUP_AUTH_DOMAIN_ADD_PATH, basic_auth_path, NULL); + soup_server_add_auth_domain (server, domain); + g_object_unref (domain); + domain = soup_auth_domain_digest_new (SOUP_AUTH_DOMAIN_REALM, realm, + SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_cb, + SOUP_AUTH_DOMAIN_ADD_PATH, digest_auth_path, NULL); + soup_server_add_auth_domain (server, domain); + g_object_unref (domain); soup_server_run_async (server); if (ssl_cert_file && ssl_key_file) { -- 2.7.4