telepathygabble/src/vcard-manager.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * vcard-manager.c - Source for Gabble vCard lookup 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 <dbus/dbus-glib-lowlevel.h>
       
    25 
       
    26 #include "debug.h"
       
    27 #include "gabble-connection.h"
       
    28 #include "namespaces.h"
       
    29 #include "telepathy-helpers.h"
       
    30 #include "util.h"
       
    31 #include "vcard-manager.h"
       
    32 
       
    33 #include "gabble_enums.h"
       
    34 
       
    35 #define DBUS_API_SUBJECT_TO_CHANGE
       
    36 #define DEBUG_FLAG GABBLE_DEBUG_VCARD
       
    37 #define DEFAULT_REQUEST_TIMEOUT 20000
       
    38 
       
    39 #ifdef DEBUG_FLAG
       
    40 //#define DEBUG(format, ...)
       
    41 #define DEBUGGING 0
       
    42 #define NODE_DEBUG(n, s)
       
    43 #endif /* DEBUG_FLAG */
       
    44 
       
    45 
       
    46 #ifndef EMULATOR
       
    47 G_DEFINE_TYPE(GabbleVCardManager, gabble_vcard_manager, G_TYPE_OBJECT);
       
    48 #endif
       
    49 
       
    50 /* signal enum */
       
    51 enum
       
    52 {
       
    53     NICKNAME_UPDATE,
       
    54     LAST_SIGNAL 
       
    55 #ifdef EMULATOR    
       
    56     = LAST_SIGNAL_VCARD_MGR
       
    57 #endif
       
    58     
       
    59 };
       
    60 
       
    61 #ifdef EMULATOR
       
    62 #include "libgabble_wsd_solution.h"
       
    63 
       
    64 	GET_STATIC_ARRAY_FROM_TLS(signals,gabble_vcard_mgr,guint)
       
    65 	#define signals (GET_WSD_VAR_NAME(signals,gabble_vcard_mgr, s)())	
       
    66 	
       
    67 	GET_STATIC_VAR_FROM_TLS(quark1,gabble_vcard_mgr,GQuark)
       
    68 	#define quark1 (*GET_WSD_VAR_NAME(quark1,gabble_vcard_mgr, s)())	
       
    69 	
       
    70 	GET_STATIC_VAR_FROM_TLS(quark2,gabble_vcard_mgr,GQuark)
       
    71 	#define quark2 (*GET_WSD_VAR_NAME(quark2,gabble_vcard_mgr, s)())
       
    72 	
       
    73 	GET_STATIC_VAR_FROM_TLS(gabble_vcard_manager_parent_class,gabble_vcard_mgr,gpointer)
       
    74 	#define gabble_vcard_manager_parent_class (*GET_WSD_VAR_NAME(gabble_vcard_manager_parent_class,gabble_vcard_mgr,s)())
       
    75 	
       
    76 	GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_vcard_mgr,GType)
       
    77 	#define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_vcard_mgr,s)())
       
    78 		
       
    79     GET_STATIC_ARRAY_FROM_TLS(NO_ALIAS,gabble_vcard_mgr,gchar)
       
    80 	#define NO_ALIAS (GET_WSD_VAR_NAME(NO_ALIAS,gabble_vcard_mgr,s)())
       
    81 	
       
    82 static void gabble_vcard_manager_init (GabbleVCardManager *self); 
       
    83 static void gabble_vcard_manager_class_init (GabbleVCardManagerClass *klass); 
       
    84 static void gabble_vcard_manager_class_intern_init (gpointer klass)
       
    85  { 
       
    86  gabble_vcard_manager_parent_class = g_type_class_peek_parent (klass);
       
    87  gabble_vcard_manager_class_init ((GabbleVCardManagerClass*) klass);
       
    88   }
       
    89    EXPORT_C GType gabble_vcard_manager_get_type (void) 
       
    90    {
       
    91    if ((g_define_type_id == 0)) { static const GTypeInfo g_define_type_info = { sizeof (GabbleVCardManagerClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_vcard_manager_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleVCardManager), 0, (GInstanceInitFunc) gabble_vcard_manager_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleVCardManager"), &g_define_type_info, (GTypeFlags) 0); { {} ; } } return g_define_type_id; } ;
       
    92 		
       
    93 #else
       
    94 
       
    95 	static guint signals[LAST_SIGNAL] = {0};
       
    96     
       
    97     static const gchar *NO_ALIAS = "none";
       
    98 
       
    99 #endif
       
   100 
       
   101 
       
   102 /* Properties */
       
   103 enum
       
   104 {
       
   105   PROP_CONNECTION = 1,
       
   106   LAST_PROPERTY
       
   107 };
       
   108 
       
   109 
       
   110 
       
   111 typedef struct _GabbleVCardManagerPrivate GabbleVCardManagerPrivate;
       
   112 struct _GabbleVCardManagerPrivate
       
   113 {
       
   114   GabbleConnection *connection;
       
   115   GList *requests;
       
   116   gboolean dispose_has_run;
       
   117 };
       
   118 
       
   119 struct _GabbleVCardManagerRequest
       
   120 {
       
   121   GabbleVCardManager *manager;
       
   122   guint timer_id;
       
   123   guint timeout;
       
   124 
       
   125   GabbleHandle handle;
       
   126   gchar **edit_args;
       
   127 
       
   128   GabbleVCardManagerCb callback;
       
   129   gpointer user_data;
       
   130   GObject *bound_object;
       
   131 };
       
   132 
       
   133 GQuark
       
   134 gabble_vcard_manager_error_quark (void)
       
   135 {
       
   136 
       
   137 #ifndef EMULATOR
       
   138   static GQuark quark1 = 0;
       
   139 #endif
       
   140   
       
   141   if (!quark1)
       
   142     quark1 = g_quark_from_static_string ("gabble-vcard-manager-error");
       
   143   return quark1;
       
   144 }
       
   145 
       
   146 GQuark
       
   147 gabble_vcard_manager_cache_quark (void)
       
   148 {
       
   149 
       
   150 #ifndef EMULATOR
       
   151   static GQuark quark2 = 0;
       
   152 #endif
       
   153   
       
   154   if (!quark2)
       
   155     quark2 = g_quark_from_static_string ("gabble-vcard-manager-cache");
       
   156   return quark2;
       
   157 }
       
   158 
       
   159 #define GABBLE_VCARD_MANAGER_GET_PRIVATE(o)     ((GabbleVCardManagerPrivate*)((o)->priv));
       
   160 
       
   161 static void
       
   162 gabble_vcard_manager_init (GabbleVCardManager *obj)
       
   163 {
       
   164   GabbleVCardManagerPrivate *priv =
       
   165      G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_VCARD_MANAGER, GabbleVCardManagerPrivate);
       
   166   obj->priv = priv;
       
   167 
       
   168 }
       
   169 
       
   170 static void gabble_vcard_manager_set_property (GObject *object, guint property_id,
       
   171     const GValue *value, GParamSpec *pspec);
       
   172 static void gabble_vcard_manager_get_property (GObject *object, guint property_id,
       
   173     GValue *value, GParamSpec *pspec);
       
   174 static void gabble_vcard_manager_dispose (GObject *object);
       
   175 static void gabble_vcard_manager_finalize (GObject *object);
       
   176 
       
   177 static void
       
   178 gabble_vcard_manager_class_init (GabbleVCardManagerClass *gabble_vcard_manager_class)
       
   179 {
       
   180   GObjectClass *object_class = G_OBJECT_CLASS (gabble_vcard_manager_class);
       
   181   GParamSpec *param_spec;
       
   182 
       
   183   g_type_class_add_private (gabble_vcard_manager_class, sizeof (GabbleVCardManagerPrivate));
       
   184 
       
   185   object_class->get_property = gabble_vcard_manager_get_property;
       
   186   object_class->set_property = gabble_vcard_manager_set_property;
       
   187 
       
   188   object_class->dispose = gabble_vcard_manager_dispose;
       
   189   object_class->finalize = gabble_vcard_manager_finalize;
       
   190 
       
   191   param_spec = g_param_spec_object ("connection", "GabbleConnection object",
       
   192                                     "Gabble connection object that owns this "
       
   193                                     "vCard lookup helper object.",
       
   194                                     GABBLE_TYPE_CONNECTION,
       
   195                                     G_PARAM_CONSTRUCT_ONLY |
       
   196                                     G_PARAM_READWRITE |
       
   197                                     G_PARAM_STATIC_NICK |
       
   198                                     G_PARAM_STATIC_BLURB);
       
   199   g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
       
   200 
       
   201   /* signal definitions */
       
   202 
       
   203   signals[NICKNAME_UPDATE] =
       
   204     g_signal_new ("nickname-update",
       
   205                   G_TYPE_FROM_CLASS (gabble_vcard_manager_class),
       
   206                   G_SIGNAL_RUN_LAST,
       
   207                   0,
       
   208                   NULL, NULL,
       
   209                   g_cclosure_marshal_VOID__UINT,
       
   210                   G_TYPE_NONE, 1, G_TYPE_UINT);
       
   211 
       
   212 }
       
   213 
       
   214 static void
       
   215 gabble_vcard_manager_get_property (GObject    *object,
       
   216                                    guint       property_id,
       
   217                                    GValue     *value,
       
   218                                    GParamSpec *pspec)
       
   219 {
       
   220   GabbleVCardManager *chan = GABBLE_VCARD_MANAGER (object);
       
   221   GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (chan);
       
   222 
       
   223   switch (property_id) {
       
   224     case PROP_CONNECTION:
       
   225       g_value_set_object (value, priv->connection);
       
   226       break;
       
   227     default:
       
   228       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   229       break;
       
   230   }
       
   231 }
       
   232 
       
   233 static void
       
   234 gabble_vcard_manager_set_property (GObject     *object,
       
   235                                    guint        property_id,
       
   236                                    const GValue *value,
       
   237                                    GParamSpec   *pspec)
       
   238 {
       
   239   GabbleVCardManager *chan = GABBLE_VCARD_MANAGER (object);
       
   240   GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (chan);
       
   241 
       
   242   switch (property_id) {
       
   243     case PROP_CONNECTION:
       
   244       priv->connection = g_value_get_object (value);
       
   245       break;
       
   246     default:
       
   247       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   248       break;
       
   249   }
       
   250 }
       
   251 
       
   252 static void cancel_request (GabbleVCardManagerRequest *request);
       
   253 
       
   254 void
       
   255 gabble_vcard_manager_dispose (GObject *object)
       
   256 {
       
   257   DBusGProxy *bus_proxy;
       
   258   GabbleVCardManager *self = GABBLE_VCARD_MANAGER (object);
       
   259   GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self);
       
   260   bus_proxy = tp_get_bus_proxy ();
       
   261 
       
   262   if (priv->dispose_has_run)
       
   263     return;
       
   264 
       
   265   priv->dispose_has_run = TRUE;
       
   266 
       
   267   /* cancel request removes the element from the list after cancelling */
       
   268   while (priv->requests)
       
   269     cancel_request (priv->requests->data);
       
   270 
       
   271   if (G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->dispose)
       
   272     G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->dispose (object);
       
   273 }
       
   274 
       
   275 void
       
   276 gabble_vcard_manager_finalize (GObject *object)
       
   277 {
       
   278   G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->finalize (object);
       
   279 }
       
   280 
       
   281 static void
       
   282 status_changed_cb (GObject *object,
       
   283                    guint status,
       
   284                    guint reason,
       
   285                    gpointer user_data)
       
   286 {
       
   287   GabbleVCardManager *self = GABBLE_VCARD_MANAGER (user_data);
       
   288   GabbleConnection *conn = GABBLE_CONNECTION (object);
       
   289 
       
   290   if (status == TP_CONN_STATUS_CONNECTED)
       
   291     {
       
   292       gchar *alias;
       
   293       GabbleConnectionAliasSource alias_src;
       
   294 
       
   295       /* if we have a better alias, patch it into our vCard on the server */
       
   296       alias_src = _gabble_connection_get_cached_alias (conn,
       
   297                                                        conn->self_handle,
       
   298                                                        &alias);
       
   299       if (alias_src > GABBLE_CONNECTION_ALIAS_FROM_VCARD)
       
   300         {
       
   301           /* ignore errors, just kick off the request in the background */
       
   302           gabble_vcard_manager_edit (self, 0, NULL, NULL, G_OBJECT (conn),
       
   303                                      NULL, "NICKNAME", alias, NULL);
       
   304         }
       
   305       else
       
   306         {
       
   307           /* find out our own alias, so it's in the cache; again,
       
   308            * there's nothing useful we can do with errors really
       
   309            */
       
   310           gabble_vcard_manager_request (self, conn->self_handle,
       
   311                                         0, NULL, NULL, NULL, NULL);
       
   312         }
       
   313 
       
   314       g_free(alias);
       
   315     }
       
   316 }
       
   317 
       
   318 /**
       
   319  * gabble_vcard_manager_new:
       
   320  * @conn: The #GabbleConnection to use for vCard lookup
       
   321  *
       
   322  * Creates an object to use for Jabber vCard lookup (JEP 0054).
       
   323  * There should be one of these per connection
       
   324  */
       
   325 GabbleVCardManager *
       
   326 gabble_vcard_manager_new (GabbleConnection *conn)
       
   327 {
       
   328   GabbleVCardManager *self;
       
   329 
       
   330   g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL);
       
   331 
       
   332   self = GABBLE_VCARD_MANAGER (g_object_new (GABBLE_TYPE_VCARD_MANAGER, "connection", conn, NULL));
       
   333   g_signal_connect (conn, "status-changed",
       
   334                     G_CALLBACK (status_changed_cb), self);
       
   335   return self;
       
   336 }
       
   337 
       
   338 static void notify_delete_request (gpointer data, GObject *obj);
       
   339 
       
   340 static void
       
   341 delete_request (GabbleVCardManagerRequest *request)
       
   342 {
       
   343   GabbleVCardManager *manager = request->manager;
       
   344   GabbleVCardManagerPrivate *priv;
       
   345 
       
   346   gabble_debug (DEBUG_FLAG, "Discarding request %p", request);
       
   347 
       
   348   g_assert (NULL != request);
       
   349   g_assert (GABBLE_IS_VCARD_MANAGER (manager));
       
   350 
       
   351   priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager);
       
   352 
       
   353   g_assert (NULL != g_list_find (priv->requests, request));
       
   354 
       
   355   priv->requests = g_list_remove (priv->requests, request);
       
   356 
       
   357   if (NULL != request->bound_object)
       
   358     {
       
   359       g_object_weak_unref (request->bound_object, notify_delete_request, request);
       
   360     }
       
   361 
       
   362   if (0 != request->timer_id)
       
   363     {
       
   364       g_source_remove (request->timer_id);
       
   365     }
       
   366 
       
   367   gabble_handle_unref (priv->connection->handles, TP_HANDLE_TYPE_CONTACT,
       
   368                        request->handle);
       
   369   g_strfreev (request->edit_args);
       
   370 
       
   371   g_free (request);
       
   372 }
       
   373 
       
   374 static gboolean
       
   375 timeout_request (gpointer data)
       
   376 {
       
   377   GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest*) data;
       
   378   GError *err;
       
   379   g_return_val_if_fail (data != NULL, FALSE);
       
   380 
       
   381   err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, GABBLE_VCARD_MANAGER_ERROR_TIMEOUT,
       
   382       "Request timed out");
       
   383   gabble_debug (DEBUG_FLAG, "Request %p timed out, notifying callback %p",
       
   384          request, request->callback);
       
   385   if (request->callback)
       
   386     {
       
   387       (request->callback)(request->manager, request, request->handle,
       
   388                           NULL, err, request->user_data);
       
   389     }
       
   390   g_error_free (err);
       
   391 
       
   392   request->timer_id = 0;
       
   393   delete_request (request);
       
   394   return FALSE;
       
   395 }
       
   396 
       
   397 static void
       
   398 cancel_request (GabbleVCardManagerRequest *request)
       
   399 {
       
   400   GError *err;
       
   401 
       
   402   g_assert (request != NULL);
       
   403 
       
   404   err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, GABBLE_VCARD_MANAGER_ERROR_CANCELLED,
       
   405       "Request cancelled");
       
   406   gabble_debug (DEBUG_FLAG, "Request %p cancelled, notifying callback %p",
       
   407          request, request->callback);
       
   408   if (request->callback)
       
   409     {
       
   410       (request->callback)(request->manager, request, request->handle,
       
   411                           NULL, err, request->user_data);
       
   412     }
       
   413   g_error_free (err);
       
   414 
       
   415   delete_request (request);
       
   416 }
       
   417 
       
   418 static void
       
   419 observe_vcard (GabbleConnection *conn, GabbleVCardManager *manager,
       
   420                GabbleHandle handle, LmMessageNode *vcard_node)
       
   421 {
       
   422   LmMessageNode *nick_node = lm_message_node_get_child (vcard_node,
       
   423                                                         "NICKNAME");
       
   424 
       
   425   gabble_debug (DEBUG_FLAG, "Observing vCard for %u", handle);
       
   426   NODE_DEBUG(vcard_node, "their vCard is");
       
   427 
       
   428   if (nick_node)
       
   429     {
       
   430       const gchar *nick = lm_message_node_get_value (nick_node);
       
   431 
       
   432       gabble_debug (DEBUG_FLAG, "%u has <NICKNAME> \"%s\"", handle, nick ? nick : "(null)");
       
   433 
       
   434       if (nick && *nick)
       
   435         {
       
   436           /* nicknames are comma-separated, let's use the first one */
       
   437           gchar **bits = g_strsplit (nick, ",", 2);
       
   438 
       
   439           if (bits[0])
       
   440             {
       
   441               gchar *alias = g_strdup (bits[0]);
       
   442 
       
   443               gabble_debug (DEBUG_FLAG, "... using \"%s\" as their alias", alias);
       
   444 
       
   445               g_signal_emit (G_OBJECT (manager), signals[NICKNAME_UPDATE],
       
   446                              0, handle);
       
   447               if (!gabble_handle_set_qdata (conn->handles,
       
   448                                             TP_HANDLE_TYPE_CONTACT,
       
   449                                             handle,
       
   450                                             gabble_vcard_manager_cache_quark(),
       
   451                                             alias, g_free))
       
   452                 {
       
   453                   gabble_debug (DEBUG_FLAG, "failed to cache their alias");
       
   454                   g_free (alias);
       
   455                 }
       
   456 
       
   457             }
       
   458 
       
   459           g_strfreev (bits);
       
   460         }
       
   461     }
       
   462   else
       
   463     {
       
   464       const gchar *fn = NULL;
       
   465       /* let's see if they have a FN (formatted name) instead */
       
   466       nick_node = lm_message_node_get_child (vcard_node, "FN");
       
   467       if (nick_node)
       
   468         fn = lm_message_node_get_value (nick_node);
       
   469       gabble_debug (DEBUG_FLAG, "%u has no <NICKNAME>, but has <FN> \"%s\"", handle,
       
   470              fn ? fn : "(null)");
       
   471       if (fn && *fn)
       
   472         {
       
   473           gchar *alias = g_strdup (fn);
       
   474 
       
   475           gabble_debug (DEBUG_FLAG, "... using \"%s\" as their alias", alias);
       
   476 
       
   477           g_signal_emit (G_OBJECT (manager), signals[NICKNAME_UPDATE],
       
   478                          0, handle);
       
   479           if (!gabble_handle_set_qdata (conn->handles,
       
   480                                         TP_HANDLE_TYPE_CONTACT,
       
   481                                         handle,
       
   482                                         gabble_vcard_manager_cache_quark(),
       
   483                                         alias, g_free))
       
   484             {
       
   485               gabble_debug (DEBUG_FLAG, "failed to cache their alias");
       
   486               g_free (alias);
       
   487             }
       
   488         }
       
   489       else
       
   490         {
       
   491           /* remember that they don't have an alias */
       
   492           if (!gabble_handle_set_qdata (conn->handles,
       
   493                                         TP_HANDLE_TYPE_CONTACT,
       
   494                                         handle,
       
   495                                         gabble_vcard_manager_cache_quark (),
       
   496                                         (gchar *) NO_ALIAS, NULL))
       
   497             gabble_debug (DEBUG_FLAG, "failed to cache their lack of vcard alias");
       
   498         }
       
   499 
       
   500     }
       
   501 }
       
   502 
       
   503 static GabbleVCardManagerRequest *request_send (GabbleVCardManagerRequest *,
       
   504                                                 LmMessageNode *replacement,
       
   505                                                 const gchar *jid,
       
   506                                                 GError **);
       
   507 
       
   508 static LmHandlerResult
       
   509 replace_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
       
   510                   LmMessage *reply_msg, GObject *object, gpointer user_data)
       
   511 {
       
   512   LmMessageNode *vcard_node = NULL;
       
   513   GError *err = NULL;
       
   514   GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest*) user_data;
       
   515   GabbleVCardManager *manager = GABBLE_VCARD_MANAGER (object);
       
   516   GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager);
       
   517 
       
   518   g_assert (request);
       
   519 
       
   520   gabble_debug (DEBUG_FLAG, "Replace request got a reply: conn@%p, sent_msg@%p, reply_msg@%p, "
       
   521          "bound object@%p, request@%p", conn, sent_msg, reply_msg, object,
       
   522          user_data);
       
   523 
       
   524   if (!g_list_find (priv->requests, request))
       
   525     {
       
   526       gabble_debug (DEBUG_FLAG, "I don't care about that request any more");
       
   527       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   528     }
       
   529 
       
   530   if (lm_message_get_sub_type (reply_msg) == LM_MESSAGE_SUB_TYPE_ERROR)
       
   531     {
       
   532       LmMessageNode *error_node;
       
   533 
       
   534       error_node = lm_message_node_get_child (reply_msg->node, "error");
       
   535       if (error_node)
       
   536         {
       
   537           err = gabble_xmpp_error_to_g_error (
       
   538               gabble_xmpp_error_from_node (error_node));
       
   539         }
       
   540 
       
   541       if (err == NULL)
       
   542         {
       
   543           err = g_error_new (GABBLE_VCARD_MANAGER_ERROR,
       
   544                              GABBLE_VCARD_MANAGER_ERROR_UNKNOWN,
       
   545                              "an unknown error occurred");
       
   546         }
       
   547     }
       
   548   else
       
   549     {
       
   550       vcard_node = lm_message_node_get_child (sent_msg->node, "vCard");
       
   551     }
       
   552 
       
   553   gabble_debug (DEBUG_FLAG, "Request %p %s, notifying callback %p", request,
       
   554          err ? "failed" : "succeeded", request->callback);
       
   555   if (request->callback)
       
   556     {
       
   557       request->callback (request->manager, request, request->handle,
       
   558                          vcard_node, err, request->user_data);
       
   559     }
       
   560   delete_request (request);
       
   561 
       
   562   if (err)
       
   563     g_error_free (err);
       
   564 
       
   565   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   566 }
       
   567 
       
   568 static LmHandlerResult
       
   569 request_reply_cb (GabbleConnection *conn,
       
   570                   LmMessage *sent_msg,
       
   571                   LmMessage *reply_msg,
       
   572                   GObject *object,
       
   573                   gpointer user_data)
       
   574 {
       
   575   LmMessageNode *vcard_node = NULL;
       
   576   GError *err = NULL;
       
   577   GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest*) user_data;
       
   578   GabbleVCardManager *manager = GABBLE_VCARD_MANAGER (object);
       
   579   GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager);
       
   580 
       
   581   g_assert (request);
       
   582 
       
   583   gabble_debug (DEBUG_FLAG, "Fetch request got a reply: conn@%p, sent_msg@%p, reply_msg@%p, "
       
   584          "bound object@%p, request@%p", conn, sent_msg, reply_msg, object,
       
   585          user_data);
       
   586 
       
   587   if (!g_list_find (priv->requests, request))
       
   588     {
       
   589       gabble_debug (DEBUG_FLAG, "I don't care about that request any more");
       
   590       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   591     }
       
   592 
       
   593 
       
   594   if (lm_message_get_sub_type (reply_msg) == LM_MESSAGE_SUB_TYPE_ERROR)
       
   595     {
       
   596       LmMessageNode *error_node;
       
   597 
       
   598       error_node = lm_message_node_get_child (reply_msg->node, "error");
       
   599       if (error_node)
       
   600         {
       
   601           err = gabble_xmpp_error_to_g_error (
       
   602               gabble_xmpp_error_from_node (error_node));
       
   603         }
       
   604 
       
   605       if (err == NULL)
       
   606         {
       
   607           err = g_error_new (GABBLE_VCARD_MANAGER_ERROR,
       
   608                              GABBLE_VCARD_MANAGER_ERROR_UNKNOWN,
       
   609                              "an unknown error occurred");
       
   610         }
       
   611     }
       
   612   else
       
   613     {
       
   614       vcard_node = lm_message_node_get_child (reply_msg->node, "vCard");
       
   615 
       
   616       if (NULL == vcard_node)
       
   617         {
       
   618           gabble_debug (DEBUG_FLAG, "successful lookup response contained no <vCard> node, "
       
   619               "creating an empty one");
       
   620 
       
   621           vcard_node = lm_message_node_add_child (reply_msg->node, "vCard",
       
   622               NULL);
       
   623           lm_message_node_set_attribute (vcard_node, "xmlns", NS_VCARD_TEMP);
       
   624         }
       
   625 
       
   626       observe_vcard (conn, manager, request->handle, vcard_node);
       
   627     }
       
   628 
       
   629   if (vcard_node && request->edit_args)
       
   630     {
       
   631       gchar **ptr;
       
   632       for (ptr = request->edit_args; *ptr; ptr++)
       
   633         {
       
   634           gchar *key = *ptr;
       
   635           gchar *value = *(++ptr);
       
   636           LmMessageNode *node;
       
   637 
       
   638           if (!value)
       
   639             {
       
   640               /* oops, someone passed in an odd number of args. */
       
   641               g_assert_not_reached ();
       
   642               break;
       
   643             }
       
   644 
       
   645           node = lm_message_node_get_child (vcard_node, key);
       
   646           if (node)
       
   647             {
       
   648               lm_message_node_set_value (node, value);
       
   649             }
       
   650           else
       
   651             {
       
   652               node = lm_message_node_add_child (vcard_node, key, value);
       
   653             }
       
   654         }
       
   655 
       
   656       request_send (request, vcard_node, NULL, &err);
       
   657 
       
   658       if (err)
       
   659         {
       
   660           gabble_debug (DEBUG_FLAG, "Request %p failed, notifying callback %p",
       
   661                  request, request->callback);
       
   662           if (request->callback)
       
   663           {
       
   664             request->callback (request->manager, request, request->handle,
       
   665                                NULL, err, request->user_data);
       
   666           }
       
   667         }
       
   668       else
       
   669         {
       
   670           gabble_debug (DEBUG_FLAG, "Request %p fetch succeeded", request);
       
   671           /* early return to avoid deleting the request */
       
   672           return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   673         }
       
   674     }
       
   675   else
       
   676     {
       
   677       gabble_debug (DEBUG_FLAG, "Request %p %s, notifying callback %p",
       
   678              request, err ? "failed" : "succeeded", request->callback);
       
   679       if (request->callback)
       
   680         {
       
   681           request->callback (request->manager, request, request->handle,
       
   682                              vcard_node, err, request->user_data);
       
   683         }
       
   684     }
       
   685 
       
   686   delete_request (request);
       
   687 
       
   688   if (err)
       
   689     g_error_free (err);
       
   690 
       
   691   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   692 }
       
   693 
       
   694 /* If @replacement is NULL sends a request, calling request_reply_cb when
       
   695  * it returns.
       
   696  *
       
   697  * Otherwise steals its children and sends an update, calling
       
   698  * replace_reply_cb when it returns.
       
   699  *
       
   700  * Frees the @request on error, returns it on success. */
       
   701 static GabbleVCardManagerRequest *
       
   702 request_send (GabbleVCardManagerRequest *request,
       
   703               LmMessageNode *replacement,
       
   704               const gchar *jid,
       
   705               GError **error)
       
   706 {
       
   707   LmMessage *msg;
       
   708   LmMessageNode *lm_node;
       
   709   GabbleVCardManager *self = request->manager;
       
   710   GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self);
       
   711 
       
   712   gabble_debug (DEBUG_FLAG, "Sending off request %p to %s for %s", request,
       
   713          replacement ? "replace vCard" : "retrieve vCard",
       
   714          jid ? jid : "myself");
       
   715   msg = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ,
       
   716                                            (replacement
       
   717                                                 ? LM_MESSAGE_SUB_TYPE_SET
       
   718                                                 : LM_MESSAGE_SUB_TYPE_GET));
       
   719   lm_node = lm_message_node_add_child (msg->node, "vCard", NULL);
       
   720   lm_message_node_set_attribute (lm_node, "xmlns", NS_VCARD_TEMP);
       
   721 
       
   722   if (replacement)
       
   723     lm_message_node_steal_children (lm_node, replacement);
       
   724 
       
   725   if (! _gabble_connection_send_with_reply (priv->connection, msg,
       
   726         (replacement ? replace_reply_cb : request_reply_cb),
       
   727         G_OBJECT(self), request, error))
       
   728     {
       
   729       delete_request (request);
       
   730       lm_message_unref (msg);
       
   731       return NULL;
       
   732     }
       
   733   else
       
   734     {
       
   735       if (0 == request->timer_id)
       
   736         {
       
   737           request->timer_id =
       
   738               g_timeout_add (request->timeout, timeout_request, request);
       
   739         }
       
   740       lm_message_unref (msg);
       
   741       return request;
       
   742     }
       
   743 }
       
   744 
       
   745 static void
       
   746 notify_delete_request (gpointer data, GObject *obj)
       
   747 {
       
   748   GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest *) data;
       
   749   request->bound_object = NULL;
       
   750   delete_request (request);
       
   751 }
       
   752 
       
   753 /* Request the vCard for the given handle. When it arrives, call the given
       
   754  * callback.
       
   755  *
       
   756  * The callback may be NULL if you just want the side-effect of this
       
   757  * operation, which is to update the cached alias.
       
   758  */
       
   759 GabbleVCardManagerRequest *
       
   760 gabble_vcard_manager_request (GabbleVCardManager *self,
       
   761                               GabbleHandle handle,
       
   762                               guint timeout,
       
   763                               GabbleVCardManagerCb callback,
       
   764                               gpointer user_data,
       
   765                               GObject *object,
       
   766                               GError **error)
       
   767 {
       
   768   GabbleVCardManagerRequest *request;
       
   769   const gchar *jid;
       
   770   GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self);
       
   771 
       
   772   if (timeout == 0)
       
   773     timeout = DEFAULT_REQUEST_TIMEOUT;
       
   774 
       
   775   request = g_new0 (GabbleVCardManagerRequest, 1);
       
   776   gabble_debug (DEBUG_FLAG, "Created request %p to retrieve <%u>'s vCard",
       
   777          request, handle);
       
   778   request->timeout = timeout;
       
   779   request->manager = self;
       
   780   gabble_handle_ref (priv->connection->handles, TP_HANDLE_TYPE_CONTACT,
       
   781                      handle);
       
   782   request->handle = handle;
       
   783   request->callback = callback;
       
   784   request->user_data = user_data;
       
   785   request->bound_object = object;
       
   786 
       
   787   if (NULL != object)
       
   788     g_object_weak_ref (object, notify_delete_request, request);
       
   789 
       
   790   priv->requests = g_list_prepend (priv->requests, request);
       
   791   if (handle == priv->connection->self_handle) 
       
   792     {
       
   793       jid = NULL;
       
   794     }
       
   795   else
       
   796     {
       
   797       jid = gabble_handle_inspect (priv->connection->handles,
       
   798                                    TP_HANDLE_TYPE_CONTACT, handle);
       
   799     }
       
   800 
       
   801   return request_send (request, NULL, jid, error);
       
   802 }
       
   803 
       
   804 GabbleVCardManagerRequest *
       
   805 gabble_vcard_manager_replace (GabbleVCardManager *self,
       
   806                               LmMessageNode *replacement,
       
   807                               guint timeout,
       
   808                               GabbleVCardManagerCb callback,
       
   809                               gpointer user_data,
       
   810                               GObject *object,
       
   811                               GError **error)
       
   812 {
       
   813   GabbleVCardManagerRequest *request;
       
   814   GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self);
       
   815 
       
   816   if (timeout == 0)
       
   817     timeout = DEFAULT_REQUEST_TIMEOUT;
       
   818 
       
   819   request = g_new0 (GabbleVCardManagerRequest, 1);
       
   820   gabble_debug (DEBUG_FLAG, "Created request %p to replace my vCard",
       
   821          request);
       
   822   request->timeout = timeout;
       
   823   request->manager = self;
       
   824   gabble_handle_ref (priv->connection->handles, TP_HANDLE_TYPE_CONTACT,
       
   825                      priv->connection->self_handle);
       
   826   request->handle = priv->connection->self_handle;
       
   827   request->callback = callback;
       
   828   request->user_data = user_data;
       
   829   request->bound_object = object;
       
   830 
       
   831   if (NULL != object)
       
   832     g_object_weak_ref (object, notify_delete_request, request);
       
   833 
       
   834   priv->requests = g_list_prepend (priv->requests, request);
       
   835 
       
   836   return request_send (request, replacement, NULL, error);
       
   837 }
       
   838 
       
   839 GabbleVCardManagerRequest *
       
   840 gabble_vcard_manager_edit (GabbleVCardManager *self,
       
   841                            guint timeout,
       
   842                            GabbleVCardManagerCb callback,
       
   843                            gpointer user_data,
       
   844                            GObject *object,
       
   845                            GError **error,
       
   846                            ...)
       
   847 {
       
   848   va_list ap;
       
   849   size_t i, argc;
       
   850   GabbleVCardManagerRequest *request;
       
   851   GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self);
       
   852 
       
   853   if (timeout == 0)
       
   854     timeout = DEFAULT_REQUEST_TIMEOUT;
       
   855 
       
   856   request = g_new0 (GabbleVCardManagerRequest, 1);
       
   857   gabble_debug (DEBUG_FLAG, "Created request %p to edit my vCard", request);
       
   858   request->timeout = timeout;
       
   859   request->manager = self;
       
   860   gabble_handle_ref (priv->connection->handles, TP_HANDLE_TYPE_CONTACT,
       
   861                      priv->connection->self_handle);
       
   862   request->handle = priv->connection->self_handle;
       
   863   request->callback = callback;
       
   864   request->user_data = user_data;
       
   865   request->bound_object = object;
       
   866 
       
   867   if (NULL != object)
       
   868     g_object_weak_ref (object, notify_delete_request, request);
       
   869 
       
   870   priv->requests = g_list_prepend (priv->requests, request);
       
   871 
       
   872   argc = 0;
       
   873   va_start (ap, error);
       
   874   while (va_arg (ap, const gchar *) != NULL)
       
   875     {
       
   876       argc++;
       
   877     }
       
   878   va_end (ap);
       
   879   g_return_val_if_fail (argc % 2 == 0, NULL);
       
   880 
       
   881   request->edit_args = g_new (gchar *, argc + 1);
       
   882 
       
   883   va_start (ap, error);
       
   884   for (i = 0; i < argc; i++)
       
   885     {
       
   886       request->edit_args[i] = g_strdup (va_arg (ap, const gchar *));
       
   887     }
       
   888   request->edit_args[argc] = NULL;
       
   889   va_end (ap);
       
   890 
       
   891   return request_send (request, NULL, NULL, error);
       
   892 }
       
   893 
       
   894 void
       
   895 gabble_vcard_manager_cancel_request (GabbleVCardManager *manager,
       
   896                                      GabbleVCardManagerRequest *request)
       
   897 {
       
   898   GabbleVCardManagerPrivate *priv;
       
   899 
       
   900   g_return_if_fail (GABBLE_IS_VCARD_MANAGER (manager));
       
   901   g_return_if_fail (NULL != request);
       
   902 
       
   903   priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager);
       
   904 
       
   905   g_return_if_fail (NULL != g_list_find (priv->requests, request));
       
   906 
       
   907   cancel_request (request);
       
   908 }
       
   909 
       
   910 /**
       
   911  * Return the cached alias derived from the vCard for the given handle,
       
   912  * if any. If there is no cached alias, return NULL.
       
   913  */
       
   914 const gchar *
       
   915 gabble_vcard_manager_get_cached_alias (GabbleVCardManager *manager,
       
   916                                        GabbleHandle handle)
       
   917 {
       
   918   GabbleVCardManagerPrivate *priv;
       
   919   const gchar *s;
       
   920 
       
   921   g_return_val_if_fail (GABBLE_IS_VCARD_MANAGER (manager), NULL);
       
   922 
       
   923   priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager);
       
   924 
       
   925   s = gabble_handle_get_qdata (priv->connection->handles,
       
   926                                TP_HANDLE_TYPE_CONTACT,
       
   927                                handle,
       
   928                                gabble_vcard_manager_cache_quark());
       
   929 
       
   930   if (s == NO_ALIAS)
       
   931     s = NULL;
       
   932 
       
   933   gabble_debug (DEBUG_FLAG, "Cached alias for %u is \"%s\"", handle, s ? s : "(null)");
       
   934   return s;
       
   935 }
       
   936 
       
   937 /**
       
   938  * Return TRUE if we've tried looking up an alias for this handle before.
       
   939  */
       
   940 gboolean
       
   941 gabble_vcard_manager_has_cached_alias (GabbleVCardManager *manager,
       
   942                                        GabbleHandle handle)
       
   943 {
       
   944   GabbleVCardManagerPrivate *priv;
       
   945   gpointer p;
       
   946 
       
   947   g_return_val_if_fail (GABBLE_IS_VCARD_MANAGER (manager), FALSE);
       
   948 
       
   949   priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager);
       
   950   p = gabble_handle_get_qdata (priv->connection->handles,
       
   951                                TP_HANDLE_TYPE_CONTACT,
       
   952                                handle,
       
   953                                gabble_vcard_manager_cache_quark());
       
   954   return p != NULL;
       
   955 }
       
   956