telepathygabble/src/group-mixin.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * group-mixin.c - Source for GabbleGroupMixin
       
     3  * Copyright (C) 2006 Collabora Ltd.
       
     4  * 
       
     5  *   @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
       
     6  *   @author Robert McQueen <robert.mcqueen@collabora.co.uk>
       
     7  *
       
     8  * This library is free software; you can redistribute it and/or
       
     9  * modify it under the terms of the GNU Lesser General Public
       
    10  * License as published by the Free Software Foundation; either
       
    11  * version 2.1 of the License, or (at your option) any later version.
       
    12  *
       
    13  * This library is distributed in the hope that it will be useful,
       
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    16  * Lesser General Public License for more details.
       
    17  *
       
    18  * You should have received a copy of the GNU Lesser General Public
       
    19  * License along with this library; if not, write to the Free Software
       
    20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    21  */
       
    22 
       
    23 #include <dbus/dbus-glib.h>
       
    24 #include <stdio.h>
       
    25 
       
    26 #include "ansi.h"
       
    27 #include "debug.h"
       
    28 #include "telepathy-errors.h"
       
    29 
       
    30 
       
    31 #include "group-mixin.h"
       
    32 #include "group-mixin-signals-marshal.h"
       
    33 
       
    34 #define DEBUG_FLAG GABBLE_DEBUG_GROUPS
       
    35 #ifdef DEBUG_FLAG
       
    36 //#define DEBUG(format, ...)
       
    37 #define DEBUGGING 0
       
    38 #define NODE_DEBUG(n, s)
       
    39 #endif /* DEBUG_FLAG */
       
    40 
       
    41 #ifdef EMULATOR
       
    42 #include "libgabble_wsd_solution.h"
       
    43 
       
    44 	GET_STATIC_VAR_FROM_TLS(offset_quark1,gabble_grp_mixin,GQuark)
       
    45 	#define offset_quark1 (*GET_WSD_VAR_NAME(offset_quark1,gabble_grp_mixin, s)())	
       
    46 	
       
    47 	GET_STATIC_VAR_FROM_TLS(offset_quark,gabble_grp_mixin,GQuark)
       
    48 	#define offset_quark (*GET_WSD_VAR_NAME(offset_quark,gabble_grp_mixin, s)())	
       
    49 	
       
    50 	
       
    51 #endif
       
    52 
       
    53 static const char *group_change_reason_str(guint reason)
       
    54 {
       
    55   switch (reason)
       
    56     {
       
    57     case TP_CHANNEL_GROUP_CHANGE_REASON_NONE:
       
    58       return "unspecified reason";
       
    59     case TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE:
       
    60       return "offline";
       
    61     case TP_CHANNEL_GROUP_CHANGE_REASON_KICKED:
       
    62       return "kicked";
       
    63     case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY:
       
    64       return "busy";
       
    65     case TP_CHANNEL_GROUP_CHANGE_REASON_INVITED:
       
    66       return "invited";
       
    67     case TP_CHANNEL_GROUP_CHANGE_REASON_BANNED:
       
    68       return "banned";
       
    69     default:
       
    70       return "(unknown reason code)";
       
    71     }
       
    72 }
       
    73 
       
    74 struct _GabbleGroupMixinPrivate {
       
    75     GabbleHandleSet *actors;
       
    76     GHashTable *handle_owners;
       
    77 };
       
    78 
       
    79 
       
    80 /**
       
    81  * gabble_group_mixin_class_get_offset_quark:
       
    82  *
       
    83  * Returns: the quark used for storing mixin offset on a GObjectClass
       
    84  */
       
    85 GQuark
       
    86 gabble_group_mixin_class_get_offset_quark ()
       
    87 {
       
    88 #ifndef EMULATOR
       
    89   static GQuark offset_quark1 = 0;
       
    90 #endif
       
    91   
       
    92   if (!offset_quark1)
       
    93     offset_quark1 = g_quark_from_static_string("GroupMixinClassOffsetQuark");
       
    94   return offset_quark1;
       
    95 }
       
    96 
       
    97 /**
       
    98  * gabble_group_mixin_get_offset_quark:
       
    99  *
       
   100  * Returns: the quark used for storing mixin offset on a GObject
       
   101  */
       
   102 GQuark
       
   103 gabble_group_mixin_get_offset_quark ()
       
   104 {
       
   105 #ifndef EMULATOR
       
   106   static GQuark offset_quark = 0;
       
   107 #endif
       
   108   
       
   109   if (!offset_quark)
       
   110     offset_quark = g_quark_from_static_string("GroupMixinOffsetQuark");
       
   111   return offset_quark;
       
   112 }
       
   113 
       
   114 void gabble_group_mixin_class_init (GObjectClass *obj_cls,
       
   115                                     glong offset,
       
   116                                     GabbleGroupMixinAddMemberFunc add_func,
       
   117                                     GabbleGroupMixinRemMemberFunc rem_func)
       
   118 {
       
   119   GabbleGroupMixinClass *mixin_cls;
       
   120 
       
   121   g_assert (G_IS_OBJECT_CLASS (obj_cls));
       
   122 
       
   123   g_type_set_qdata (G_OBJECT_CLASS_TYPE (obj_cls),
       
   124                     GABBLE_GROUP_MIXIN_CLASS_OFFSET_QUARK,
       
   125                     GINT_TO_POINTER (offset));
       
   126 
       
   127   mixin_cls = GABBLE_GROUP_MIXIN_CLASS (obj_cls);
       
   128 
       
   129   mixin_cls->add_member = add_func;
       
   130   mixin_cls->remove_member = rem_func;
       
   131 
       
   132   mixin_cls->group_flags_changed_signal_id =
       
   133     g_signal_new ("group-flags-changed",
       
   134                   G_OBJECT_CLASS_TYPE (obj_cls),
       
   135                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   136                   0,
       
   137                   NULL, NULL,
       
   138                   group_mixin_marshal_VOID__UINT_UINT,
       
   139                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
       
   140 
       
   141   mixin_cls->members_changed_signal_id =
       
   142     g_signal_new ("members-changed",
       
   143                   G_OBJECT_CLASS_TYPE (obj_cls),
       
   144                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   145                   0,
       
   146                   NULL, NULL,
       
   147                   group_mixin_marshal_VOID__STRING_BOXED_BOXED_BOXED_BOXED_UINT_UINT,
       
   148                   G_TYPE_NONE, 7, G_TYPE_STRING, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, DBUS_TYPE_G_UINT_ARRAY, G_TYPE_UINT, G_TYPE_UINT);
       
   149 }
       
   150 
       
   151 void gabble_group_mixin_init (GObject *obj,
       
   152                               glong offset,
       
   153                               GabbleHandleRepo *handle_repo,
       
   154                               GabbleHandle self_handle)
       
   155 {
       
   156   GabbleGroupMixin *mixin;
       
   157 
       
   158   g_assert (G_IS_OBJECT (obj));
       
   159 
       
   160   g_type_set_qdata (G_OBJECT_TYPE (obj),
       
   161                     GABBLE_GROUP_MIXIN_OFFSET_QUARK,
       
   162                     GINT_TO_POINTER (offset));
       
   163 
       
   164   mixin = GABBLE_GROUP_MIXIN (obj);
       
   165 
       
   166   mixin->handle_repo = handle_repo;
       
   167   mixin->self_handle = self_handle;
       
   168 
       
   169   mixin->group_flags = 0;
       
   170 
       
   171   mixin->members = handle_set_new (handle_repo, TP_HANDLE_TYPE_CONTACT);
       
   172   mixin->local_pending = handle_set_new (handle_repo, TP_HANDLE_TYPE_CONTACT);
       
   173   mixin->remote_pending = handle_set_new (handle_repo, TP_HANDLE_TYPE_CONTACT);
       
   174 
       
   175   mixin->priv = g_new0 (GabbleGroupMixinPrivate, 1);
       
   176   mixin->priv->handle_owners = g_hash_table_new (g_direct_hash, g_direct_equal);
       
   177   mixin->priv->actors = handle_set_new (handle_repo, TP_HANDLE_TYPE_CONTACT);
       
   178 }
       
   179 
       
   180 static void
       
   181 handle_owners_foreach_unref (gpointer key,
       
   182                              gpointer value,
       
   183                              gpointer user_data)
       
   184 {
       
   185   GabbleGroupMixin *mixin = user_data;
       
   186 
       
   187   gabble_handle_unref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT,
       
   188                        GPOINTER_TO_UINT (key));
       
   189   gabble_handle_unref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT,
       
   190                        GPOINTER_TO_UINT (value));
       
   191 }
       
   192 
       
   193 void gabble_group_mixin_finalize (GObject *obj)
       
   194 {
       
   195   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   196 
       
   197   handle_set_destroy (mixin->priv->actors);
       
   198 
       
   199   g_hash_table_foreach (mixin->priv->handle_owners,
       
   200                         handle_owners_foreach_unref,
       
   201                         mixin);
       
   202 
       
   203   g_hash_table_destroy (mixin->priv->handle_owners);
       
   204 
       
   205   g_free (mixin->priv);
       
   206 
       
   207   handle_set_destroy (mixin->members);
       
   208   handle_set_destroy (mixin->local_pending);
       
   209   handle_set_destroy (mixin->remote_pending);
       
   210 }
       
   211 
       
   212 gboolean
       
   213 gabble_group_mixin_get_self_handle (GObject *obj, guint *ret, GError **error)
       
   214 {
       
   215   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   216 
       
   217   if (handle_set_is_member (mixin->members, mixin->self_handle) ||
       
   218       handle_set_is_member (mixin->local_pending, mixin->self_handle) ||
       
   219       handle_set_is_member (mixin->remote_pending, mixin->self_handle))
       
   220     {
       
   221       *ret = mixin->self_handle;
       
   222     }
       
   223   else
       
   224     {
       
   225       *ret = 0;
       
   226     }
       
   227 
       
   228   return TRUE;
       
   229 }
       
   230 
       
   231 gboolean
       
   232 gabble_group_mixin_get_group_flags (GObject *obj, guint *ret, GError **error)
       
   233 {
       
   234   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   235 
       
   236   *ret = mixin->group_flags;
       
   237 
       
   238   return TRUE;
       
   239 }
       
   240 
       
   241 gboolean
       
   242 gabble_group_mixin_add_members (GObject *obj, const GArray *contacts, const gchar *message, GError **error)
       
   243 {
       
   244   GabbleGroupMixinClass *mixin_cls = GABBLE_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj));
       
   245   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   246   guint i;
       
   247   GabbleHandle handle;
       
   248 
       
   249   /* reject invalid handles */
       
   250   if (!gabble_handles_are_valid (mixin->handle_repo,
       
   251                                  TP_HANDLE_TYPE_CONTACT,
       
   252                                  contacts,
       
   253                                  FALSE,
       
   254                                  error))
       
   255     return FALSE;
       
   256 
       
   257   /* check that adding is allowed by flags */
       
   258   for (i = 0; i < contacts->len; i++)
       
   259     {
       
   260       handle = g_array_index (contacts, GabbleHandle, i);
       
   261 
       
   262       if ((mixin->group_flags & TP_CHANNEL_GROUP_FLAG_CAN_ADD) == 0 &&
       
   263           !handle_set_is_member (mixin->local_pending, handle))
       
   264         {
       
   265           gabble_debug (DEBUG_FLAG, "handle %u cannot be added to members without GROUP_FLAG_CAN_ADD",
       
   266               handle);
       
   267 
       
   268           g_set_error (error, TELEPATHY_ERRORS, PermissionDenied,
       
   269               "handle %u cannot be added to members without GROUP_FLAG_CAN_ADD",
       
   270               handle);
       
   271 
       
   272           return FALSE;
       
   273         }
       
   274     }
       
   275 
       
   276   /* add handle by handle */
       
   277   for (i = 0; i < contacts->len; i++)
       
   278     {
       
   279       handle = g_array_index (contacts, GabbleHandle, i);
       
   280 
       
   281       if (handle_set_is_member (mixin->members, handle))
       
   282         {
       
   283           gabble_debug (DEBUG_FLAG, "handle %u is already a member, skipping", handle);
       
   284 
       
   285           continue;
       
   286         }
       
   287 
       
   288       if (!mixin_cls->add_member (obj, handle, message, error))
       
   289         {
       
   290           return FALSE;
       
   291         }
       
   292     }
       
   293 
       
   294   return TRUE;
       
   295 }
       
   296 
       
   297 gboolean
       
   298 gabble_group_mixin_remove_members (GObject *obj, const GArray *contacts, const gchar *message, GError **error)
       
   299 {
       
   300   GabbleGroupMixinClass *mixin_cls = GABBLE_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj));
       
   301   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   302   guint i;
       
   303   GabbleHandle handle;
       
   304 
       
   305   /* reject invalid handles */
       
   306   if (!gabble_handles_are_valid (mixin->handle_repo,
       
   307                                  TP_HANDLE_TYPE_CONTACT,
       
   308                                  contacts,
       
   309                                  FALSE,
       
   310                                  error))
       
   311     return FALSE;
       
   312 
       
   313   /* check removing is allowed by flags */
       
   314   for (i = 0; i < contacts->len; i++)
       
   315     {
       
   316       handle = g_array_index (contacts, GabbleHandle, i);
       
   317 
       
   318       if (handle_set_is_member (mixin->members, handle))
       
   319         {
       
   320           if ((mixin->group_flags & TP_CHANNEL_GROUP_FLAG_CAN_REMOVE) == 0)
       
   321             {
       
   322               gabble_debug (DEBUG_FLAG, "handle %u cannot be removed from members without GROUP_FLAG_CAN_REMOVE",
       
   323                   handle);
       
   324 
       
   325               g_set_error (error, TELEPATHY_ERRORS, PermissionDenied,
       
   326                   "handle %u cannot be removed from members without GROUP_FLAG_CAN_REMOVE",
       
   327                   handle);
       
   328 
       
   329               return FALSE;
       
   330             }
       
   331         }
       
   332       else if (handle_set_is_member (mixin->remote_pending, handle))
       
   333         {
       
   334           if ((mixin->group_flags & TP_CHANNEL_GROUP_FLAG_CAN_RESCIND) == 0)
       
   335             {
       
   336               gabble_debug (DEBUG_FLAG, "handle %u cannot be removed from remote pending without GROUP_FLAG_CAN_RESCIND",
       
   337                   handle);
       
   338 
       
   339               g_set_error (error, TELEPATHY_ERRORS, PermissionDenied,
       
   340                   "handle %u cannot be removed from remote pending without GROUP_FLAG_CAN_RESCIND",
       
   341                   handle);
       
   342 
       
   343               return FALSE;
       
   344             }
       
   345         }
       
   346       else if (!handle_set_is_member (mixin->local_pending, handle))
       
   347         {
       
   348           gabble_debug (DEBUG_FLAG, "handle %u is not a current or pending member",
       
   349                    handle);
       
   350 
       
   351           g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
   352               "handle %u is not a current or pending member", handle);
       
   353 
       
   354           return FALSE;
       
   355         }
       
   356     }
       
   357 
       
   358   /* remove handle by handle */
       
   359   for (i = 0; i < contacts->len; i++)
       
   360     {
       
   361       handle = g_array_index (contacts, GabbleHandle, i);
       
   362 
       
   363       if (!mixin_cls->remove_member (obj, handle, message, error))
       
   364         {
       
   365           return FALSE;
       
   366         }
       
   367     }
       
   368 
       
   369   return TRUE;
       
   370 }
       
   371 
       
   372 gboolean
       
   373 gabble_group_mixin_get_members (GObject *obj, GArray **ret, GError **error)
       
   374 {
       
   375   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   376 
       
   377   *ret = handle_set_to_array (mixin->members);
       
   378 
       
   379   return TRUE;
       
   380 }
       
   381 
       
   382 gboolean
       
   383 gabble_group_mixin_get_local_pending_members (GObject *obj, GArray **ret, GError **error)
       
   384 {
       
   385   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   386 
       
   387   *ret = handle_set_to_array (mixin->local_pending);
       
   388 
       
   389   return TRUE;
       
   390 }
       
   391 
       
   392 gboolean
       
   393 gabble_group_mixin_get_remote_pending_members (GObject *obj, GArray **ret, GError **error)
       
   394 {
       
   395   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   396 
       
   397   *ret = handle_set_to_array (mixin->remote_pending);
       
   398 
       
   399   return TRUE;
       
   400 }
       
   401 
       
   402 gboolean
       
   403 gabble_group_mixin_get_all_members (GObject *obj, GArray **ret, GArray **ret1, GArray **ret2, GError **error)
       
   404 {
       
   405   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   406 
       
   407   *ret = handle_set_to_array (mixin->members);
       
   408   *ret1 = handle_set_to_array (mixin->local_pending);
       
   409   *ret2 = handle_set_to_array (mixin->remote_pending);
       
   410 
       
   411   return TRUE;
       
   412 }
       
   413 
       
   414 gboolean
       
   415 gabble_group_mixin_get_handle_owners (GObject *obj,
       
   416                                       const GArray *handles,
       
   417                                       GArray **ret,
       
   418                                       GError **error)
       
   419 {
       
   420   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   421   GabbleGroupMixinPrivate *priv = mixin->priv;
       
   422   guint i;
       
   423 
       
   424   if ((mixin->group_flags &
       
   425         TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES) == 0)
       
   426     {
       
   427       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
   428           "channel doesn't have channel specific handles");
       
   429 
       
   430       return FALSE;
       
   431     }
       
   432 
       
   433   if (!gabble_handles_are_valid (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT,
       
   434                                  handles, FALSE, error))
       
   435     {
       
   436       return FALSE;
       
   437     }
       
   438 
       
   439   *ret = g_array_sized_new (FALSE, FALSE, sizeof (GabbleHandle), handles->len);
       
   440 
       
   441   for (i = 0; i < handles->len; i++)
       
   442     {
       
   443       GabbleHandle local_handle = g_array_index (handles, GabbleHandle, i);
       
   444       GabbleHandle owner_handle;
       
   445 
       
   446       if (!handle_set_is_member (mixin->members, local_handle))
       
   447         {
       
   448           g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   449               "handle %u is not a member", local_handle);
       
   450 
       
   451           g_array_free (*ret, TRUE);
       
   452           *ret = NULL;
       
   453 
       
   454           return FALSE;
       
   455         }
       
   456 
       
   457       owner_handle = GPOINTER_TO_UINT (
       
   458           g_hash_table_lookup (priv->handle_owners,
       
   459                                GUINT_TO_POINTER (local_handle)));
       
   460 
       
   461       g_array_append_val (*ret, owner_handle);
       
   462     }
       
   463 
       
   464   return TRUE;
       
   465 }
       
   466 
       
   467 #define GFTS_APPEND_FLAG_IF_SET(flag) \
       
   468   if (flags & flag) \
       
   469     { \
       
   470       if (i++ > 0) \
       
   471         g_string_append (str, "|"); \
       
   472       g_string_append (str, #flag + 22); \
       
   473     }
       
   474 
       
   475 static gchar *
       
   476 group_flags_to_string (TpChannelGroupFlags flags)
       
   477 {
       
   478   gint i = 0;
       
   479   GString *str;
       
   480 
       
   481   str = g_string_new ("[" ANSI_BOLD_OFF);
       
   482 
       
   483   GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CAN_ADD);
       
   484   GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CAN_REMOVE);
       
   485   GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CAN_RESCIND);
       
   486   GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD);
       
   487   GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE);
       
   488   GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_ACCEPT);
       
   489   GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_REJECT);
       
   490   GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_RESCIND);
       
   491 
       
   492   g_string_append (str, ANSI_BOLD_ON "]");
       
   493 
       
   494   return g_string_free (str, FALSE);
       
   495 }
       
   496 
       
   497 /**
       
   498  * gabble_group_mixin_change_flags:
       
   499  *
       
   500  * Request a change to be made to the flags. Emits the
       
   501  * signal with the changes which were made.
       
   502  */
       
   503 void
       
   504 gabble_group_mixin_change_flags (GObject *obj,
       
   505                                  TpChannelGroupFlags add,
       
   506                                  TpChannelGroupFlags remove)
       
   507 {
       
   508   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   509   GabbleGroupMixinClass *mixin_cls = GABBLE_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj));
       
   510   TpChannelGroupFlags added, removed;
       
   511 
       
   512   added = add & ~mixin->group_flags;
       
   513   mixin->group_flags |= added;
       
   514 
       
   515   removed = remove & mixin->group_flags;
       
   516   mixin->group_flags &= ~removed;
       
   517 
       
   518   if (add != 0 || remove != 0)
       
   519     {
       
   520       gchar *str_added, *str_removed, *str_flags;
       
   521 
       
   522       if (DEBUGGING)
       
   523         {
       
   524           str_added = group_flags_to_string (added);
       
   525           str_removed = group_flags_to_string (removed);
       
   526           str_flags = group_flags_to_string (mixin->group_flags);
       
   527 
       
   528           g_message (ANSI_BOLD_ON ANSI_FG_WHITE
       
   529                   "%s: emitting group flags changed\n"
       
   530                   "  added    : %s\n"
       
   531                   "  removed  : %s\n"
       
   532                   "  flags now: %s\n" ANSI_RESET,
       
   533                   G_STRFUNC, str_added, str_removed, str_flags);
       
   534 
       
   535           fflush (stdout);
       
   536 
       
   537           g_free (str_added);
       
   538           g_free (str_removed);
       
   539           g_free (str_flags);
       
   540         }
       
   541 
       
   542       g_signal_emit(obj, mixin_cls->group_flags_changed_signal_id, 0, added, removed);
       
   543     }
       
   544 }
       
   545 
       
   546 static gchar *
       
   547 member_array_to_string (GabbleHandleRepo *repo, const GArray *array)
       
   548 {
       
   549   GString *str;
       
   550   guint i;
       
   551 
       
   552   str = g_string_new ("[" ANSI_BOLD_OFF);
       
   553 
       
   554   for (i = 0; i < array->len; i++)
       
   555     {
       
   556       GabbleHandle handle;
       
   557       const gchar *handle_str;
       
   558 
       
   559       handle = g_array_index (array, guint32, i);
       
   560       handle_str = gabble_handle_inspect (repo, TP_HANDLE_TYPE_CONTACT, handle);
       
   561 
       
   562       g_string_append_printf (str, "%s%u (%s)",
       
   563           (i > 0) ? "\n              " : "",
       
   564           handle, handle_str);
       
   565     }
       
   566 
       
   567   g_string_append (str, ANSI_BOLD_ON "]");
       
   568 
       
   569   return g_string_free (str, FALSE);
       
   570 }
       
   571 
       
   572 static void remove_handle_owners_if_exist (GObject *obj, GArray *array);
       
   573 
       
   574 /**
       
   575  * gabble_group_mixin_change_members:
       
   576  *
       
   577  * Request members to be added, removed or marked as local or remote pending.
       
   578  * Changes member sets, references, and emits the MembersChanged signal.
       
   579  */
       
   580 gboolean
       
   581 gabble_group_mixin_change_members (GObject *obj,
       
   582                                    const gchar *message,
       
   583                                    GIntSet *add,
       
   584                                    GIntSet *remove,
       
   585                                    GIntSet *local_pending,
       
   586                                    GIntSet *remote_pending,
       
   587                                    GabbleHandle actor,
       
   588                                    guint reason)
       
   589 {
       
   590   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   591   GabbleGroupMixinClass *mixin_cls = GABBLE_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj));
       
   592   GIntSet *new_add, *new_remove, *new_local_pending,
       
   593           *new_remote_pending, *tmp, *tmp2, *empty;
       
   594   gboolean ret;
       
   595 
       
   596   empty = g_intset_new ();
       
   597 
       
   598   if (add == NULL)
       
   599     add = empty;
       
   600 
       
   601   if (remove == NULL)
       
   602     remove = empty;
       
   603 
       
   604   if (local_pending == NULL)
       
   605     local_pending = empty;
       
   606 
       
   607   if (remote_pending == NULL)
       
   608     remote_pending = empty;
       
   609 
       
   610   /* members + add */
       
   611   new_add = handle_set_update (mixin->members, add);
       
   612 
       
   613   /* members - remove */
       
   614   new_remove = handle_set_difference_update (mixin->members, remove);
       
   615 
       
   616   /* members - local_pending */
       
   617   tmp = handle_set_difference_update (mixin->members, local_pending);
       
   618   g_intset_destroy (tmp);
       
   619 
       
   620   /* members - remote_pending */
       
   621   tmp = handle_set_difference_update (mixin->members, remote_pending);
       
   622   g_intset_destroy (tmp);
       
   623 
       
   624 
       
   625   /* local pending + local_pending */
       
   626   new_local_pending = handle_set_update (mixin->local_pending, local_pending);
       
   627 
       
   628   /* local pending - add */
       
   629   tmp = handle_set_difference_update (mixin->local_pending, add);
       
   630   g_intset_destroy (tmp);
       
   631 
       
   632   /* local pending - remove */
       
   633   tmp = handle_set_difference_update (mixin->local_pending, remove);
       
   634   tmp2 = g_intset_union (new_remove, tmp);
       
   635   g_intset_destroy (new_remove);
       
   636   g_intset_destroy (tmp);
       
   637   new_remove = tmp2;
       
   638 
       
   639   /* local pending - remote_pending */
       
   640   tmp = handle_set_difference_update (mixin->local_pending, remote_pending);
       
   641   g_intset_destroy (tmp);
       
   642 
       
   643 
       
   644   /* remote pending + remote_pending */
       
   645   new_remote_pending = handle_set_update (mixin->remote_pending, remote_pending);
       
   646 
       
   647   /* remote pending - add */
       
   648   tmp = handle_set_difference_update (mixin->remote_pending, add);
       
   649   g_intset_destroy (tmp);
       
   650 
       
   651   /* remote pending - remove */
       
   652   tmp = handle_set_difference_update (mixin->remote_pending, remove);
       
   653   tmp2 = g_intset_union (new_remove, tmp);
       
   654   g_intset_destroy (new_remove);
       
   655   g_intset_destroy (tmp);
       
   656   new_remove = tmp2;
       
   657 
       
   658   /* remote pending - local_pending */
       
   659   tmp = handle_set_difference_update (mixin->remote_pending, local_pending);
       
   660   g_intset_destroy (tmp);
       
   661 
       
   662   if (g_intset_size (new_add) > 0 ||
       
   663       g_intset_size (new_remove) > 0 ||
       
   664       g_intset_size (new_local_pending) > 0 ||
       
   665       g_intset_size (new_remote_pending) > 0)
       
   666     {
       
   667       GArray *arr_add, *arr_remove, *arr_local, *arr_remote;
       
   668       gchar *add_str, *rem_str, *local_str, *remote_str;
       
   669 
       
   670       /* translate intsets to arrays */
       
   671       arr_add = g_intset_to_array (new_add);
       
   672       arr_remove = g_intset_to_array (new_remove);
       
   673       arr_local = g_intset_to_array (new_local_pending);
       
   674       arr_remote = g_intset_to_array (new_remote_pending);
       
   675 
       
   676       /* remove any handle owner mappings */
       
   677       remove_handle_owners_if_exist (obj, arr_remove);
       
   678 
       
   679       if (DEBUGGING)
       
   680         {
       
   681           add_str = member_array_to_string (mixin->handle_repo, arr_add);
       
   682           rem_str = member_array_to_string (mixin->handle_repo, arr_remove);
       
   683           local_str = member_array_to_string (mixin->handle_repo, arr_local);
       
   684           remote_str = member_array_to_string (mixin->handle_repo, arr_remote);
       
   685 
       
   686           g_message (ANSI_BOLD_ON ANSI_FG_CYAN
       
   687                   "%s: emitting members changed\n"
       
   688                   "  message       : \"%s\"\n"
       
   689                   "  added         : %s\n"
       
   690                   "  removed       : %s\n"
       
   691                   "  local_pending : %s\n"
       
   692                   "  remote_pending: %s\n"
       
   693                   "  actor         : %u\n"
       
   694                   "  reason        : %u: %s\n" ANSI_RESET,
       
   695                   G_STRFUNC, message, add_str, rem_str, local_str, remote_str,
       
   696                   actor, reason, group_change_reason_str(reason));
       
   697 
       
   698           fflush (stdout);
       
   699 
       
   700           g_free (add_str);
       
   701           g_free (rem_str);
       
   702           g_free (local_str);
       
   703           g_free (remote_str);
       
   704         }
       
   705 
       
   706       if (actor)
       
   707         {
       
   708           handle_set_add (mixin->priv->actors, actor);
       
   709         }
       
   710       /* emit signal */
       
   711       g_signal_emit(obj, mixin_cls->members_changed_signal_id, 0,
       
   712                     message,
       
   713                     arr_add, arr_remove,
       
   714                     arr_local, arr_remote,
       
   715                     actor, reason);
       
   716 
       
   717       /* free arrays */
       
   718       g_array_free (arr_add, TRUE);
       
   719       g_array_free (arr_remove, TRUE);
       
   720       g_array_free (arr_local, TRUE);
       
   721       g_array_free (arr_remote, TRUE);
       
   722 
       
   723       ret = TRUE;
       
   724     }
       
   725   else
       
   726     {
       
   727       gabble_debug (DEBUG_FLAG, "not emitting signal, nothing changed");
       
   728 
       
   729       ret = FALSE;
       
   730     }
       
   731 
       
   732   /* free intsets */
       
   733   g_intset_destroy (new_add);
       
   734   g_intset_destroy (new_remove);
       
   735   g_intset_destroy (new_local_pending);
       
   736   g_intset_destroy (new_remote_pending);
       
   737   g_intset_destroy (empty);
       
   738 
       
   739   return ret;
       
   740 }
       
   741 
       
   742 void
       
   743 gabble_group_mixin_add_handle_owner (GObject *obj,
       
   744                                      GabbleHandle local_handle,
       
   745                                      GabbleHandle owner_handle)
       
   746 {
       
   747   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   748   GabbleGroupMixinPrivate *priv = mixin->priv;
       
   749 
       
   750   g_hash_table_insert (priv->handle_owners, GUINT_TO_POINTER (local_handle),
       
   751                        GUINT_TO_POINTER (owner_handle));
       
   752 
       
   753   gabble_handle_ref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT,
       
   754                      local_handle);
       
   755   gabble_handle_ref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT,
       
   756                      owner_handle);
       
   757 }
       
   758 
       
   759 static void
       
   760 remove_handle_owners_if_exist (GObject *obj, GArray *array)
       
   761 {
       
   762   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
   763   GabbleGroupMixinPrivate *priv = mixin->priv;
       
   764   guint i;
       
   765 
       
   766   for (i = 0; i < array->len; i++)
       
   767     {
       
   768       GabbleHandle handle = g_array_index (array, guint32, i);
       
   769       gpointer local_handle, owner_handle;
       
   770 
       
   771       if (g_hash_table_lookup_extended (priv->handle_owners,
       
   772                                         GUINT_TO_POINTER (handle),
       
   773                                         &local_handle,
       
   774                                         &owner_handle))
       
   775         {
       
   776           gabble_handle_unref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT,
       
   777                                GPOINTER_TO_UINT (local_handle));
       
   778           gabble_handle_unref (mixin->handle_repo, TP_HANDLE_TYPE_CONTACT,
       
   779                                GPOINTER_TO_UINT (owner_handle));
       
   780 
       
   781           g_hash_table_remove (priv->handle_owners, GUINT_TO_POINTER (handle));
       
   782         }
       
   783     }
       
   784 }
       
   785