After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 561775 - souphttpsrc patch to support basic and digest authentication
souphttpsrc patch to support basic and digest authentication
Status: RESOLVED FIXED
Product: GStreamer
Classification: Platform
Component: gst-plugins-good
0.10.11
Other Linux
: Normal enhancement
: 0.10.14
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2008-11-21 07:42 UTC by rmcouat
Modified: 2009-01-23 08:41 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
souphttpsrc authentication patch (12.95 KB, patch)
2008-11-24 16:13 UTC, rmcouat
committed Details | Review

Description rmcouat 2008-11-21 07:42:18 UTC
The souphttpsrc element is preferred for connecting to HTTP MJPEG cameras but was missing authentication capabilities. This patch addresses that for both basic and digest authentication. The unit tests were also added to verify correct operation against the libsoup server built into the test program. Although unlikely to be used there are also properties and methods for authentication through a proxy server. I was not able to test that but the code paths through the element are identical except for a 407 instead of a 401 status.

Thanks to Wouter Cloetens for encouraging me to get this done.

Please let me know if you would like this in a different form or need some adjustment.

This patch is a context diff from cvs-head

diff -r -c cvs-head/ext/soup/gstsouphttpsrc.c work/ext/soup/gstsouphttpsrc.c
*** cvs-head/ext/soup/gstsouphttpsrc.c	2008-11-19 20:47:48.000000000 -0800
--- work/ext/soup/gstsouphttpsrc.c	2008-11-20 00:08:18.000000000 -0800
***************
*** 121,126 ****
--- 121,130 ----
    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,201 ****
      GstSoupHTTPSrc * src);
  static void gst_soup_http_src_finished_cb (SoupMessage * msg,
      GstSoupHTTPSrc * src);
! 
  
  static void
  _do_init (GType type)
--- 199,206 ----
      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,267 ****
--- 267,288 ----
        g_param_spec_string ("proxy", "Proxy",
            "HTTP proxy server URI", "", G_PARAM_READWRITE));
    g_object_class_install_property (gobject_class,
+       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,
***************
*** 319,324 ****
--- 340,349 ----
    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,363 ****
--- 383,396 ----
      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,440 ****
--- 468,493 ----
      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,492 ****
--- 540,557 ----
      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,569 ****
--- 629,649 ----
  }
  
  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;
***************
*** 1061,1066 ****
--- 1141,1148 ----
      return FALSE;
    }
  
+   g_signal_connect (src->session, "authenticate",
+       G_CALLBACK (gst_soup_http_src_authenticate_cb), src);
    return TRUE;
  }
  
diff -r -c cvs-head/ext/soup/gstsouphttpsrc.h work/ext/soup/gstsouphttpsrc.h
*** cvs-head/ext/soup/gstsouphttpsrc.h	2008-11-19 20:48:09.000000000 -0800
--- work/ext/soup/gstsouphttpsrc.h	2008-11-20 00:43:15.000000000 -0800
***************
*** 52,57 ****
--- 52,61 ----
    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 -r -c cvs-head/tests/check/elements/souphttpsrc.c work/tests/check/elements/souphttpsrc.c
*** cvs-head/tests/check/elements/souphttpsrc.c	2008-11-20 18:54:19.000000000 -0800
--- work/tests/check/elements/souphttpsrc.c	2008-11-20 22:51:08.000000000 -0800
***************
*** 28,33 ****
--- 28,36 ----
  #include <libsoup/soup-address.h>
  #include <libsoup/soup-message.h>
  #include <libsoup/soup-server.h>
+ #include <libsoup/soup-auth-domain.h>
+ #include <libsoup/soup-auth-domain-basic.h>
+ #include <libsoup/soup-auth-domain-digest.h>
  #include <gst/check/gstcheck.h>
  
  static int http_port = 0, https_port = 0;
***************
*** 36,41 ****
--- 39,55 ----
  
  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,53 ****
--- 62,87 ----
      *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,99 ****
--- 128,138 ----
    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,119 ****
--- 153,160 ----
        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,215 ****
--- 251,340 ----
  
  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,336 ****
--- 456,467 ----
    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,445 ****
--- 571,578 ----
  
    static int server_running = 0;
  
+   SoupAuthDomain *domain = NULL;
+ 
    if (server_running)
      return 0;
    server_running = 1;
***************
*** 454,459 ****
--- 587,606 ----
    *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) {
Comment 1 Sebastian Dröge (slomo) 2008-11-24 10:47:17 UTC
Thanks for the patch, looks good from a first look.

But could you please attach it as a file to this bug? :)
Comment 2 rmcouat 2008-11-24 16:13:45 UTC
Created attachment 123325 [details] [review]
souphttpsrc authentication patch

Oops missed the create attachment link, here it is.
Comment 3 Sebastian Dröge (slomo) 2008-11-27 12:18:39 UTC
The patch looks good but we need a real name from you for the changelog and release notes if possible :)
Comment 4 rmcouat 2008-11-27 16:40:25 UTC
My real name is Ron McOuat
from Vancouver BC Canada
Comment 5 Sebastian Dröge (slomo) 2008-11-27 16:44:54 UTC
2008-11-27  Sebastian Dröge  <sebastian.droege@collabora.co.uk>

        Patch by: Ron McOuat <rmcouat at smartt dot com>

        * 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.