+ * gst_rtsp_client_set_auth:
+ * @client: a #GstRTSPClient
+ * @auth: a #GstRTSPAuth
+ *
+ * configure @auth to be used as the authentication manager of @client.
+ */
+void
+gst_rtsp_client_set_auth (GstRTSPClient * client, GstRTSPAuth * auth)
+{
+ GstRTSPAuth *old;
+
+ g_return_if_fail (GST_IS_RTSP_CLIENT (client));
+
+ old = client->auth;
+
+ if (old != auth) {
+ if (auth)
+ g_object_ref (auth);
+ client->auth = auth;
+ if (old)
+ g_object_unref (old);
+ }
+}
+
+
+/**
+ * gst_rtsp_client_get_auth:
+ * @client: a #GstRTSPClient
+ *
+ * Get the #GstRTSPAuth used as the authentication manager of @client.
+ *
+ * Returns: the #GstRTSPAuth of @client. g_object_unref() after
+ * usage.
+ */
+GstRTSPAuth *
+gst_rtsp_client_get_auth (GstRTSPClient * client)
+{
+ GstRTSPAuth *result;
+
+ g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
+
+ if ((result = client->auth))
+ g_object_ref (result);
+
+ return result;
+}
+
+static GstRTSPResult
+message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
+ gpointer user_data)
+{
+ GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
+
+ switch (message->type) {
+ case GST_RTSP_MESSAGE_REQUEST:
+ handle_request (client, message);
+ break;
+ case GST_RTSP_MESSAGE_RESPONSE:
+ break;
+ case GST_RTSP_MESSAGE_DATA:
+ handle_data (client, message);
+ break;
+ default:
+ break;
+ }
+ return GST_RTSP_OK;
+}
+
+static GstRTSPResult
+message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
+{
+ /* GstRTSPClient *client; */
+
+ /* client = GST_RTSP_CLIENT (user_data); */
+
+ /* GST_INFO ("client %p: sent a message with cseq %d", client, cseq); */
+
+ return GST_RTSP_OK;
+}
+
+static GstRTSPResult
+closed (GstRTSPWatch * watch, gpointer user_data)
+{
+ GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
+ const gchar *tunnelid;
+
+ GST_INFO ("client %p: connection closed", client);
+
+ if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
+ g_mutex_lock (&tunnels_lock);
+ /* remove from tunnelids */
+ g_hash_table_remove (tunnels, tunnelid);
+ g_mutex_unlock (&tunnels_lock);
+ }
+
+ return GST_RTSP_OK;
+}
+
+static GstRTSPResult
+error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
+{
+ GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
+ gchar *str;
+
+ str = gst_rtsp_strresult (result);
+ GST_INFO ("client %p: received an error %s", client, str);
+ g_free (str);
+
+ return GST_RTSP_OK;
+}
+
+static GstRTSPResult
+error_full (GstRTSPWatch * watch, GstRTSPResult result,
+ GstRTSPMessage * message, guint id, gpointer user_data)
+{
+ GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
+ gchar *str;
+
+ str = gst_rtsp_strresult (result);
+ GST_INFO
+ ("client %p: received an error %s when handling message %p with id %d",
+ client, str, message, id);
+ g_free (str);
+
+ return GST_RTSP_OK;
+}
+
+static gboolean
+remember_tunnel (GstRTSPClient * client)
+{
+ const gchar *tunnelid;
+
+ /* store client in the pending tunnels */
+ tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
+ if (tunnelid == NULL)
+ goto no_tunnelid;
+
+ GST_INFO ("client %p: inserting tunnel session %s", client, tunnelid);
+
+ /* we can't have two clients connecting with the same tunnelid */
+ g_mutex_lock (&tunnels_lock);
+ if (g_hash_table_lookup (tunnels, tunnelid))
+ goto tunnel_existed;
+
+ g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
+ g_mutex_unlock (&tunnels_lock);
+
+ return TRUE;
+
+ /* ERRORS */
+no_tunnelid:
+ {
+ GST_ERROR ("client %p: no tunnelid provided", client);
+ return FALSE;
+ }
+tunnel_existed:
+ {
+ g_mutex_unlock (&tunnels_lock);
+ GST_ERROR ("client %p: tunnel session %s already existed", client,
+ tunnelid);
+ return FALSE;
+ }
+}
+
+static GstRTSPStatusCode
+tunnel_start (GstRTSPWatch * watch, gpointer user_data)
+{
+ GstRTSPClient *client;
+
+ client = GST_RTSP_CLIENT (user_data);
+
+ GST_INFO ("client %p: tunnel start (connection %p)", client,
+ client->connection);
+
+ if (!remember_tunnel (client))
+ goto tunnel_error;
+
+ return GST_RTSP_STS_OK;
+
+ /* ERRORS */
+tunnel_error:
+ {
+ GST_ERROR ("client %p: error starting tunnel", client);
+ return GST_RTSP_STS_SERVICE_UNAVAILABLE;
+ }
+}
+
+static GstRTSPResult
+tunnel_lost (GstRTSPWatch * watch, gpointer user_data)
+{
+ GstRTSPClient *client;
+
+ client = GST_RTSP_CLIENT (user_data);
+
+ GST_INFO ("client %p: tunnel lost (connection %p)", client,
+ client->connection);
+
+ /* ignore error, it'll only be a problem when the client does a POST again */
+ remember_tunnel (client);
+
+ return GST_RTSP_OK;
+}
+
+static GstRTSPResult
+tunnel_complete (GstRTSPWatch * watch, gpointer user_data)
+{
+ const gchar *tunnelid;
+ GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
+ GstRTSPClient *oclient;
+
+ GST_INFO ("client %p: tunnel complete", client);
+
+ /* find previous tunnel */
+ tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
+ if (tunnelid == NULL)
+ goto no_tunnelid;
+
+ g_mutex_lock (&tunnels_lock);
+ if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
+ goto no_tunnel;
+
+ /* remove the old client from the table. ref before because removing it will
+ * remove the ref to it. */
+ g_object_ref (oclient);
+ g_hash_table_remove (tunnels, tunnelid);
+
+ if (oclient->watch == NULL)
+ goto tunnel_closed;
+ g_mutex_unlock (&tunnels_lock);
+
+ GST_INFO ("client %p: found tunnel %p (old %p, new %p)", client, oclient,
+ oclient->connection, client->connection);
+
+ /* merge the tunnels into the first client */
+ gst_rtsp_connection_do_tunnel (oclient->connection, client->connection);
+ gst_rtsp_watch_reset (oclient->watch);
+ g_object_unref (oclient);
+
+ /* we don't need this watch anymore */
+ g_source_destroy ((GSource *) client->watch);
+ client->watchid = 0;
+ client->watch = NULL;
+
+ return GST_RTSP_OK;
+
+ /* ERRORS */
+no_tunnelid:
+ {
+ GST_INFO ("client %p: no tunnelid provided", client);
+ return GST_RTSP_ERROR;
+ }
+no_tunnel:
+ {
+ g_mutex_unlock (&tunnels_lock);
+ GST_INFO ("client %p: tunnel session %s not found", client, tunnelid);
+ return GST_RTSP_ERROR;
+ }
+tunnel_closed:
+ {
+ g_mutex_unlock (&tunnels_lock);
+ GST_INFO ("client %p: tunnel session %s was closed", client, tunnelid);
+ g_object_unref (oclient);
+ return GST_RTSP_ERROR;
+ }
+}
+
+static GstRTSPWatchFuncs watch_funcs = {
+ message_received,
+ message_sent,
+ closed,
+ error,
+ tunnel_start,
+ tunnel_complete,
+ error_full,
+ tunnel_lost
+};
+
+static void
+client_watch_notify (GstRTSPClient * client)
+{
+ GST_INFO ("client %p: watch destroyed", client);
+ client->watchid = 0;
+ client->watch = NULL;
+ g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_CLOSED], 0, NULL);
+ g_object_unref (client);
+}
+
+/**