Revert "rtsp-session-pool: Make sure session IDs are properly URI-escaped"
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-session-pool.c
index 5111eaa..772390f 100644 (file)
@@ -50,6 +50,7 @@ struct _GstRTSPSessionPoolPrivate
   GMutex lock;                  /* protects everything in this struct */
   guint max_sessions;
   GHashTable *sessions;
+  guint sessions_cookie;
 };
 
 #define DEFAULT_MAX_SESSIONS 0
@@ -69,6 +70,14 @@ static const gchar session_id_charset[] =
   '8', '9', '$', '-', '_', '.', '+'
 };
 
+enum
+{
+  SIGNAL_SESSION_REMOVED,
+  SIGNAL_LAST
+};
+
+static guint gst_rtsp_session_pool_signals[SIGNAL_LAST] = { 0 };
+
 GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
 #define GST_CAT_DEFAULT rtsp_session_debug
 
@@ -103,6 +112,12 @@ gst_rtsp_session_pool_class_init (GstRTSPSessionPoolClass * klass)
           0, G_MAXUINT, DEFAULT_MAX_SESSIONS,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED] =
+      g_signal_new ("session-removed", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPSessionPoolClass,
+          session_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
+      1, GST_TYPE_RTSP_SESSION);
+
   klass->create_session_id = create_session_id;
   klass->create_session = create_session;
 
@@ -123,14 +138,22 @@ gst_rtsp_session_pool_init (GstRTSPSessionPool * pool)
   priv->max_sessions = DEFAULT_MAX_SESSIONS;
 }
 
+static GstRTSPFilterResult
+remove_sessions_func (GstRTSPSessionPool * pool, GstRTSPSession * session,
+    gpointer user_data)
+{
+  return GST_RTSP_FILTER_REMOVE;
+}
+
 static void
 gst_rtsp_session_pool_finalize (GObject * object)
 {
   GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
   GstRTSPSessionPoolPrivate *priv = pool->priv;
 
-  g_mutex_clear (&priv->lock);
+  gst_rtsp_session_pool_filter (pool, remove_sessions_func, NULL);
   g_hash_table_unref (priv->sessions);
+  g_mutex_clear (&priv->lock);
 
   G_OBJECT_CLASS (gst_rtsp_session_pool_parent_class)->finalize (object);
 }
@@ -266,8 +289,8 @@ gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool * pool)
  * Find the session with @sessionid in @pool. The access time of the session
  * will be updated with gst_rtsp_session_touch().
  *
- * Returns: (transfer full): the #GstRTSPSession with @sessionid or %NULL when the session did
- * not exist. g_object_unref() after usage.
+ * Returns: (transfer full) (nullable): the #GstRTSPSession with @sessionid
+ * or %NULL when the session did not exist. g_object_unref() after usage.
  */
 GstRTSPSession *
 gst_rtsp_session_pool_find (GstRTSPSessionPool * pool, const gchar * sessionid)
@@ -294,7 +317,7 @@ gst_rtsp_session_pool_find (GstRTSPSessionPool * pool, const gchar * sessionid)
 static gchar *
 create_session_id (GstRTSPSessionPool * pool)
 {
-  gchar id[17];
+  gchar id[16];
   gint i;
 
   for (i = 0; i < 16; i++) {
@@ -302,9 +325,8 @@ create_session_id (GstRTSPSessionPool * pool)
         session_id_charset[g_random_int_range (0,
             G_N_ELEMENTS (session_id_charset))];
   }
-  id[16] = 0;
 
-  return g_uri_escape_string (id, NULL, FALSE);
+  return g_strndup (id, 16);
 }
 
 static GstRTSPSession *
@@ -372,6 +394,7 @@ gst_rtsp_session_pool_create (GstRTSPSessionPool * pool)
       g_object_ref (result);
       g_hash_table_insert (priv->sessions,
           (gchar *) gst_rtsp_session_get_sessionid (result), result);
+      priv->sessions_cookie++;
     }
     g_mutex_unlock (&priv->lock);
 
@@ -428,18 +451,41 @@ gst_rtsp_session_pool_remove (GstRTSPSessionPool * pool, GstRTSPSession * sess)
   priv = pool->priv;
 
   g_mutex_lock (&priv->lock);
+  g_object_ref (sess);
   found =
       g_hash_table_remove (priv->sessions,
       gst_rtsp_session_get_sessionid (sess));
+  if (found)
+    priv->sessions_cookie++;
   g_mutex_unlock (&priv->lock);
 
+  if (found)
+    g_signal_emit (pool, gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED],
+        0, sess);
+
+  g_object_unref (sess);
+
   return found;
 }
 
+typedef struct
+{
+  GTimeVal now;
+  GstRTSPSessionPool *pool;
+  GList *removed;
+} CleanupData;
+
 static gboolean
-cleanup_func (gchar * sessionid, GstRTSPSession * sess, GTimeVal * now)
+cleanup_func (gchar * sessionid, GstRTSPSession * sess, CleanupData * data)
 {
-  return gst_rtsp_session_is_expired (sess, now);
+  gboolean expired;
+
+  expired = gst_rtsp_session_is_expired (sess, &data->now);
+  if (expired) {
+    GST_DEBUG ("session expired");
+    data->removed = g_list_prepend (data->removed, g_object_ref (sess));
+  }
+  return expired;
 }
 
 /**
@@ -456,52 +502,36 @@ gst_rtsp_session_pool_cleanup (GstRTSPSessionPool * pool)
 {
   GstRTSPSessionPoolPrivate *priv;
   guint result;
-  GTimeVal now;
+  CleanupData data;
+  GList *walk;
 
   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
 
   priv = pool->priv;
 
-  g_get_current_time (&now);
+  g_get_current_time (&data.now);
+  data.pool = pool;
+  data.removed = NULL;
 
   g_mutex_lock (&priv->lock);
   result =
       g_hash_table_foreach_remove (priv->sessions, (GHRFunc) cleanup_func,
-      &now);
+      &data);
+  if (result > 0)
+    priv->sessions_cookie++;
   g_mutex_unlock (&priv->lock);
 
-  return result;
-}
+  for (walk = data.removed; walk; walk = walk->next) {
+    GstRTSPSession *sess = walk->data;
 
-typedef struct
-{
-  GstRTSPSessionPool *pool;
-  GstRTSPSessionPoolFilterFunc func;
-  gpointer user_data;
-  GList *list;
-} FilterData;
+    g_signal_emit (pool,
+        gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED], 0, sess);
 
-static gboolean
-filter_func (gchar * sessionid, GstRTSPSession * sess, FilterData * data)
-{
-  GstRTSPFilterResult res;
-
-  if (data->func)
-    res = data->func (data->pool, sess, data->user_data);
-  else
-    res = GST_RTSP_FILTER_REF;
-
-  switch (res) {
-    case GST_RTSP_FILTER_REMOVE:
-      return TRUE;
-    case GST_RTSP_FILTER_REF:
-      /* keep ref */
-      data->list = g_list_prepend (data->list, g_object_ref (sess));
-      /* fallthrough */
-    default:
-    case GST_RTSP_FILTER_KEEP:
-      return FALSE;
+    g_object_unref (sess);
   }
+  g_list_free (data.removed);
+
+  return result;
 }
 
 /**
@@ -514,7 +544,8 @@ filter_func (gchar * sessionid, GstRTSPSession * sess, FilterData * data)
  * what happens to the session. @func will be called with the session pool
  * locked so no further actions on @pool can be performed from @func.
  *
- * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be removed from
+ * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be set to the
+ * expired state with gst_rtsp_session_set_expired() and removed from
  * @pool.
  *
  * If @func returns #GST_RTSP_FILTER_KEEP, the session will remain in @pool.
@@ -534,22 +565,89 @@ gst_rtsp_session_pool_filter (GstRTSPSessionPool * pool,
     GstRTSPSessionPoolFilterFunc func, gpointer user_data)
 {
   GstRTSPSessionPoolPrivate *priv;
-  FilterData data;
+  GHashTableIter iter;
+  gpointer key, value;
+  GList *result;
+  GHashTable *visited;
+  guint cookie;
 
   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
 
   priv = pool->priv;
 
-  data.pool = pool;
-  data.func = func;
-  data.user_data = user_data;
-  data.list = NULL;
+  result = NULL;
+  if (func)
+    visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
 
   g_mutex_lock (&priv->lock);
-  g_hash_table_foreach_remove (priv->sessions, (GHRFunc) filter_func, &data);
+restart:
+  g_hash_table_iter_init (&iter, priv->sessions);
+  cookie = priv->sessions_cookie;
+  while (g_hash_table_iter_next (&iter, &key, &value)) {
+    GstRTSPSession *session = value;
+    GstRTSPFilterResult res;
+    gboolean changed;
+
+    if (func) {
+      /* only visit each session once */
+      if (g_hash_table_contains (visited, session))
+        continue;
+
+      g_hash_table_add (visited, g_object_ref (session));
+      g_mutex_unlock (&priv->lock);
+
+      res = func (pool, session, user_data);
+
+      g_mutex_lock (&priv->lock);
+    } else
+      res = GST_RTSP_FILTER_REF;
+
+    changed = (cookie != priv->sessions_cookie);
+
+    switch (res) {
+      case GST_RTSP_FILTER_REMOVE:
+      {
+        gboolean removed = TRUE;
+
+        if (changed)
+          /* something changed, check if we still have the session */
+          removed = g_hash_table_remove (priv->sessions, key);
+        else
+          g_hash_table_iter_remove (&iter);
+
+        if (removed) {
+          /* if we managed to remove the session, update the cookie and
+           * signal */
+          cookie = ++priv->sessions_cookie;
+          g_mutex_unlock (&priv->lock);
+
+          g_signal_emit (pool,
+              gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED], 0,
+              session);
+
+          g_mutex_lock (&priv->lock);
+          /* cookie could have changed again, make sure we restart */
+          changed |= (cookie != priv->sessions_cookie);
+        }
+        break;
+      }
+      case GST_RTSP_FILTER_REF:
+        /* keep ref */
+        result = g_list_prepend (result, g_object_ref (session));
+        break;
+      case GST_RTSP_FILTER_KEEP:
+      default:
+        break;
+    }
+    if (changed)
+      goto restart;
+  }
   g_mutex_unlock (&priv->lock);
 
-  return data.list;
+  if (func)
+    g_hash_table_unref (visited);
+
+  return result;
 }
 
 typedef struct