[telepathy-gabble/master] Add some hysteresis to the Google Talk roster workaround.

Will Thompson will.thompson at collabora.co.uk
Mon Jul 20 10:45:16 PDT 2009


We want to respect subscription requests being rescinded, but we need to
wait and see if they're actually being rescinded or whether Google's
lying to us. So if we receive a rescind notification just after an
ask=subscribe notification, we wait a second to see if Google's lying,
and only then actually remove them.
---
 src/roster.c |  148 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 141 insertions(+), 7 deletions(-)

diff --git a/src/roster.c b/src/roster.c
index 75fa032..a89a86f 100644
--- a/src/roster.c
+++ b/src/roster.c
@@ -116,6 +116,17 @@ struct _GabbleRosterItem
    * already sent is acknowledged - this prevents some race conditions
    */
   GabbleRosterItemEdit *unsent_edits;
+
+  /* If non-zero, the GSource id for a call to flicker_prevention_timeout. */
+  guint flicker_prevention_id;
+};
+
+typedef struct _FlickerPreventionCtx FlickerPreventionCtx;
+struct _FlickerPreventionCtx
+{
+  GabbleRoster *roster;
+  TpHandle handle;
+  GabbleRosterItem *item;
 };
 
 static void channel_manager_iface_init (gpointer, gpointer);
@@ -129,6 +140,7 @@ static void gabble_roster_set_property (GObject *object, guint property_id,
 static void gabble_roster_get_property (GObject *object, guint property_id,
     GValue *value, GParamSpec *pspec);
 
+static void roster_item_cancel_flicker_timeout (GabbleRosterItem *item);
 static void _gabble_roster_item_free (GabbleRosterItem *item);
 static void item_edit_free (GabbleRosterItemEdit *edits);
 static void gabble_roster_close_all (GabbleRoster *roster);
@@ -289,6 +301,9 @@ _gabble_roster_item_free (GabbleRosterItem *item)
   item_edit_free (item->unsent_edits);
   g_free (item->name);
   g_free (item->alias_for);
+
+  roster_item_cancel_flicker_timeout (item);
+
   g_slice_free (GabbleRosterItem, item);
 }
 
@@ -1093,6 +1108,116 @@ _update_group (gpointer key,
   return TRUE;
 }
 
+static FlickerPreventionCtx *
+flicker_prevention_ctx_new (GabbleRoster *roster,
+    TpHandle handle,
+    GabbleRosterItem *item)
+{
+  FlickerPreventionCtx *ret = g_slice_new (FlickerPreventionCtx);
+
+  /* Not taking a ref to the roster. The context is owned by the Item (well, by
+   * its timeout) which is owned by the roster.
+   */
+  ret->roster = roster;
+  /* Not taking a ref to the handle; we borrow the roster's ref, which is
+   * released after the GabbleRosterItem is freed, at which point this context
+   * will be destroyed.
+   */
+  ret->handle = handle;
+  ret->item = item;
+
+  return ret;
+}
+
+static void
+flicker_prevention_ctx_free (gpointer ctx_)
+{
+  FlickerPreventionCtx *ctx = ctx_;
+
+  ctx->roster = NULL;
+  ctx->handle = 0;
+  ctx->item = NULL;
+
+  g_slice_free (FlickerPreventionCtx, ctx);
+}
+
+/* As described in roster/test-google-roster.py, we work around a Google Talk
+ * server bug to avoid contacts flickering off and onto
+ * subscribe:remote-pending when you try to subscribe to someone's presence.
+ *
+ * When we see a roster item with subscription=none/from and ask=subscribe:
+ *  * if no call to this function is scheduled, we schedule a call
+ *  * if one is already scheduled, we cancel it.
+ *
+ * When we see a roster item with subscription=none/from and no ask=subscribe:
+ *  * if a call to this timeout is scheduled, do nothing, in case the contact
+ *    flickers back to ask=subscribe before this fires;
+ *  * if a call to this timeout is not scheduled, remove the contact from the
+ *    subscribe list.
+ *
+ * This way, our subscription being cancelled or our subscription requests
+ * being rescinded will show up on the subscribe list, albeit with a slight lag
+ * in certain situations in case we're just seeing the Google talk server bug.
+ */
+static gboolean
+flicker_prevention_timeout (gpointer ctx_)
+{
+  FlickerPreventionCtx *ctx = ctx_;
+  GabbleRosterItem *item = ctx->item;
+
+  DEBUG ("called for %u", ctx->handle);
+
+  if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_NONE
+      && !item->ask_subscribe)
+    {
+      GabbleRosterChannel *sub_chan = _gabble_roster_get_channel (ctx->roster,
+          TP_HANDLE_TYPE_LIST, GABBLE_LIST_HANDLE_SUBSCRIBE, NULL, NULL);
+      TpIntSet *rem = tp_intset_new_containing (ctx->handle);
+
+      DEBUG ("removing %u from subscribe", ctx->handle);
+      tp_group_mixin_change_members ((GObject *) sub_chan, "", NULL, rem, NULL,
+          NULL, 0, 0);
+
+      tp_intset_destroy (rem);
+    }
+  else
+    {
+      DEBUG ("subscription=%s and ask_subscribe=%s, nothing to do",
+          _subscription_to_string (item->subscription),
+          item->ask_subscribe ? "true" : "false");
+    }
+
+  ctx->item->flicker_prevention_id = 0;
+
+  return FALSE;
+}
+
+static void
+roster_item_ensure_flicker_timeout (GabbleRoster *roster,
+    TpHandle handle,
+    GabbleRosterItem *item)
+{
+  if (item->flicker_prevention_id == 0)
+    {
+      FlickerPreventionCtx *ctx = flicker_prevention_ctx_new (roster, handle,
+          item);
+
+      item->flicker_prevention_id = g_timeout_add_seconds_full (
+          G_PRIORITY_DEFAULT, 1, flicker_prevention_timeout, ctx,
+          flicker_prevention_ctx_free);
+    }
+}
+
+static void
+roster_item_cancel_flicker_timeout (GabbleRosterItem *item)
+{
+  if (item->flicker_prevention_id != 0)
+    {
+      g_source_remove (item->flicker_prevention_id);
+      item->flicker_prevention_id = 0;
+    }
+}
+
 /**
  * gabble_roster_iq_cb
  *
@@ -1286,6 +1411,9 @@ got_roster_iq (GabbleRoster *roster,
                 tp_intset_add (sub_rem, handle);
               else
                 tp_intset_add (sub_add, handle);
+
+              roster_item_cancel_flicker_timeout (item);
+
               break;
             case GABBLE_ROSTER_SUBSCRIPTION_NONE:
             case GABBLE_ROSTER_SUBSCRIPTION_FROM:
@@ -1298,18 +1426,24 @@ got_roster_iq (GabbleRoster *roster,
                     }
                   else
                     {
+                      if (item->flicker_prevention_id == 0)
+                        roster_item_ensure_flicker_timeout (roster, handle, item);
+                      else
+                        roster_item_cancel_flicker_timeout (item);
+
                       tp_intset_add (sub_rp, handle);
                     }
                 }
+              else if (item->flicker_prevention_id == 0)
+                {
+                  /* We're not expecting this contact's ask=subscribe to
+                   * flicker off and on again, so let's remove them immediately.
+                   */
+                  tp_intset_add (sub_rem, handle);
+                }
               else
                 {
-                  /* don't remove remote pending, presence="unsubscribed"
-                   * does that */
-                  if (!tp_handle_set_is_member (sub_chan->group.remote_pending,
-                        handle))
-                    {
-                      tp_intset_add (sub_rem, handle);
-                    }
+                  DEBUG ("delaying removal of %s from pending", jid);
                 }
               break;
             case GABBLE_ROSTER_SUBSCRIPTION_REMOVE:
-- 
1.5.6.5




More information about the telepathy-commits mailing list