telepathygabble/src/roster.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * roster.c - Source for Gabble roster helper
       
     3  *
       
     4  * Copyright (C) 2006 Collabora Ltd.
       
     5  * 
       
     6  *
       
     7  * This library is free software; you can redistribute it and/or
       
     8  * modify it under the terms of the GNU Lesser General Public
       
     9  * License as published by the Free Software Foundation; either
       
    10  * version 2.1 of the License, or (at your option) any later version.
       
    11  *
       
    12  * This library is distributed in the hope that it will be useful,
       
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    15  * Lesser General Public License for more details.
       
    16  *
       
    17  * You should have received a copy of the GNU Lesser General Public
       
    18  * License along with this library; if not, write to the Free Software
       
    19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    20  */
       
    21 
       
    22 
       
    23 #include <dbus/dbus-glib.h>
       
    24 #include <string.h>
       
    25 
       
    26 #include "telepathy-interfaces.h"
       
    27 #include "tp-channel-factory-iface.h"
       
    28 
       
    29 #include "debug.h"
       
    30 #include "gabble-connection.h"
       
    31 #include "gabble-roster-channel.h"
       
    32 #include "namespaces.h"
       
    33 #include "roster.h"
       
    34 #include "util.h"
       
    35 
       
    36 #include "gabble_enums.h"
       
    37 
       
    38 #define DBUS_API_SUBJECT_TO_CHANGE
       
    39 #define DEBUG_FLAG GABBLE_DEBUG_ROSTER
       
    40 #define GOOGLE_ROSTER_VERSION "2"
       
    41 
       
    42 #ifdef DEBUG_FLAG
       
    43 //#define DEBUG(format, ...)
       
    44 #define DEBUGGING 0
       
    45 #define NODE_DEBUG(n, s)
       
    46 #endif /* DEBUG_FLAG */
       
    47 
       
    48 /* Properties */
       
    49 enum
       
    50 {
       
    51   PROP_CONNECTION = 1,
       
    52   LAST_PROPERTY
       
    53 };
       
    54 
       
    55 /* signal enum */
       
    56 enum
       
    57 {
       
    58   NICKNAME_UPDATE,
       
    59   LAST_SIGNAL 
       
    60 #ifdef EMULATOR  
       
    61   = LAST_SIGNAL_ROSTER
       
    62 #endif
       
    63   
       
    64 };
       
    65 
       
    66 
       
    67 #ifdef EMULATOR
       
    68 #include "libgabble_wsd_solution.h"
       
    69 
       
    70 	GET_STATIC_ARRAY_FROM_TLS(signals,gabble_roster,guint)
       
    71 	#define signals (GET_WSD_VAR_NAME(signals,gabble_roster, s)())
       
    72 	
       
    73 #else
       
    74 
       
    75 	static guint signals[LAST_SIGNAL] = {0};
       
    76 
       
    77 #endif
       
    78 
       
    79 
       
    80 typedef struct _GabbleRosterPrivate GabbleRosterPrivate;
       
    81 struct _GabbleRosterPrivate
       
    82 {
       
    83   GabbleConnection *conn;
       
    84 
       
    85   LmMessageHandler *iq_cb;
       
    86   LmMessageHandler *presence_cb;
       
    87 
       
    88   GHashTable *channels;
       
    89   GHashTable *items;
       
    90 
       
    91   gboolean roster_received;
       
    92   gboolean dispose_has_run;
       
    93 };
       
    94 
       
    95 typedef enum
       
    96 {
       
    97   GOOGLE_ITEM_TYPE_NORMAL = 0,
       
    98   GOOGLE_ITEM_TYPE_BLOCKED,
       
    99   GOOGLE_ITEM_TYPE_HIDDEN,
       
   100   GOOGLE_ITEM_TYPE_PINNED
       
   101 } GoogleItemType;
       
   102 
       
   103 typedef struct _GabbleRosterItem GabbleRosterItem;
       
   104 struct _GabbleRosterItem
       
   105 {
       
   106   GabbleRosterSubscription subscription;
       
   107   gboolean ask_subscribe;
       
   108   GoogleItemType google_type;
       
   109   gchar *name;
       
   110   gchar **groups;
       
   111 };
       
   112 
       
   113 static void gabble_roster_factory_iface_init ();
       
   114 static void gabble_roster_init (GabbleRoster *roster);
       
   115 static GObject * gabble_roster_constructor (GType type, guint n_props, GObjectConstructParam *props);
       
   116 static void gabble_roster_dispose (GObject *object);
       
   117 static void gabble_roster_finalize (GObject *object);
       
   118 static void gabble_roster_set_property (GObject *object, guint property_id,
       
   119     const GValue *value, GParamSpec *pspec);
       
   120 static void gabble_roster_get_property (GObject *object, guint property_id,
       
   121     GValue *value, GParamSpec *pspec);
       
   122 
       
   123 static void _gabble_roster_item_free (GabbleRosterItem *item);
       
   124 
       
   125 #ifndef EMULATOR
       
   126 G_DEFINE_TYPE_WITH_CODE (GabbleRoster, gabble_roster, G_TYPE_OBJECT,
       
   127     G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE, gabble_roster_factory_iface_init));
       
   128 #else
       
   129 	GET_STATIC_VAR_FROM_TLS(gabble_roster_parent_class,gabble_roster,gpointer)
       
   130 	#define gabble_roster_parent_class (*GET_WSD_VAR_NAME(gabble_roster_parent_class,gabble_roster,s)())
       
   131 	
       
   132 	GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_roster,GType)
       
   133 	#define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_roster,s)())
       
   134 	
       
   135 	
       
   136 	static void gabble_roster_init (GabbleRoster *self); 
       
   137 	static void gabble_roster_class_init (GabbleRosterClass *klass); 
       
   138 	static void gabble_roster_class_intern_init (gpointer klass) { gabble_roster_parent_class = g_type_class_peek_parent (klass); gabble_roster_class_init ((GabbleRosterClass*) klass); } EXPORT_C GType gabble_roster_get_type (void) { if ((g_define_type_id == 0)) { static const GTypeInfo g_define_type_info = { sizeof (GabbleRosterClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_roster_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleRoster), 0, (GInstanceInitFunc) gabble_roster_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleRoster"), &g_define_type_info, (GTypeFlags) 0); { { static const GInterfaceInfo g_implement_interface_info = { (GInterfaceInitFunc) gabble_roster_factory_iface_init }; g_type_add_interface_static (g_define_type_id, tp_channel_factory_iface_get_type(), &g_implement_interface_info); } ; } } return g_define_type_id; }    ;
       
   139 		
       
   140 #endif
       
   141 
       
   142 #define GABBLE_ROSTER_GET_PRIVATE(o)     ((GabbleRosterPrivate*) ((o)->priv));
       
   143 
       
   144 static void
       
   145 gabble_roster_class_init (GabbleRosterClass *gabble_roster_class)
       
   146 {
       
   147   GObjectClass *object_class = G_OBJECT_CLASS (gabble_roster_class);
       
   148   GParamSpec *param_spec;
       
   149 
       
   150   g_type_class_add_private (gabble_roster_class, sizeof (GabbleRosterPrivate));
       
   151 
       
   152   object_class->constructor = gabble_roster_constructor;
       
   153 
       
   154   object_class->dispose = gabble_roster_dispose;
       
   155   object_class->finalize = gabble_roster_finalize;
       
   156 
       
   157   object_class->get_property = gabble_roster_get_property;
       
   158   object_class->set_property = gabble_roster_set_property;
       
   159 
       
   160   param_spec = g_param_spec_object ("connection", "GabbleConnection object",
       
   161                                     "Gabble connection object that owns this "
       
   162                                     "XMPP roster object.",
       
   163                                     GABBLE_TYPE_CONNECTION,
       
   164                                     G_PARAM_CONSTRUCT_ONLY |
       
   165                                     G_PARAM_READWRITE |
       
   166                                     G_PARAM_STATIC_NICK |
       
   167                                     G_PARAM_STATIC_BLURB);
       
   168   g_object_class_install_property (object_class,
       
   169                                    PROP_CONNECTION,
       
   170                                    param_spec);
       
   171 
       
   172   signals[NICKNAME_UPDATE] = g_signal_new (
       
   173     "nickname-update",
       
   174     G_TYPE_FROM_CLASS (gabble_roster_class),
       
   175     G_SIGNAL_RUN_LAST,
       
   176     0,
       
   177     NULL, NULL,
       
   178     g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
       
   179 }
       
   180 
       
   181 static void
       
   182 gabble_roster_init (GabbleRoster *obj)
       
   183 {
       
   184   GabbleRosterPrivate *priv =
       
   185      G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_ROSTER, GabbleRosterPrivate);
       
   186 
       
   187   obj->priv = priv;
       
   188 
       
   189   priv->channels = g_hash_table_new_full (g_direct_hash,
       
   190                                           g_direct_equal,
       
   191                                           NULL,
       
   192                                           g_object_unref);
       
   193 
       
   194   priv->items = g_hash_table_new_full (g_direct_hash,
       
   195                                        g_direct_equal,
       
   196                                        NULL,
       
   197                                        (GDestroyNotify) _gabble_roster_item_free);
       
   198 }
       
   199 
       
   200 static GObject *
       
   201 gabble_roster_constructor (GType type, guint n_props,
       
   202                            GObjectConstructParam *props)
       
   203 {
       
   204   GObject *obj;
       
   205   /* GabbleRosterPrivate *priv; */
       
   206 
       
   207   obj = G_OBJECT_CLASS (gabble_roster_parent_class)->
       
   208            constructor (type, n_props, props);
       
   209   /* priv = GABBLE_ROSTER_GET_PRIVATE (GABBLE_ROSTER (obj)); */
       
   210 
       
   211   return obj;
       
   212 }
       
   213 
       
   214 void
       
   215 gabble_roster_dispose (GObject *object)
       
   216 {
       
   217   GabbleRoster *self = GABBLE_ROSTER (object);
       
   218   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (self);
       
   219 
       
   220   if (priv->dispose_has_run)
       
   221     return;
       
   222 
       
   223   gabble_debug (DEBUG_FLAG, "dispose called");
       
   224 
       
   225   priv->dispose_has_run = TRUE;
       
   226 
       
   227   g_assert (priv->iq_cb == NULL);
       
   228   g_assert (priv->presence_cb == NULL);
       
   229 
       
   230   tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object));
       
   231   g_assert (priv->channels == NULL);
       
   232 
       
   233   if (G_OBJECT_CLASS (gabble_roster_parent_class)->dispose)
       
   234     G_OBJECT_CLASS (gabble_roster_parent_class)->dispose (object);
       
   235 }
       
   236 
       
   237 static void
       
   238 item_handle_unref_foreach (gpointer key, gpointer data, gpointer user_data)
       
   239 {
       
   240   GabbleHandle handle = (GabbleHandle) key;
       
   241   GabbleRosterPrivate *priv = (GabbleRosterPrivate *) user_data;
       
   242 
       
   243   gabble_handle_unref (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, handle);
       
   244 }
       
   245 
       
   246 void
       
   247 gabble_roster_finalize (GObject *object)
       
   248 {
       
   249   GabbleRoster *self = GABBLE_ROSTER (object);
       
   250   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (self);
       
   251 
       
   252   gabble_debug (DEBUG_FLAG, "called with %p", object);
       
   253 
       
   254   g_hash_table_foreach (priv->items, item_handle_unref_foreach, priv);
       
   255   g_hash_table_destroy (priv->items);
       
   256 
       
   257   G_OBJECT_CLASS (gabble_roster_parent_class)->finalize (object);
       
   258 }
       
   259 
       
   260 static void
       
   261 gabble_roster_get_property (GObject    *object,
       
   262                             guint       property_id,
       
   263                             GValue     *value,
       
   264                             GParamSpec *pspec)
       
   265 {
       
   266   GabbleRoster *roster = GABBLE_ROSTER (object);
       
   267   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   268 
       
   269   switch (property_id) {
       
   270     case PROP_CONNECTION:
       
   271       g_value_set_object (value, priv->conn);
       
   272       break;
       
   273     default:
       
   274       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   275       break;
       
   276   }
       
   277 }
       
   278 
       
   279 static void
       
   280 gabble_roster_set_property (GObject     *object,
       
   281                             guint        property_id,
       
   282                             const GValue *value,
       
   283                             GParamSpec   *pspec)
       
   284 {
       
   285   GabbleRoster *roster = GABBLE_ROSTER (object);
       
   286   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   287 
       
   288   switch (property_id) {
       
   289     case PROP_CONNECTION:
       
   290       priv->conn = g_value_get_object (value);
       
   291       break;
       
   292     default:
       
   293       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   294       break;
       
   295   }
       
   296 }
       
   297 
       
   298 static void
       
   299 _gabble_roster_item_free (GabbleRosterItem *item)
       
   300 {
       
   301   g_assert (item != NULL);
       
   302 
       
   303   g_strfreev (item->groups);
       
   304   g_free (item->name);
       
   305   g_free (item);
       
   306 }
       
   307 
       
   308 static const gchar *
       
   309 _subscription_to_string (GabbleRosterSubscription subscription)
       
   310 {
       
   311   switch (subscription)
       
   312     {
       
   313       case GABBLE_ROSTER_SUBSCRIPTION_NONE:
       
   314         return "none";
       
   315       case GABBLE_ROSTER_SUBSCRIPTION_FROM:
       
   316         return "from";
       
   317       case GABBLE_ROSTER_SUBSCRIPTION_TO:
       
   318         return "to";
       
   319       case GABBLE_ROSTER_SUBSCRIPTION_BOTH:
       
   320         return "both";
       
   321       case GABBLE_ROSTER_SUBSCRIPTION_REMOVE:
       
   322         return "remove";
       
   323       default:
       
   324         g_assert_not_reached ();
       
   325         return NULL;
       
   326     }
       
   327 }
       
   328 
       
   329 static GabbleRosterSubscription
       
   330 _parse_item_subscription (LmMessageNode *item_node)
       
   331 {
       
   332   const gchar *subscription;
       
   333 
       
   334   g_assert (item_node != NULL);
       
   335 
       
   336   subscription = lm_message_node_get_attribute (item_node, "subscription");
       
   337 
       
   338   if (NULL == subscription || 0 == strcmp (subscription, "none"))
       
   339     return GABBLE_ROSTER_SUBSCRIPTION_NONE;
       
   340   else if (0 == strcmp (subscription, "from"))
       
   341     return GABBLE_ROSTER_SUBSCRIPTION_FROM;
       
   342   else if (0 == strcmp (subscription, "to"))
       
   343     return GABBLE_ROSTER_SUBSCRIPTION_TO;
       
   344   else if (0 == strcmp (subscription, "both"))
       
   345     return GABBLE_ROSTER_SUBSCRIPTION_BOTH;
       
   346   else if (0 == strcmp (subscription, "remove"))
       
   347     return GABBLE_ROSTER_SUBSCRIPTION_REMOVE;
       
   348   else
       
   349     {
       
   350        NODE_DEBUG (item_node, "got unexpected subscription value");
       
   351       return GABBLE_ROSTER_SUBSCRIPTION_NONE;
       
   352     }
       
   353 }
       
   354 
       
   355 static gchar **
       
   356 _parse_item_groups (LmMessageNode *item_node)
       
   357 {
       
   358   LmMessageNode *group_node;
       
   359   GPtrArray *strv;
       
   360 
       
   361   strv = g_ptr_array_new ();
       
   362 
       
   363   for (group_node = item_node->children;
       
   364       NULL != group_node;
       
   365       group_node = group_node->next)
       
   366     {
       
   367       if (0 != strcmp (group_node->name, "group"))
       
   368         continue;
       
   369 
       
   370       if (NULL == group_node->value)
       
   371         continue;
       
   372 
       
   373       g_ptr_array_add (strv, g_strdup (group_node->value));
       
   374     }
       
   375 
       
   376   g_ptr_array_add (strv, NULL);
       
   377 
       
   378   return (gchar **) g_ptr_array_free (strv, FALSE);
       
   379 }
       
   380 
       
   381 static const gchar *
       
   382 _google_item_type_to_string (GoogleItemType google_type)
       
   383 {
       
   384   switch (google_type)
       
   385     {
       
   386       case GOOGLE_ITEM_TYPE_NORMAL:
       
   387         return NULL;
       
   388       case GOOGLE_ITEM_TYPE_BLOCKED:
       
   389         return "B";
       
   390       case GOOGLE_ITEM_TYPE_HIDDEN:
       
   391         return "H";
       
   392       case GOOGLE_ITEM_TYPE_PINNED:
       
   393         return "P";
       
   394     }
       
   395 
       
   396   g_assert_not_reached ();
       
   397 
       
   398   return NULL;
       
   399 }
       
   400 
       
   401 static GoogleItemType
       
   402 _parse_google_item_type (LmMessageNode *item_node)
       
   403 {
       
   404   const gchar *google_type;
       
   405 
       
   406   g_assert (item_node != NULL);
       
   407 
       
   408   google_type = lm_message_node_get_attribute (item_node, "gr:t");
       
   409 
       
   410   if (NULL == google_type)
       
   411     return GOOGLE_ITEM_TYPE_NORMAL;
       
   412   else if (!g_strdiff (google_type, "B"))
       
   413     return GOOGLE_ITEM_TYPE_BLOCKED;
       
   414   else if (!g_strdiff (google_type, "H"))
       
   415     return GOOGLE_ITEM_TYPE_HIDDEN;
       
   416   else if (!g_strdiff (google_type, "P"))
       
   417     return GOOGLE_ITEM_TYPE_PINNED;
       
   418 
       
   419   NODE_DEBUG (item_node, "got unexpected google contact type value");
       
   420 
       
   421   return GOOGLE_ITEM_TYPE_NORMAL;
       
   422 }
       
   423 
       
   424 static gboolean
       
   425 _google_roster_item_should_keep (LmMessageNode *item_node,
       
   426                                  GabbleRosterItem *item)
       
   427 {
       
   428   const gchar *attr;
       
   429 
       
   430   /* skip automatically subscribed Google roster items */
       
   431   attr = lm_message_node_get_attribute (item_node, "gr:autosub");
       
   432 
       
   433   if (!g_strdiff (attr, "true"))
       
   434     return FALSE;
       
   435 
       
   436   /* skip email addresses that replied to an invite */
       
   437   attr = lm_message_node_get_attribute (item_node, "gr:alias-for");
       
   438 
       
   439   if (attr != NULL)
       
   440     return FALSE;
       
   441 
       
   442   /* allow items that we've requested a subscription from */
       
   443   if (item->ask_subscribe)
       
   444     return TRUE;
       
   445 
       
   446   if (item->subscription != GABBLE_ROSTER_SUBSCRIPTION_NONE)
       
   447     return TRUE;
       
   448 
       
   449   /* discard anything else */
       
   450   return FALSE;
       
   451 }
       
   452 
       
   453 static GabbleRosterItem *
       
   454 _gabble_roster_item_get (GabbleRoster *roster,
       
   455                          GabbleHandle handle)
       
   456 {
       
   457   GabbleRosterItem *item;
       
   458 
       
   459   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   460 
       
   461   g_assert (roster != NULL);
       
   462   g_assert (GABBLE_IS_ROSTER (roster));
       
   463   g_assert (gabble_handle_is_valid (priv->conn->handles,
       
   464         TP_HANDLE_TYPE_CONTACT, handle, NULL));
       
   465 
       
   466   item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle));
       
   467 
       
   468   if (NULL == item)
       
   469     {
       
   470       item = g_new0 (GabbleRosterItem, 1);
       
   471       gabble_handle_ref (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, handle);
       
   472       g_hash_table_insert (priv->items, GINT_TO_POINTER (handle), item);
       
   473     }
       
   474 
       
   475   return item;
       
   476 }
       
   477 
       
   478 static void
       
   479 _gabble_roster_item_remove (GabbleRoster *roster,
       
   480                             GabbleHandle handle)
       
   481 {
       
   482   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   483 
       
   484   g_assert (roster != NULL);
       
   485   g_assert (GABBLE_IS_ROSTER (roster));
       
   486   g_assert (gabble_handle_is_valid (priv->conn->handles,
       
   487         TP_HANDLE_TYPE_CONTACT, handle, NULL));
       
   488 
       
   489   g_hash_table_remove (priv->items, GINT_TO_POINTER (handle));
       
   490   gabble_handle_unref (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, handle);
       
   491 }
       
   492 
       
   493 static GabbleRosterItem *
       
   494 _gabble_roster_item_update (GabbleRoster *roster,
       
   495                             GabbleHandle handle,
       
   496                             LmMessageNode *node,
       
   497                             gboolean google_roster_mode)
       
   498 {
       
   499   GabbleRosterItem *item;
       
   500   const gchar *ask, *name;
       
   501 
       
   502   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   503 
       
   504   g_assert (roster != NULL);
       
   505   g_assert (GABBLE_IS_ROSTER (roster));
       
   506   g_assert (gabble_handle_is_valid (priv->conn->handles,
       
   507         TP_HANDLE_TYPE_CONTACT, handle, NULL));
       
   508   g_assert (node != NULL);
       
   509 
       
   510   item = _gabble_roster_item_get (roster, handle);
       
   511 
       
   512   item->subscription = _parse_item_subscription (node);
       
   513 
       
   514   ask = lm_message_node_get_attribute (node, "ask");
       
   515   if (NULL != ask && 0 == strcmp (ask, "subscribe"))
       
   516     item->ask_subscribe = TRUE;
       
   517   else
       
   518     item->ask_subscribe = FALSE;
       
   519 
       
   520   if (google_roster_mode)
       
   521     {
       
   522       item->google_type = _parse_google_item_type (node);
       
   523 
       
   524       /* discard roster item if strange, just hide it if it's hidden */
       
   525       if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN)
       
   526         {
       
   527           gabble_debug (DEBUG_FLAG, "Google roster: caching hidden contact %d (%s)", handle,
       
   528               lm_message_node_get_attribute (node, "jid"));
       
   529           item->subscription = GABBLE_ROSTER_SUBSCRIPTION_NONE;
       
   530         }
       
   531       else if (!_google_roster_item_should_keep (node, item))
       
   532         {
       
   533           gabble_debug (DEBUG_FLAG, "Google roster: discarding odd contact %d (%s)", handle,
       
   534               lm_message_node_get_attribute (node, "jid"));
       
   535           item->subscription = GABBLE_ROSTER_SUBSCRIPTION_REMOVE;
       
   536         }
       
   537     }
       
   538 
       
   539   if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE)
       
   540     name = NULL;
       
   541   else
       
   542     name = lm_message_node_get_attribute (node, "name");
       
   543 
       
   544   if (g_strdiff (item->name, name))
       
   545     {
       
   546       g_free (item->name);
       
   547       item->name = g_strdup (name);
       
   548 
       
   549       gabble_debug (DEBUG_FLAG, "name for handle %d changed to %s", handle, name);
       
   550       g_signal_emit (G_OBJECT (roster), signals[NICKNAME_UPDATE], 0, handle);
       
   551     }
       
   552 
       
   553   g_strfreev (item->groups);
       
   554   item->groups = _parse_item_groups (node);
       
   555 
       
   556   return item;
       
   557 }
       
   558 
       
   559 
       
   560 #ifdef ENABLE_DEBUG
       
   561 static gchar *
       
   562 _gabble_roster_item_dump (GabbleRosterItem *item)
       
   563 {
       
   564   GString *str;
       
   565 
       
   566   g_assert (item != NULL);
       
   567 
       
   568   str = g_string_new ("subscription: ");
       
   569 
       
   570   g_string_append (str, _subscription_to_string (item->subscription));
       
   571 
       
   572   if (item->ask_subscribe)
       
   573     g_string_append (str, ", ask: subscribe");
       
   574 
       
   575   if (item->google_type != GOOGLE_ITEM_TYPE_NORMAL)
       
   576     g_string_append_printf (str, ", google_type: %s",
       
   577         _google_item_type_to_string (item->google_type));
       
   578 
       
   579   if (item->name)
       
   580     g_string_append_printf (str, ", name: %s", item->name);
       
   581 
       
   582   if (item->groups)
       
   583     {
       
   584       gchar **tmp;
       
   585       g_string_append (str, ", groups: { ");
       
   586       for (tmp = item->groups; *tmp; tmp++)
       
   587         {
       
   588           g_string_append (str, *tmp);
       
   589           g_string_append_c (str, ' ');
       
   590         }
       
   591       g_string_append (str, "}");
       
   592     }
       
   593 
       
   594   return g_string_free (str, FALSE);
       
   595 }
       
   596 #endif /* ENABLE_DEBUG */
       
   597 
       
   598 
       
   599 static LmMessage *
       
   600 _gabble_roster_message_new (GabbleRoster *roster,
       
   601                             LmMessageSubType sub_type,
       
   602                             LmMessageNode **query_return)
       
   603 {
       
   604   LmMessage *message;
       
   605   LmMessageNode *query_node;
       
   606 
       
   607   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   608 
       
   609   g_assert (roster != NULL);
       
   610   g_assert (GABBLE_IS_ROSTER (roster));
       
   611 
       
   612   message = lm_message_new_with_sub_type (NULL,
       
   613                                           LM_MESSAGE_TYPE_IQ,
       
   614                                           sub_type);
       
   615 
       
   616   query_node = lm_message_node_add_child (message->node, "query", NULL);
       
   617 
       
   618   if (NULL != query_return)
       
   619     *query_return = query_node;
       
   620 
       
   621   lm_message_node_set_attribute (query_node, "xmlns", NS_ROSTER);
       
   622 
       
   623   if (priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER)
       
   624     {
       
   625       lm_message_node_set_attributes (query_node,
       
   626           "xmlns:gr", NS_GOOGLE_ROSTER,
       
   627           "gr:ext", GOOGLE_ROSTER_VERSION,
       
   628           "gr:include", "all",
       
   629           NULL);
       
   630     }
       
   631 
       
   632   return message;
       
   633 }
       
   634 
       
   635 
       
   636 static LmMessage *
       
   637 _gabble_roster_item_to_message (GabbleRoster *roster,
       
   638                                 GabbleHandle handle,
       
   639                                 LmMessageNode **item_return)
       
   640 {
       
   641   const gchar *jid;
       
   642 
       
   643   LmMessage *message;
       
   644   LmMessageNode *query_node, *item_node;
       
   645   GabbleRosterItem *item;
       
   646 
       
   647   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   648 
       
   649   g_assert (roster != NULL);
       
   650   g_assert (GABBLE_IS_ROSTER (roster));
       
   651   g_assert (gabble_handle_is_valid (priv->conn->handles,
       
   652         TP_HANDLE_TYPE_CONTACT, handle, NULL));
       
   653 
       
   654   item = _gabble_roster_item_get (roster, handle);
       
   655 
       
   656   message = _gabble_roster_message_new (roster, LM_MESSAGE_SUB_TYPE_SET,
       
   657       &query_node);
       
   658 
       
   659   item_node = lm_message_node_add_child (query_node, "item", NULL);
       
   660 
       
   661   if (NULL != item_return)
       
   662     *item_return = item_node;
       
   663 
       
   664   jid = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_CONTACT,
       
   665       handle);
       
   666   lm_message_node_set_attribute (item_node, "jid", jid);
       
   667 
       
   668   if (item->subscription != GABBLE_ROSTER_SUBSCRIPTION_NONE)
       
   669     {
       
   670       const gchar *subscription =  _subscription_to_string (item->subscription);
       
   671       lm_message_node_set_attribute (item_node, "subscription", subscription);
       
   672     }
       
   673 
       
   674   if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE)
       
   675     goto DONE;
       
   676 
       
   677   if ((priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) &&
       
   678       item->google_type != GOOGLE_ITEM_TYPE_NORMAL)
       
   679     lm_message_node_set_attribute (item_node, "gr:t",
       
   680         _google_item_type_to_string (item->google_type));
       
   681 
       
   682   if (item->ask_subscribe)
       
   683     lm_message_node_set_attribute (item_node, "ask", "subscribe");
       
   684 
       
   685   if (item->name)
       
   686     lm_message_node_set_attribute (item_node, "name", item->name);
       
   687 
       
   688   if (item->groups)
       
   689     {
       
   690       gchar **tmp;
       
   691 
       
   692       for (tmp = item->groups; *tmp; tmp++)
       
   693         {
       
   694           lm_message_node_add_child (item_node, "group", *tmp);
       
   695         }
       
   696     }
       
   697 
       
   698 DONE:
       
   699   return message;
       
   700 }
       
   701 
       
   702 static GabbleRosterChannel *
       
   703 _gabble_roster_create_channel (GabbleRoster *roster,
       
   704                                GabbleHandle handle)
       
   705 {
       
   706   const char *name;
       
   707   char *object_path;
       
   708 
       
   709   GabbleRosterChannel *chan;
       
   710   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   711 
       
   712   g_assert (priv->channels != NULL);
       
   713   g_assert (g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)) == NULL);
       
   714 
       
   715   name = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_LIST, handle);
       
   716   object_path = g_strdup_printf ("%s/RosterChannel/%s", priv->conn->object_path, name);
       
   717   chan = g_object_new (GABBLE_TYPE_ROSTER_CHANNEL,
       
   718                        "connection", priv->conn,
       
   719                        "object-path", object_path,
       
   720                        "handle", handle,
       
   721                        NULL);
       
   722 
       
   723   gabble_debug (DEBUG_FLAG, "created %s", object_path);
       
   724   g_free (object_path);
       
   725 
       
   726   g_hash_table_insert (priv->channels, GINT_TO_POINTER (handle), chan);
       
   727 
       
   728   if (priv->roster_received)
       
   729     {
       
   730       gabble_debug (DEBUG_FLAG, "roster already received, emitting signal for %s list channel",
       
   731           name);
       
   732 
       
   733       g_signal_emit_by_name (roster, "new-channel", chan);
       
   734     }
       
   735   else
       
   736     {
       
   737       gabble_debug (DEBUG_FLAG, "roster not yet received, not emitting signal for %s list channel",
       
   738           name);
       
   739     }
       
   740 
       
   741   return chan;
       
   742 }
       
   743 
       
   744 static GabbleRosterChannel *
       
   745 _gabble_roster_get_channel (GabbleRoster *roster,
       
   746                             GabbleHandle handle)
       
   747 {
       
   748   GabbleRosterChannel *chan;
       
   749   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   750 
       
   751   g_assert (priv->channels != NULL);
       
   752   g_assert (gabble_handle_is_valid (priv->conn->handles, TP_HANDLE_TYPE_LIST, handle, NULL));
       
   753 
       
   754   chan = g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle));
       
   755 
       
   756   if (chan == NULL)
       
   757     chan = _gabble_roster_create_channel (roster, handle);
       
   758 
       
   759   return chan;
       
   760 }
       
   761 
       
   762 static void
       
   763 _gabble_roster_emit_one (gpointer key,
       
   764                          gpointer value,
       
   765                          gpointer data)
       
   766 {
       
   767   GabbleRoster *roster = GABBLE_ROSTER (data);
       
   768   GabbleRosterChannel *chan = GABBLE_ROSTER_CHANNEL (value);
       
   769 #ifdef ENABLE_DEBUG
       
   770   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   771   GabbleHandle handle = GPOINTER_TO_INT (key);
       
   772   const gchar *name = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_LIST, handle);
       
   773 
       
   774   gabble_debug (DEBUG_FLAG, "roster now received, emitting signal signal for %s list channel",
       
   775       name);
       
   776 #endif
       
   777 
       
   778   g_signal_emit_by_name (roster, "new-channel", chan);
       
   779 }
       
   780 
       
   781 static void
       
   782 _gabble_roster_received (GabbleRoster *roster)
       
   783 {
       
   784   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   785 
       
   786   g_assert (priv->channels != NULL);
       
   787 
       
   788   if (!priv->roster_received)
       
   789     {
       
   790       priv->roster_received = TRUE;
       
   791 
       
   792       g_hash_table_foreach (priv->channels, _gabble_roster_emit_one, roster);
       
   793     }
       
   794 }
       
   795 
       
   796 /**
       
   797  * gabble_roster_iq_cb
       
   798  *
       
   799  * Called by loudmouth when we get an incoming <iq>. This handler
       
   800  * is concerned only with roster queries, and allows other handlers
       
   801  * if queries other than rosters are received.
       
   802  */
       
   803 static LmHandlerResult
       
   804 gabble_roster_iq_cb (LmMessageHandler *handler,
       
   805                      LmConnection *lmconn,
       
   806                      LmMessage *message,
       
   807                      gpointer user_data)
       
   808 {
       
   809   const gchar *from;
       
   810   gboolean google_roster = FALSE;
       
   811   LmMessageNode *iq_node, *query_node;
       
   812   LmMessageSubType sub_type;
       
   813 
       
   814   GabbleRoster *roster = GABBLE_ROSTER (user_data);
       
   815   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
   816 
       
   817   g_assert (lmconn == priv->conn->lmconn);
       
   818 
       
   819   if (priv->channels == NULL)
       
   820     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   821 
       
   822   iq_node = lm_message_get_node (message);
       
   823   query_node = lm_message_node_get_child_with_namespace (iq_node, "query",
       
   824       NS_ROSTER);
       
   825 
       
   826   if (query_node == NULL)
       
   827     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   828 
       
   829   from = lm_message_node_get_attribute (message->node, "from");
       
   830 
       
   831   if (from != NULL)
       
   832     {
       
   833       GabbleHandle sender;
       
   834 
       
   835       sender = gabble_handle_for_contact (priv->conn->handles,
       
   836           from, FALSE);
       
   837 
       
   838       if (sender != priv->conn->self_handle)
       
   839         {
       
   840            NODE_DEBUG (iq_node, "discarding roster IQ which is not from "
       
   841               "ourselves or the server");
       
   842           return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   843         }
       
   844     }
       
   845 
       
   846   if (priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER)
       
   847     {
       
   848       const char *gr_ext;
       
   849 
       
   850       gr_ext = lm_message_node_get_attribute (query_node, "gr:ext");
       
   851 
       
   852       if (!g_strdiff (gr_ext, GOOGLE_ROSTER_VERSION))
       
   853         google_roster = TRUE;
       
   854     }
       
   855 
       
   856   sub_type = lm_message_get_sub_type (message);
       
   857 
       
   858   /* if this is a result, it's from our initial query. if it's a set,
       
   859    * it's a roster push. either way, parse the items. */
       
   860   switch (sub_type)
       
   861     {
       
   862       LmMessageNode *item_node;
       
   863       GIntSet *pub_add, *pub_rem,
       
   864               *sub_add, *sub_rem, *sub_rp,
       
   865               *known_add, *known_rem,
       
   866               *deny_add, *deny_rem;
       
   867       GArray *removed;
       
   868       GabbleHandle handle;
       
   869       GabbleRosterChannel *chan;
       
   870       guint i;
       
   871 
       
   872     case LM_MESSAGE_SUB_TYPE_RESULT:
       
   873     case LM_MESSAGE_SUB_TYPE_SET:
       
   874       /* asymmetry is because we don't get locally pending subscription
       
   875        * requests via <roster>, we get it via <presence> */
       
   876       pub_add = g_intset_new ();
       
   877       pub_rem = g_intset_new ();
       
   878       sub_add = g_intset_new ();
       
   879       sub_rem = g_intset_new ();
       
   880       sub_rp = g_intset_new ();
       
   881       known_add = g_intset_new ();
       
   882       known_rem = g_intset_new ();
       
   883       removed = g_array_new (FALSE, FALSE, sizeof (GabbleHandle));
       
   884 
       
   885       if (google_roster)
       
   886         {
       
   887           deny_add = g_intset_new ();
       
   888           deny_rem = g_intset_new ();
       
   889         }
       
   890       else
       
   891         {
       
   892           deny_add = NULL;
       
   893           deny_rem = NULL;
       
   894         }
       
   895 
       
   896       /* get the publish channel first because we need it when processing */
       
   897       handle = GABBLE_LIST_HANDLE_PUBLISH;
       
   898       chan = _gabble_roster_get_channel (roster, handle);
       
   899 
       
   900       /* iterate every sub-node, which we expect to be <item>s */
       
   901       for (item_node = query_node->children;
       
   902            item_node;
       
   903            item_node = item_node->next)
       
   904         {
       
   905           const char *jid;
       
   906           GabbleRosterItem *item;
       
   907 
       
   908           if (strcmp (item_node->name, "item"))
       
   909             {
       
   910                NODE_DEBUG (item_node, "query sub-node is not item, skipping");
       
   911               continue;
       
   912             }
       
   913 
       
   914           jid = lm_message_node_get_attribute (item_node, "jid");
       
   915           if (!jid)
       
   916             {
       
   917                NODE_DEBUG (item_node, "item node has no jid, skipping");
       
   918               continue;
       
   919             }
       
   920 
       
   921           handle = gabble_handle_for_contact (priv->conn->handles, jid, FALSE);
       
   922           if (handle == 0)
       
   923             {
       
   924                NODE_DEBUG (item_node, "item jid is malformed, skipping");
       
   925               continue;
       
   926             }
       
   927 
       
   928           item = _gabble_roster_item_update (roster, handle, item_node,
       
   929               google_roster);
       
   930 
       
   931 #ifdef ENABLE_DEBUG
       
   932           if (DEBUGGING)
       
   933             {
       
   934               gchar *dump = _gabble_roster_item_dump (item);
       
   935               gabble_debug (DEBUG_FLAG, "jid: %s, %s", jid, dump);
       
   936               g_free (dump);
       
   937             }
       
   938 #endif
       
   939 
       
   940           /* handle publish list changes */
       
   941           switch (item->subscription)
       
   942             {
       
   943             case GABBLE_ROSTER_SUBSCRIPTION_FROM:
       
   944             case GABBLE_ROSTER_SUBSCRIPTION_BOTH:
       
   945               g_intset_add (pub_add, handle);
       
   946               break;
       
   947             case GABBLE_ROSTER_SUBSCRIPTION_NONE:
       
   948             case GABBLE_ROSTER_SUBSCRIPTION_TO:
       
   949             case GABBLE_ROSTER_SUBSCRIPTION_REMOVE:
       
   950               /* publish channel is a bit odd, the roster item doesn't tell us
       
   951                * if someone is awaiting our approval - we get this via presence
       
   952                * type=subscribe, so we have to not remove them if they're
       
   953                * already local_pending in our publish channel */
       
   954               if (!handle_set_is_member (chan->group.local_pending, handle))
       
   955                 {
       
   956                   g_intset_add (pub_rem, handle);
       
   957                 }
       
   958               break;
       
   959             default:
       
   960               g_assert_not_reached ();
       
   961             }
       
   962 
       
   963           /* handle subscribe list changes */
       
   964           switch (item->subscription)
       
   965             {
       
   966             case GABBLE_ROSTER_SUBSCRIPTION_TO:
       
   967             case GABBLE_ROSTER_SUBSCRIPTION_BOTH:
       
   968               g_intset_add (sub_add, handle);
       
   969               break;
       
   970             case GABBLE_ROSTER_SUBSCRIPTION_NONE:
       
   971             case GABBLE_ROSTER_SUBSCRIPTION_FROM:
       
   972               if (item->ask_subscribe)
       
   973                 g_intset_add (sub_rp, handle);
       
   974               else
       
   975                 g_intset_add (sub_rem, handle);
       
   976               break;
       
   977             case GABBLE_ROSTER_SUBSCRIPTION_REMOVE:
       
   978               g_intset_add (sub_rem, handle);
       
   979               break;
       
   980             default:
       
   981               g_assert_not_reached ();
       
   982             }
       
   983 
       
   984           /* handle known list changes */
       
   985           switch (item->subscription)
       
   986             {
       
   987             case GABBLE_ROSTER_SUBSCRIPTION_NONE:
       
   988             case GABBLE_ROSTER_SUBSCRIPTION_TO:
       
   989             case GABBLE_ROSTER_SUBSCRIPTION_FROM:
       
   990             case GABBLE_ROSTER_SUBSCRIPTION_BOTH:
       
   991               if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN)
       
   992                   g_intset_add (known_rem, handle);
       
   993               else
       
   994                   g_intset_add (known_add, handle);
       
   995               break;
       
   996             case GABBLE_ROSTER_SUBSCRIPTION_REMOVE:
       
   997               g_intset_add (known_rem, handle);
       
   998               break;
       
   999             default:
       
  1000               g_assert_not_reached ();
       
  1001             }
       
  1002 
       
  1003           /* handle deny list changes */
       
  1004           if (google_roster)
       
  1005             {
       
  1006               switch (item->subscription)
       
  1007                 {
       
  1008                 case GABBLE_ROSTER_SUBSCRIPTION_NONE:
       
  1009                 case GABBLE_ROSTER_SUBSCRIPTION_TO:
       
  1010                 case GABBLE_ROSTER_SUBSCRIPTION_FROM:
       
  1011                 case GABBLE_ROSTER_SUBSCRIPTION_BOTH:
       
  1012                   if (item->google_type == GOOGLE_ITEM_TYPE_BLOCKED)
       
  1013                     g_intset_add (deny_add, handle);
       
  1014                   else
       
  1015                     g_intset_add (deny_rem, handle);
       
  1016                   break;
       
  1017                 case GABBLE_ROSTER_SUBSCRIPTION_REMOVE:
       
  1018                   g_intset_add (deny_rem, handle);
       
  1019                   break;
       
  1020                 default:
       
  1021                   g_assert_not_reached ();
       
  1022                 }
       
  1023             }
       
  1024 
       
  1025           /* delay removing items from roster until signals have been emitted;
       
  1026            * otherwise handles go out of scope! */
       
  1027           if (GABBLE_ROSTER_SUBSCRIPTION_REMOVE == item->subscription)
       
  1028             g_array_append_val (removed, handle);
       
  1029         }
       
  1030 
       
  1031       /* chan was initialised to the publish channel before the for loop */
       
  1032 
       
  1033       gabble_debug (DEBUG_FLAG, "calling change members on publish channel");
       
  1034       gabble_group_mixin_change_members (G_OBJECT (chan),
       
  1035             "", pub_add, pub_rem, NULL, NULL, 0, 0);
       
  1036 
       
  1037       handle = GABBLE_LIST_HANDLE_SUBSCRIBE;
       
  1038       chan = _gabble_roster_get_channel (roster, handle);
       
  1039 
       
  1040       gabble_debug (DEBUG_FLAG, "calling change members on subscribe channel");
       
  1041       gabble_group_mixin_change_members (G_OBJECT (chan),
       
  1042             "", sub_add, sub_rem, NULL, sub_rp, 0, 0);
       
  1043 
       
  1044       handle = GABBLE_LIST_HANDLE_KNOWN;
       
  1045       chan = _gabble_roster_get_channel (roster, handle);
       
  1046 
       
  1047       gabble_debug (DEBUG_FLAG, "calling change members on known channel");
       
  1048       gabble_group_mixin_change_members (G_OBJECT (chan),
       
  1049             "", known_add, known_rem, NULL, NULL, 0, 0);
       
  1050 
       
  1051       if (google_roster)
       
  1052         {
       
  1053           handle = GABBLE_LIST_HANDLE_DENY;
       
  1054           chan = _gabble_roster_get_channel (roster, handle);
       
  1055 
       
  1056           gabble_debug (DEBUG_FLAG, "calling change members on deny channel");
       
  1057           gabble_group_mixin_change_members (G_OBJECT (chan),
       
  1058               "", deny_add, deny_rem, NULL, NULL, 0, 0);
       
  1059 
       
  1060           g_intset_destroy (deny_add);
       
  1061           g_intset_destroy (deny_rem);
       
  1062         }
       
  1063 
       
  1064       for (i = 0; i < removed->len; i++)
       
  1065           _gabble_roster_item_remove (roster,
       
  1066               g_array_index (removed, GabbleHandle, i));
       
  1067 
       
  1068       g_intset_destroy (pub_add);
       
  1069       g_intset_destroy (pub_rem);
       
  1070       g_intset_destroy (sub_add);
       
  1071       g_intset_destroy (sub_rem);
       
  1072       g_intset_destroy (sub_rp);
       
  1073       g_intset_destroy (known_add);
       
  1074       g_intset_destroy (known_rem);
       
  1075       g_array_free (removed, TRUE);
       
  1076       break;
       
  1077     default:
       
  1078        NODE_DEBUG (iq_node, "unhandled roster IQ");
       
  1079       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1080     }
       
  1081 
       
  1082   switch (sub_type)
       
  1083     {
       
  1084     case LM_MESSAGE_SUB_TYPE_RESULT:
       
  1085       /* result means it's a roster push, so the roster is now complete and we
       
  1086        * can emit signals */
       
  1087       _gabble_roster_received (roster);
       
  1088       break;
       
  1089     case LM_MESSAGE_SUB_TYPE_SET:
       
  1090       /* acknowledge roster */
       
  1091       _gabble_connection_acknowledge_set_iq (priv->conn, message);
       
  1092       break;
       
  1093     default:
       
  1094       break;
       
  1095     }
       
  1096 
       
  1097   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1098 }
       
  1099 
       
  1100 
       
  1101 static void
       
  1102 _gabble_roster_send_presence_ack (GabbleRoster *roster,
       
  1103                                   const gchar *from,
       
  1104                                   LmMessageSubType sub_type,
       
  1105                                   gboolean changed)
       
  1106 {
       
  1107   LmMessage *reply;
       
  1108 
       
  1109   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1110 
       
  1111   if (!changed)
       
  1112     {
       
  1113       gabble_debug (DEBUG_FLAG, "not sending ack to avoid loop with buggy server");
       
  1114       return;
       
  1115     }
       
  1116 
       
  1117   switch (sub_type)
       
  1118     {
       
  1119     case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE:
       
  1120       sub_type = LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED;
       
  1121       break;
       
  1122     case LM_MESSAGE_SUB_TYPE_SUBSCRIBED:
       
  1123       sub_type = LM_MESSAGE_SUB_TYPE_SUBSCRIBE;
       
  1124       break;
       
  1125     case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED:
       
  1126       sub_type = LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE;
       
  1127       break;
       
  1128     default:
       
  1129       g_assert_not_reached();
       
  1130       return;
       
  1131     }
       
  1132 
       
  1133   reply = lm_message_new_with_sub_type (from,
       
  1134       LM_MESSAGE_TYPE_PRESENCE,
       
  1135       sub_type);
       
  1136 
       
  1137   _gabble_connection_send (priv->conn, reply, NULL);
       
  1138 
       
  1139   lm_message_unref (reply);
       
  1140 }
       
  1141 
       
  1142 
       
  1143 /**
       
  1144  * connection_presence_roster_cb:
       
  1145  * @handler: #LmMessageHandler for this message
       
  1146  * @connection: #LmConnection that originated the message
       
  1147  * @message: the presence message
       
  1148  * @user_data: callback data
       
  1149  *
       
  1150  * Called by loudmouth when we get an incoming <presence>.
       
  1151  */
       
  1152 static LmHandlerResult
       
  1153 gabble_roster_presence_cb (LmMessageHandler *handler,
       
  1154                            LmConnection *lmconn,
       
  1155                            LmMessage *message,
       
  1156                            gpointer user_data)
       
  1157 {
       
  1158   LmMessageNode *pres_node, *child_node;
       
  1159   const char *from;
       
  1160   LmMessageSubType sub_type;
       
  1161   GIntSet *tmp;
       
  1162   GabbleHandle handle;
       
  1163   const gchar *status_message = NULL;
       
  1164   GabbleRosterChannel *chan = NULL;
       
  1165   gboolean changed;
       
  1166   GabbleRoster *roster = GABBLE_ROSTER (user_data);
       
  1167   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1168 
       
  1169   g_assert (lmconn == priv->conn->lmconn);
       
  1170 
       
  1171   if (priv->channels == NULL)
       
  1172     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1173 
       
  1174   pres_node = lm_message_get_node (message);
       
  1175 
       
  1176   from = lm_message_node_get_attribute (pres_node, "from");
       
  1177 
       
  1178   if (from == NULL)
       
  1179     {
       
  1180        NODE_DEBUG (pres_node, "presence stanza without from attribute, ignoring");
       
  1181       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1182     }
       
  1183 
       
  1184   sub_type = lm_message_get_sub_type (message);
       
  1185 
       
  1186   handle = gabble_handle_for_contact (priv->conn->handles, from, FALSE);
       
  1187 
       
  1188   if (handle == 0)
       
  1189     {
       
  1190       NODE_DEBUG (pres_node, "ignoring presence from malformed jid");
       
  1191       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1192     }
       
  1193 
       
  1194   if (handle == priv->conn->self_handle)
       
  1195     {
       
  1196        NODE_DEBUG (pres_node, "ignoring presence from ourselves on another resource");
       
  1197       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1198     }
       
  1199 
       
  1200   g_assert (handle != 0);
       
  1201 
       
  1202   child_node = lm_message_node_get_child (pres_node, "status");
       
  1203   if (child_node)
       
  1204     status_message = lm_message_node_get_value (child_node);
       
  1205 
       
  1206   switch (sub_type)
       
  1207     {
       
  1208     case LM_MESSAGE_SUB_TYPE_SUBSCRIBE:
       
  1209       gabble_debug (DEBUG_FLAG, "making %s (handle %u) local pending on the publish channel",
       
  1210           from, handle);
       
  1211 
       
  1212       tmp = g_intset_new ();
       
  1213       g_intset_add (tmp, handle);
       
  1214 
       
  1215       handle = GABBLE_LIST_HANDLE_PUBLISH;
       
  1216       chan = _gabble_roster_get_channel (roster, handle);
       
  1217       gabble_group_mixin_change_members (G_OBJECT (chan), status_message,
       
  1218           NULL, NULL, tmp, NULL, 0, 0);
       
  1219 
       
  1220       g_intset_destroy (tmp);
       
  1221 
       
  1222       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1223     case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE:
       
  1224       gabble_debug (DEBUG_FLAG, "removing %s (handle %u) from the publish channel",
       
  1225           from, handle);
       
  1226 
       
  1227       tmp = g_intset_new ();
       
  1228       g_intset_add (tmp, handle);
       
  1229 
       
  1230       handle = GABBLE_LIST_HANDLE_PUBLISH;
       
  1231       chan = _gabble_roster_get_channel (roster, handle);
       
  1232       changed = gabble_group_mixin_change_members (G_OBJECT (chan),
       
  1233           status_message, NULL, tmp, NULL, NULL, 0, 0);
       
  1234 
       
  1235       _gabble_roster_send_presence_ack (roster, from, sub_type, changed);
       
  1236 
       
  1237       g_intset_destroy (tmp);
       
  1238 
       
  1239       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1240     case LM_MESSAGE_SUB_TYPE_SUBSCRIBED:
       
  1241       gabble_debug (DEBUG_FLAG, "adding %s (handle %u) to the subscribe channel",
       
  1242           from, handle);
       
  1243 
       
  1244       tmp = g_intset_new ();
       
  1245       g_intset_add (tmp, handle);
       
  1246 
       
  1247       handle = GABBLE_LIST_HANDLE_SUBSCRIBE;
       
  1248       chan = _gabble_roster_get_channel (roster, handle);
       
  1249       changed = gabble_group_mixin_change_members (G_OBJECT (chan),
       
  1250           status_message, tmp, NULL, NULL, NULL, 0, 0);
       
  1251 
       
  1252       _gabble_roster_send_presence_ack (roster, from, sub_type, changed);
       
  1253 
       
  1254       g_intset_destroy (tmp);
       
  1255 
       
  1256       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1257     case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED:
       
  1258       gabble_debug (DEBUG_FLAG, "removing %s (handle %u) from the subscribe channel",
       
  1259           from, handle);
       
  1260 
       
  1261       tmp = g_intset_new ();
       
  1262       g_intset_add (tmp, handle);
       
  1263 
       
  1264       handle = GABBLE_LIST_HANDLE_SUBSCRIBE;
       
  1265       chan = _gabble_roster_get_channel (roster, handle);
       
  1266       changed = gabble_group_mixin_change_members (G_OBJECT (chan),
       
  1267           status_message, NULL, tmp, NULL, NULL, 0, 0);
       
  1268 
       
  1269       _gabble_roster_send_presence_ack (roster, from, sub_type, changed);
       
  1270 
       
  1271       g_intset_destroy (tmp);
       
  1272 
       
  1273       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1274     default:
       
  1275       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
  1276     }
       
  1277 }
       
  1278 
       
  1279 static void
       
  1280 gabble_roster_factory_iface_close_all (TpChannelFactoryIface *iface)
       
  1281 {
       
  1282   GabbleRoster *roster = GABBLE_ROSTER (iface);
       
  1283   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1284 
       
  1285   gabble_debug (DEBUG_FLAG, "closing channels");
       
  1286 
       
  1287   if (priv->channels)
       
  1288     {
       
  1289       g_hash_table_destroy (priv->channels);
       
  1290       priv->channels = NULL;
       
  1291     }
       
  1292 }
       
  1293 
       
  1294 static void
       
  1295 gabble_roster_factory_iface_connecting (TpChannelFactoryIface *iface)
       
  1296 {
       
  1297   GabbleRoster *roster = GABBLE_ROSTER (iface);
       
  1298   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1299 
       
  1300   gabble_debug (DEBUG_FLAG, "adding callbacks");
       
  1301 
       
  1302   g_assert (priv->iq_cb == NULL);
       
  1303   g_assert (priv->presence_cb == NULL);
       
  1304 
       
  1305   priv->iq_cb = lm_message_handler_new (gabble_roster_iq_cb,
       
  1306                                         roster, NULL);
       
  1307   lm_connection_register_message_handler (priv->conn->lmconn,
       
  1308                                           priv->iq_cb,
       
  1309                                           LM_MESSAGE_TYPE_IQ,
       
  1310                                           LM_HANDLER_PRIORITY_NORMAL);
       
  1311 
       
  1312   priv->presence_cb = lm_message_handler_new (gabble_roster_presence_cb,
       
  1313                                               roster, NULL);
       
  1314   lm_connection_register_message_handler (priv->conn->lmconn,
       
  1315                                           priv->presence_cb,
       
  1316                                           LM_MESSAGE_TYPE_PRESENCE,
       
  1317                                           LM_HANDLER_PRIORITY_LAST);
       
  1318 }
       
  1319 
       
  1320 static void
       
  1321 gabble_roster_factory_iface_connected (TpChannelFactoryIface *iface)
       
  1322 {
       
  1323   LmMessage *message;
       
  1324 
       
  1325   GabbleRoster *roster = GABBLE_ROSTER (iface);
       
  1326   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1327 
       
  1328   gabble_debug (DEBUG_FLAG, "requesting roster");
       
  1329 
       
  1330   message = _gabble_roster_message_new (roster, LM_MESSAGE_SUB_TYPE_GET, NULL);
       
  1331 
       
  1332   _gabble_connection_send (priv->conn, message, NULL);
       
  1333 
       
  1334   lm_message_unref (message);
       
  1335 }
       
  1336 
       
  1337 static void
       
  1338 gabble_roster_factory_iface_disconnected (TpChannelFactoryIface *iface)
       
  1339 {
       
  1340   GabbleRoster *roster = GABBLE_ROSTER (iface);
       
  1341   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1342 
       
  1343   gabble_debug (DEBUG_FLAG, "removing callbacks");
       
  1344 
       
  1345   g_assert (priv->iq_cb != NULL);
       
  1346   g_assert (priv->presence_cb != NULL);
       
  1347 
       
  1348   lm_connection_unregister_message_handler (priv->conn->lmconn,
       
  1349                                             priv->iq_cb,
       
  1350                                             LM_MESSAGE_TYPE_IQ);
       
  1351   lm_message_handler_unref (priv->iq_cb);
       
  1352   priv->iq_cb = NULL;
       
  1353 
       
  1354   lm_connection_unregister_message_handler (priv->conn->lmconn,
       
  1355                                             priv->presence_cb,
       
  1356                                             LM_MESSAGE_TYPE_PRESENCE);
       
  1357   lm_message_handler_unref (priv->presence_cb);
       
  1358   priv->presence_cb = NULL;
       
  1359 }
       
  1360 
       
  1361 struct foreach_data {
       
  1362     TpChannelFunc func;
       
  1363     gpointer data;
       
  1364 };
       
  1365 
       
  1366 static void
       
  1367 _gabble_roster_factory_iface_foreach_one (gpointer key,
       
  1368                                           gpointer value,
       
  1369                                           gpointer data)
       
  1370 {
       
  1371   TpChannelIface *chan = TP_CHANNEL_IFACE (value);
       
  1372   struct foreach_data *foreach = (struct foreach_data *) data;
       
  1373 
       
  1374   foreach->func (chan, foreach->data);
       
  1375 }
       
  1376 
       
  1377 static void
       
  1378 gabble_roster_factory_iface_foreach (TpChannelFactoryIface *iface,
       
  1379                                      TpChannelFunc func,
       
  1380                                      gpointer data)
       
  1381 {
       
  1382   struct foreach_data foreach;
       
  1383 
       
  1384   GabbleRoster *roster = GABBLE_ROSTER (iface);
       
  1385   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1386 
       
  1387   foreach.func = func;
       
  1388   foreach.data = data;
       
  1389 
       
  1390   g_hash_table_foreach (priv->channels,
       
  1391       _gabble_roster_factory_iface_foreach_one, &foreach);
       
  1392 }
       
  1393 
       
  1394 static TpChannelFactoryRequestStatus
       
  1395 gabble_roster_factory_iface_request (TpChannelFactoryIface *iface,
       
  1396                                      const gchar *chan_type,
       
  1397                                      TpHandleType handle_type,
       
  1398                                      guint handle,
       
  1399                                      TpChannelIface **ret,
       
  1400                                      GError **error)
       
  1401 {
       
  1402   GabbleRoster *roster = GABBLE_ROSTER (iface);
       
  1403   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1404 
       
  1405   if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST))
       
  1406     return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED;
       
  1407 
       
  1408   if (handle_type != TP_HANDLE_TYPE_LIST)
       
  1409     return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE;
       
  1410 
       
  1411   if (!gabble_handle_is_valid (priv->conn->handles,
       
  1412                                TP_HANDLE_TYPE_LIST,
       
  1413                                handle,
       
  1414                                NULL))
       
  1415     return TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE;
       
  1416 
       
  1417   /* disallow "deny" channels if we don't have google:roster support */
       
  1418   if (handle == GABBLE_LIST_HANDLE_DENY &&
       
  1419       !(priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER))
       
  1420     return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE;
       
  1421 
       
  1422   if (priv->roster_received)
       
  1423     {
       
  1424       GabbleRosterChannel *chan;
       
  1425       chan = _gabble_roster_get_channel (roster, handle);
       
  1426       *ret = TP_CHANNEL_IFACE (chan);
       
  1427       return TP_CHANNEL_FACTORY_REQUEST_STATUS_DONE;
       
  1428     }
       
  1429   else
       
  1430     {
       
  1431       return TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED;
       
  1432     }
       
  1433 }
       
  1434 
       
  1435 static void
       
  1436 gabble_roster_factory_iface_init (gpointer g_iface,
       
  1437                                   gpointer iface_data)
       
  1438 {
       
  1439   TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface;
       
  1440 
       
  1441   klass->close_all = gabble_roster_factory_iface_close_all;
       
  1442   klass->connecting = gabble_roster_factory_iface_connecting;
       
  1443   klass->connected = gabble_roster_factory_iface_connected;
       
  1444   klass->disconnected = gabble_roster_factory_iface_disconnected;
       
  1445   klass->foreach = gabble_roster_factory_iface_foreach;
       
  1446   klass->request = gabble_roster_factory_iface_request;
       
  1447 }
       
  1448 
       
  1449 GabbleRoster *
       
  1450 gabble_roster_new (GabbleConnection *conn)
       
  1451 {
       
  1452   g_return_val_if_fail (conn != NULL, NULL);
       
  1453 
       
  1454   return g_object_new (GABBLE_TYPE_ROSTER,
       
  1455                        "connection", conn,
       
  1456                        NULL);
       
  1457 }
       
  1458 
       
  1459 GabbleRosterSubscription
       
  1460 gabble_roster_handle_get_subscription (GabbleRoster *roster,
       
  1461                                        GabbleHandle handle)
       
  1462 {
       
  1463   GabbleRosterItem *item;
       
  1464 
       
  1465   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1466 
       
  1467   g_return_val_if_fail (roster != NULL, GABBLE_ROSTER_SUBSCRIPTION_NONE);
       
  1468   g_return_val_if_fail (GABBLE_IS_ROSTER (roster),
       
  1469       GABBLE_ROSTER_SUBSCRIPTION_NONE);
       
  1470   g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles,
       
  1471         TP_HANDLE_TYPE_CONTACT, handle, NULL),
       
  1472       GABBLE_ROSTER_SUBSCRIPTION_NONE);
       
  1473 
       
  1474   item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle));
       
  1475 
       
  1476   if (NULL == item)
       
  1477     return GABBLE_ROSTER_SUBSCRIPTION_NONE;
       
  1478 
       
  1479   return item->subscription;
       
  1480 }
       
  1481 
       
  1482 gboolean
       
  1483 gabble_roster_handle_set_blocked (GabbleRoster *roster,
       
  1484                                   GabbleHandle handle,
       
  1485                                   gboolean blocked,
       
  1486                                   GError **error)
       
  1487 {
       
  1488   GabbleRosterItem *item;
       
  1489   GoogleItemType orig_type;
       
  1490   LmMessage *message;
       
  1491   gboolean ret;
       
  1492   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1493 
       
  1494   g_return_val_if_fail (roster != NULL, FALSE);
       
  1495   g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE);
       
  1496   g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles,
       
  1497       TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE);
       
  1498   g_return_val_if_fail (priv->conn->features &
       
  1499       GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER, FALSE);
       
  1500 
       
  1501   item = _gabble_roster_item_get (roster, handle);
       
  1502   orig_type = item->google_type;
       
  1503 
       
  1504   if (blocked == (orig_type == GOOGLE_ITEM_TYPE_BLOCKED))
       
  1505     return TRUE;
       
  1506 
       
  1507   /* temporarily set the desired block state and generate a message */
       
  1508   if (blocked)
       
  1509     item->google_type = GOOGLE_ITEM_TYPE_BLOCKED;
       
  1510   else
       
  1511     item->google_type = GOOGLE_ITEM_TYPE_NORMAL;
       
  1512   message = _gabble_roster_item_to_message (roster, handle, NULL);
       
  1513   item->google_type = orig_type;
       
  1514 
       
  1515   ret = _gabble_connection_send (priv->conn, message, error);
       
  1516 
       
  1517   lm_message_unref (message);
       
  1518 
       
  1519   return ret;
       
  1520 }
       
  1521 
       
  1522 gboolean
       
  1523 gabble_roster_handle_has_entry (GabbleRoster *roster,
       
  1524                                 GabbleHandle handle)
       
  1525 {
       
  1526   GabbleRosterItem *item;
       
  1527 
       
  1528   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1529 
       
  1530   g_return_val_if_fail (roster != NULL, FALSE);
       
  1531   g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE);
       
  1532   g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles,
       
  1533       TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE);
       
  1534 
       
  1535   item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle));
       
  1536 
       
  1537   return (NULL != item);
       
  1538 }
       
  1539 
       
  1540 const gchar *
       
  1541 gabble_roster_handle_get_name (GabbleRoster *roster,
       
  1542                                GabbleHandle handle)
       
  1543 {
       
  1544   GabbleRosterItem *item;
       
  1545 
       
  1546   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1547 
       
  1548   g_return_val_if_fail (roster != NULL, NULL);
       
  1549   g_return_val_if_fail (GABBLE_IS_ROSTER (roster), NULL);
       
  1550   g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles,
       
  1551       TP_HANDLE_TYPE_CONTACT, handle, NULL), NULL);
       
  1552 
       
  1553   item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle));
       
  1554 
       
  1555   if (NULL == item)
       
  1556     return NULL;
       
  1557 
       
  1558   return item->name;
       
  1559 }
       
  1560 
       
  1561 gboolean
       
  1562 gabble_roster_handle_set_name (GabbleRoster *roster,
       
  1563                                GabbleHandle handle,
       
  1564                                const gchar *name,
       
  1565                                GError **error)
       
  1566 {
       
  1567   LmMessage *message;
       
  1568   LmMessageNode *item_node;
       
  1569   gboolean ret;
       
  1570   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1571 
       
  1572   g_return_val_if_fail (roster != NULL, FALSE);
       
  1573   g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE);
       
  1574   g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles,
       
  1575       TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE);
       
  1576   g_return_val_if_fail (name != NULL, FALSE);
       
  1577 
       
  1578   message = _gabble_roster_item_to_message (roster, handle, &item_node);
       
  1579 
       
  1580   lm_message_node_set_attribute (item_node, "name", name);
       
  1581 
       
  1582   ret = _gabble_connection_send (priv->conn, message, error);
       
  1583 
       
  1584   lm_message_unref (message);
       
  1585 
       
  1586   return ret;
       
  1587 }
       
  1588 
       
  1589 gboolean
       
  1590 gabble_roster_handle_remove (GabbleRoster *roster,
       
  1591                              GabbleHandle handle,
       
  1592                              GError **error)
       
  1593 {
       
  1594   GabbleRosterItem *item;
       
  1595   GabbleRosterSubscription subscription;
       
  1596   LmMessage *message;
       
  1597   gboolean ret;
       
  1598   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1599 
       
  1600   g_return_val_if_fail (roster != NULL, FALSE);
       
  1601   g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE);
       
  1602   g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles,
       
  1603       TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE);
       
  1604 
       
  1605   item = _gabble_roster_item_get (roster, handle);
       
  1606   subscription = item->subscription;
       
  1607   item->subscription = GABBLE_ROSTER_SUBSCRIPTION_REMOVE;
       
  1608 
       
  1609   message = _gabble_roster_item_to_message (roster, handle, NULL);
       
  1610   ret = _gabble_connection_send (priv->conn, message, error);
       
  1611   lm_message_unref (message);
       
  1612 
       
  1613   item->subscription = subscription;
       
  1614 
       
  1615   return ret;
       
  1616 }
       
  1617 
       
  1618 gboolean
       
  1619 gabble_roster_handle_add (GabbleRoster *roster,
       
  1620                           GabbleHandle handle,
       
  1621                           GError **error)
       
  1622 {
       
  1623   GabbleRosterItem *item;
       
  1624   LmMessage *message;
       
  1625   gboolean do_add = FALSE;
       
  1626   gboolean ret;
       
  1627   GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster);
       
  1628 
       
  1629   g_return_val_if_fail (roster != NULL, FALSE);
       
  1630   g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE);
       
  1631   g_return_val_if_fail (gabble_handle_is_valid (priv->conn->handles,
       
  1632       TP_HANDLE_TYPE_CONTACT, handle, NULL), FALSE);
       
  1633 
       
  1634   if (!gabble_roster_handle_has_entry (roster, handle))
       
  1635       do_add = TRUE;
       
  1636 
       
  1637   item = _gabble_roster_item_get (roster, handle);
       
  1638 
       
  1639   if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN)
       
  1640     {
       
  1641       item->google_type = GOOGLE_ITEM_TYPE_NORMAL;
       
  1642       do_add = TRUE;
       
  1643     }
       
  1644 
       
  1645   if (!do_add)
       
  1646       return TRUE;
       
  1647 
       
  1648   message = _gabble_roster_item_to_message (roster, handle, NULL);
       
  1649   ret = _gabble_connection_send (priv->conn, message, error);
       
  1650   lm_message_unref (message);
       
  1651 
       
  1652   return ret;
       
  1653 }