1 /*
2 * group-mixin.c - Source for TpGroupMixin
3 *
4 * Copyright (C) 2006-2007 Collabora Ltd. <http://www.collabora.co.uk/>
5 * Copyright (C) 2006-2007 Nokia Corporation
6 * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
7 * @author Robert McQueen <robert.mcqueen@collabora.co.uk>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 /**
25 * SECTION:group-mixin
26 * @title: TpGroupMixin
27 * @short_description: a mixin implementation of the groups interface
28 * @see_also: #TpSvcChannelInterfaceGroup
29 *
30 * This mixin can be added to a channel GObject class to implement the
31 * groups interface in a general way.
32 *
33 * To use the group mixin, include a #TpGroupMixinClass somewhere in your
34 * class structure and a #TpGroupMixin somewhere in your instance structure,
35 * and call tp_group_mixin_class_init() from your class_init function,
36 * tp_group_mixin_init() from your init function or constructor, and
37 * tp_group_mixin_finalize() from your dispose or finalize function.
38 *
39 * To use the group mixin as the implementation of
40 * #TpSvcChannelInterfaceGroup, call
41 * <literal>G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
42 * tp_group_mixin_iface_init)</literal> in the fourth argument to
43 * <literal>G_DEFINE_TYPE_WITH_CODE</literal>.
44 *
45 * Since 0.5.13 you can also implement the group interface by forwarding all
46 * group operations to the group mixin of an associated object (mainly useful
47 * for Tubes channels). To do this, call tp_external_group_mixin_init()
48 * in the constructor after the associated object has been set,
49 * tp_external_group_mixin_finalize() in the dispose or finalize function, and
50 * <literal>G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
51 * tp_external_group_mixin_iface_init)</literal> in the fourth argument to
52 * <literal>G_DEFINE_TYPE_WITH_CODE</literal>.
53 *
54 * Since 0.7.10 you can also implement the properties of Group channels,
55 * by calling tp_group_mixin_init_dbus_properties() or
56 * tp_external_group_mixin_init_dbus_properties() (as appropriate).
57 */
58
59 #include "config.h"
60
61 #include <telepathy-glib/group-mixin.h>
62
63 #include <dbus/dbus-glib.h>
64 #include <stdio.h>
65 #include <string.h>
66
67 #include <telepathy-glib/dbus.h>
68 #include <telepathy-glib/debug-ansi.h>
69 #include <telepathy-glib/errors.h>
70 #include <telepathy-glib/gtypes.h>
71 #include <telepathy-glib/interfaces.h>
72
73 #define DEBUG_FLAG TP_DEBUG_GROUPS
74
75 #include "debug-internal.h"
76
77 static const char *
group_change_reason_str(guint reason)78 group_change_reason_str (guint reason)
79 {
80 switch (reason)
81 {
82 case TP_CHANNEL_GROUP_CHANGE_REASON_NONE:
83 return "unspecified reason";
84 case TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE:
85 return "offline";
86 case TP_CHANNEL_GROUP_CHANGE_REASON_KICKED:
87 return "kicked";
88 case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY:
89 return "busy";
90 case TP_CHANNEL_GROUP_CHANGE_REASON_INVITED:
91 return "invited";
92 case TP_CHANNEL_GROUP_CHANGE_REASON_BANNED:
93 return "banned";
94 case TP_CHANNEL_GROUP_CHANGE_REASON_ERROR:
95 return "error";
96 case TP_CHANNEL_GROUP_CHANGE_REASON_INVALID_CONTACT:
97 return "invalid contact";
98 case TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER:
99 return "no answer";
100 case TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED:
101 return "renamed";
102 case TP_CHANNEL_GROUP_CHANGE_REASON_PERMISSION_DENIED:
103 return "permission denied";
104 case TP_CHANNEL_GROUP_CHANGE_REASON_SEPARATED:
105 return "separated";
106 default:
107 return "(unknown reason code)";
108 }
109 }
110
111 typedef struct {
112 TpHandle actor;
113 guint reason;
114 const gchar *message;
115 TpHandleRepoIface *repo;
116 } LocalPendingInfo;
117
118 static LocalPendingInfo *
local_pending_info_new(TpHandleRepoIface * repo,TpHandle actor,guint reason,const gchar * message)119 local_pending_info_new (TpHandleRepoIface *repo,
120 TpHandle actor,
121 guint reason,
122 const gchar *message)
123 {
124 LocalPendingInfo *info = g_slice_new0 (LocalPendingInfo);
125 info->reason = reason;
126 info->message = g_strdup (message);
127 info->repo = repo;
128
129 if (actor != 0)
130 info->actor = actor;
131
132 return info;
133 }
134
135 static void
local_pending_info_free(LocalPendingInfo * info)136 local_pending_info_free (LocalPendingInfo *info)
137 {
138 g_free ((gchar *) info->message);
139 g_slice_free (LocalPendingInfo, info);
140 }
141
142 struct _TpGroupMixinClassPrivate {
143 TpGroupMixinRemMemberWithReasonFunc remove_with_reason;
144 unsigned allow_self_removal : 1;
145 };
146
147 struct _TpGroupMixinPrivate {
148 TpHandleSet *actors;
149 GHashTable *handle_owners;
150 GHashTable *local_pending_info;
151 GPtrArray *externals;
152 };
153
154 /**
155 * TP_HAS_GROUP_MIXIN:
156 * @o: a #GObject instance
157 *
158 * <!-- -->
159 *
160 * Returns: %TRUE if @o (or one of its parent classes) has the group mixin.
161 *
162 * Since: 0.13.9
163 */
164
165 /**
166 * TP_HAS_GROUP_MIXIN_CLASS:
167 * @cls: a #GObjectClass structure
168 *
169 * <!-- -->
170 *
171 * Returns: %TRUE if @cls (or one of its parent classes) has the group mixin.
172 *
173 * Since: 0.13.9
174 */
175
176 /**
177 * tp_group_mixin_class_get_offset_quark: (skip)
178 *
179 * <!--Returns: says it all-->
180 *
181 * Returns: the quark used for storing mixin offset on a GObjectClass
182 */
183 GQuark
tp_group_mixin_class_get_offset_quark()184 tp_group_mixin_class_get_offset_quark ()
185 {
186 static GQuark offset_quark = 0;
187 if (!offset_quark)
188 offset_quark = g_quark_from_static_string ("TpGroupMixinClassOffsetQuark");
189 return offset_quark;
190 }
191
192 /**
193 * tp_group_mixin_get_offset_quark: (skip)
194 *
195 * <!--Returns: says it all-->
196 *
197 * Returns: the quark used for storing mixin offset on a GObject
198 */
199 GQuark
tp_group_mixin_get_offset_quark()200 tp_group_mixin_get_offset_quark ()
201 {
202 static GQuark offset_quark = 0;
203 if (!offset_quark)
204 offset_quark = g_quark_from_static_string ("TpGroupMixinOffsetQuark");
205 return offset_quark;
206 }
207
208 /**
209 * tp_group_mixin_class_set_remove_with_reason_func: (skip)
210 * @cls: The class of an object implementing the group interface using this
211 * mixin
212 * @func: A callback to be used to remove contacts from this group with a
213 * specified reason.
214 *
215 * Set a callback to be used to implement RemoveMembers() and
216 * RemoveMembersWithReason(). If this function is called during class
217 * initialization, the given callback will be used instead of the remove
218 * callback passed to tp_group_mixin_class_init() (which must be %NULL
219 * in this case).
220 *
221 * Since: 0.5.13
222 */
223 void
tp_group_mixin_class_set_remove_with_reason_func(GObjectClass * cls,TpGroupMixinRemMemberWithReasonFunc func)224 tp_group_mixin_class_set_remove_with_reason_func (GObjectClass *cls,
225 TpGroupMixinRemMemberWithReasonFunc func)
226 {
227 TpGroupMixinClass *mixin_cls = TP_GROUP_MIXIN_CLASS (cls);
228
229 g_return_if_fail (mixin_cls->remove_member == NULL);
230 g_return_if_fail (mixin_cls->priv->remove_with_reason == NULL);
231 mixin_cls->priv->remove_with_reason = func;
232 }
233
234 /**
235 * tp_group_mixin_class_init: (skip)
236 * @obj_cls: The class of an object implementing the group interface using this
237 * mixin
238 * @offset: The offset of the TpGroupMixinClass structure within the class
239 * structure
240 * @add_func: A callback to be used to add contacts to this group
241 * @rem_func: A callback to be used to remove contacts from this group.
242 * This must be %NULL if you will subsequently call
243 * tp_group_mixin_class_set_remove_with_reason_func().
244 *
245 * Configure the mixin for use with the given class.
246 */
247 void
tp_group_mixin_class_init(GObjectClass * obj_cls,glong offset,TpGroupMixinAddMemberFunc add_func,TpGroupMixinRemMemberFunc rem_func)248 tp_group_mixin_class_init (GObjectClass *obj_cls,
249 glong offset,
250 TpGroupMixinAddMemberFunc add_func,
251 TpGroupMixinRemMemberFunc rem_func)
252 {
253 TpGroupMixinClass *mixin_cls;
254
255 g_assert (G_IS_OBJECT_CLASS (obj_cls));
256
257 g_type_set_qdata (G_OBJECT_CLASS_TYPE (obj_cls),
258 TP_GROUP_MIXIN_CLASS_OFFSET_QUARK,
259 GINT_TO_POINTER (offset));
260
261 mixin_cls = TP_GROUP_MIXIN_CLASS (obj_cls);
262
263 mixin_cls->add_member = add_func;
264 mixin_cls->remove_member = rem_func;
265
266 mixin_cls->priv = g_slice_new0 (TpGroupMixinClassPrivate);
267 }
268
269 /**
270 * tp_group_mixin_class_allow_self_removal: (skip)
271 * @obj_cls: The class of an object implementing the group interface using this
272 * mixin
273 *
274 * Configure the mixin to allow attempts to remove the SelfHandle from this
275 * Group, even if the group flags would otherwise disallow this. The
276 * channel's #TpGroupMixinRemMemberFunc or
277 * #TpGroupMixinRemMemberWithReasonFunc will be called as usual for such
278 * attempts, and may make them fail with %TP_ERROR_PERMISSION_DENIED if
279 * required.
280 *
281 * This function should be called from the GObject @class_init callback,
282 * after calling tp_group_mixin_class_init().
283 *
284 * (Recent telepathy-spec changes make it valid to try to remove the
285 * self-handle at all times, regardless of group flags. However, if this was
286 * implemented automatically in TpGroupMixin, this would risk crashing
287 * connection manager implementations that assume that TpGroupMixin will
288 * enforce the group flags strictly. As a result, connection managers should
289 * call this function to indicate to the TpGroupMixin that it may call their
290 * removal callback with the self-handle regardless of flag settings.)
291 *
292 * Since: 0.7.27
293 */
294 void
tp_group_mixin_class_allow_self_removal(GObjectClass * obj_cls)295 tp_group_mixin_class_allow_self_removal (GObjectClass *obj_cls)
296 {
297 TpGroupMixinClass *mixin_cls = TP_GROUP_MIXIN_CLASS (obj_cls);
298
299 mixin_cls->priv->allow_self_removal = TRUE;
300 }
301
302 /**
303 * tp_group_mixin_init: (skip)
304 * @obj: An object implementing the group interface using this mixin
305 * @offset: The offset of the TpGroupMixin structure within the instance
306 * structure
307 * @handle_repo: The connection's handle repository for contacts
308 * @self_handle: The handle of the local user in this group, if any
309 *
310 * Initialize the mixin.
311 */
312 void
tp_group_mixin_init(GObject * obj,glong offset,TpHandleRepoIface * handle_repo,TpHandle self_handle)313 tp_group_mixin_init (GObject *obj,
314 glong offset,
315 TpHandleRepoIface *handle_repo,
316 TpHandle self_handle)
317 {
318 TpGroupMixin *mixin;
319
320 g_assert (G_IS_OBJECT (obj));
321
322 g_type_set_qdata (G_OBJECT_TYPE (obj),
323 TP_GROUP_MIXIN_OFFSET_QUARK,
324 GINT_TO_POINTER (offset));
325
326 mixin = TP_GROUP_MIXIN (obj);
327
328 mixin->handle_repo = handle_repo;
329
330 if (self_handle != 0)
331 mixin->self_handle = self_handle;
332
333 mixin->group_flags = TP_CHANNEL_GROUP_FLAG_MEMBERS_CHANGED_DETAILED;
334
335 mixin->members = tp_handle_set_new (handle_repo);
336 mixin->local_pending = tp_handle_set_new (handle_repo);
337 mixin->remote_pending = tp_handle_set_new (handle_repo);
338
339 mixin->priv = g_slice_new0 (TpGroupMixinPrivate);
340 mixin->priv->handle_owners = g_hash_table_new (NULL, NULL);
341 mixin->priv->local_pending_info = g_hash_table_new_full (NULL, NULL, NULL,
342 (GDestroyNotify)local_pending_info_free);
343 mixin->priv->actors = tp_handle_set_new (handle_repo);
344 mixin->priv->externals = NULL;
345 }
346
347 static void
tp_group_mixin_add_external(GObject * obj,GObject * external)348 tp_group_mixin_add_external (GObject *obj, GObject *external)
349 {
350 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
351
352 if (mixin->priv->externals == NULL)
353 mixin->priv->externals = g_ptr_array_new ();
354 g_ptr_array_add (mixin->priv->externals, external);
355 }
356
357 static void
tp_group_mixin_remove_external(GObject * obj,GObject * external)358 tp_group_mixin_remove_external (GObject *obj, GObject *external)
359 {
360 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
361
362 /* we can't have added it if we have no array to add it to... */
363 g_return_if_fail (mixin->priv->externals != NULL);
364 g_ptr_array_remove_fast (mixin->priv->externals, external);
365 }
366
367 /**
368 * tp_group_mixin_finalize: (skip)
369 * @obj: An object implementing the group interface using this mixin
370 *
371 * Unreference handles and free resources used by this mixin.
372 */
373 void
tp_group_mixin_finalize(GObject * obj)374 tp_group_mixin_finalize (GObject *obj)
375 {
376 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
377
378 tp_handle_set_destroy (mixin->priv->actors);
379
380 g_hash_table_unref (mixin->priv->handle_owners);
381 g_hash_table_unref (mixin->priv->local_pending_info);
382
383 if (mixin->priv->externals)
384 g_ptr_array_unref (mixin->priv->externals);
385
386 g_slice_free (TpGroupMixinPrivate, mixin->priv);
387
388 tp_handle_set_destroy (mixin->members);
389 tp_handle_set_destroy (mixin->local_pending);
390 tp_handle_set_destroy (mixin->remote_pending);
391 }
392
393 /**
394 * tp_group_mixin_get_self_handle: (skip)
395 * @obj: An object implementing the group mixin using this interface
396 * @ret: Used to return the local user's handle in this group
397 * @error: Unused
398 *
399 * Set the guint pointed to by ret to the local user's handle in this
400 * group, or to 0 if the local user is not present in this group.
401 *
402 * Returns: %TRUE.
403 */
404 gboolean
tp_group_mixin_get_self_handle(GObject * obj,guint * ret,GError ** error)405 tp_group_mixin_get_self_handle (GObject *obj,
406 guint *ret,
407 GError **error)
408 {
409 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
410
411 if (tp_handle_set_is_member (mixin->members, mixin->self_handle) ||
412 tp_handle_set_is_member (mixin->local_pending, mixin->self_handle) ||
413 tp_handle_set_is_member (mixin->remote_pending, mixin->self_handle))
414 {
415 *ret = mixin->self_handle;
416 }
417 else
418 {
419 *ret = 0;
420 }
421
422 return TRUE;
423 }
424
425
426 /**
427 * tp_group_mixin_change_self_handle: (skip)
428 * @obj: An object implementing the group interface using this mixin
429 * @new_self_handle: The new self-handle for this group
430 *
431 * Change the self-handle for this group to the given value.
432 */
433 void
tp_group_mixin_change_self_handle(GObject * obj,TpHandle new_self_handle)434 tp_group_mixin_change_self_handle (GObject *obj,
435 TpHandle new_self_handle)
436 {
437 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
438 const gchar *new_self_id = tp_handle_inspect (mixin->handle_repo,
439 new_self_handle);
440
441 DEBUG ("%u '%s'", new_self_handle, new_self_id);
442
443 mixin->self_handle = new_self_handle;
444
445 tp_svc_channel_interface_group_emit_self_handle_changed (obj,
446 new_self_handle);
447 tp_svc_channel_interface_group_emit_self_contact_changed (obj,
448 new_self_handle, new_self_id);
449 }
450
451
452 static void
tp_group_mixin_get_self_handle_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)453 tp_group_mixin_get_self_handle_async (TpSvcChannelInterfaceGroup *obj,
454 DBusGMethodInvocation *context)
455 {
456 guint ret;
457 GError *error = NULL;
458
459 if (tp_group_mixin_get_self_handle ((GObject *) obj, &ret, &error))
460 {
461 tp_svc_channel_interface_group_return_from_get_self_handle (
462 context, ret);
463 }
464 else
465 {
466 dbus_g_method_return_error (context, error);
467 g_error_free (error);
468 }
469 }
470
471 /**
472 * tp_group_mixin_get_group_flags: (skip)
473 * @obj: An object implementing the group mixin using this interface
474 * @ret: Used to return the flags
475 * @error: Unused
476 *
477 * Set the guint pointed to by ret to this group's flags, to be
478 * interpreted according to TpChannelGroupFlags.
479 *
480 * Returns: %TRUE
481 */
482 gboolean
tp_group_mixin_get_group_flags(GObject * obj,guint * ret,GError ** error)483 tp_group_mixin_get_group_flags (GObject *obj,
484 guint *ret,
485 GError **error)
486 {
487 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
488
489 *ret = mixin->group_flags;
490
491 return TRUE;
492 }
493
494 static void
tp_group_mixin_get_group_flags_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)495 tp_group_mixin_get_group_flags_async (TpSvcChannelInterfaceGroup *obj,
496 DBusGMethodInvocation *context)
497 {
498 guint ret;
499 GError *error = NULL;
500
501 if (tp_group_mixin_get_group_flags ((GObject *) obj, &ret, &error))
502 {
503 tp_svc_channel_interface_group_return_from_get_group_flags (
504 context, ret);
505 }
506 else
507 {
508 dbus_g_method_return_error (context, error);
509 g_error_free (error);
510 }
511 }
512
513 /**
514 * tp_group_mixin_add_members: (skip)
515 * @obj: An object implementing the group interface using this mixin
516 * @contacts: A GArray of guint representing contacts
517 * @message: A message associated with the addition request, if supported
518 * @error: Used to return an error if %FALSE is returned
519 *
520 * Request that the given contacts be added to the group as if in response
521 * to user action. If the group's flags prohibit this, raise
522 * PermissionDenied. If any of the handles is invalid, raise InvalidHandle.
523 * Otherwise attempt to add the contacts by calling the callbacks provided
524 * by the channel implementation.
525 *
526 * Returns: %TRUE on success
527 */
528 gboolean
tp_group_mixin_add_members(GObject * obj,const GArray * contacts,const gchar * message,GError ** error)529 tp_group_mixin_add_members (GObject *obj,
530 const GArray *contacts,
531 const gchar *message,
532 GError **error)
533 {
534 TpGroupMixinClass *mixin_cls = TP_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj));
535 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
536 guint i;
537 TpHandle handle;
538
539 /* reject invalid handles */
540 if (!tp_handles_are_valid (mixin->handle_repo, contacts, FALSE, error))
541 return FALSE;
542
543 /* check that adding is allowed by flags */
544 for (i = 0; i < contacts->len; i++)
545 {
546 handle = g_array_index (contacts, TpHandle, i);
547
548 if ((mixin->group_flags & TP_CHANNEL_GROUP_FLAG_CAN_ADD) == 0 &&
549 !tp_handle_set_is_member (mixin->members, handle) &&
550 !tp_handle_set_is_member (mixin->local_pending, handle))
551 {
552 DEBUG ("handle %u cannot be added to members without "
553 "GROUP_FLAG_CAN_ADD", handle);
554
555 g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED,
556 "handle %u cannot be added to members without "
557 "GROUP_FLAG_CAN_ADD", handle);
558
559 return FALSE;
560 }
561 }
562
563 /* add handle by handle */
564 for (i = 0; i < contacts->len; i++)
565 {
566 handle = g_array_index (contacts, TpHandle, i);
567
568 if (tp_handle_set_is_member (mixin->members, handle))
569 {
570 DEBUG ("handle %u is already a member, skipping", handle);
571
572 continue;
573 }
574
575 if (mixin_cls->add_member == NULL)
576 {
577 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
578 "Adding members to this Group channel is not possible");
579 return FALSE;
580 }
581 if (!mixin_cls->add_member (obj, handle, message, error))
582 {
583 return FALSE;
584 }
585 }
586
587 return TRUE;
588 }
589
590 static void
tp_group_mixin_add_members_async(TpSvcChannelInterfaceGroup * obj,const GArray * contacts,const gchar * message,DBusGMethodInvocation * context)591 tp_group_mixin_add_members_async (TpSvcChannelInterfaceGroup *obj,
592 const GArray *contacts,
593 const gchar *message,
594 DBusGMethodInvocation *context)
595 {
596 GError *error = NULL;
597
598 if (tp_group_mixin_add_members ((GObject *) obj, contacts, message, &error))
599 {
600 tp_svc_channel_interface_group_return_from_add_members (context);
601 }
602 else
603 {
604 dbus_g_method_return_error (context, error);
605 g_error_free (error);
606 }
607 }
608
609 /**
610 * tp_group_mixin_remove_members: (skip)
611 * @obj: An object implementing the group interface using this mixin
612 * @contacts: A GArray of guint representing contacts
613 * @message: A message to be sent to those contacts, if supported
614 * @error: Used to return an error if %FALSE is returned
615 *
616 * Request that the given contacts be removed from the group as if in response
617 * to user action. If the group's flags prohibit this, raise
618 * PermissionDenied. If any of the handles is invalid, raise InvalidHandle.
619 * If any of the handles is absent from the group, raise NotAvailable.
620 * Otherwise attempt to remove the contacts by calling the callbacks provided
621 * by the channel implementation.
622 *
623 * Returns: %TRUE on success
624 */
625 gboolean
tp_group_mixin_remove_members(GObject * obj,const GArray * contacts,const gchar * message,GError ** error)626 tp_group_mixin_remove_members (GObject *obj,
627 const GArray *contacts,
628 const gchar *message,
629 GError **error)
630 {
631 return tp_group_mixin_remove_members_with_reason (obj, contacts, message,
632 TP_CHANNEL_GROUP_CHANGE_REASON_NONE, error);
633 }
634
635 /**
636 * tp_group_mixin_remove_members_with_reason: (skip)
637 * @obj: An object implementing the group interface using this mixin
638 * @contacts: A GArray of guint representing contacts
639 * @message: A message to be sent to those contacts, if supported
640 * @reason: A #TpChannelGroupChangeReason
641 * @error: Used to return an error if %FALSE is returned
642 *
643 * Request that the given contacts be removed from the group as if in response
644 * to user action. If the group's flags prohibit this, raise
645 * PermissionDenied. If any of the handles is invalid, raise InvalidHandle.
646 * If any of the handles is absent from the group, raise NotAvailable.
647 * Otherwise attempt to remove the contacts by calling the callbacks provided
648 * by the channel implementation.
649 *
650 * Returns: %TRUE on success
651 */
652 gboolean
tp_group_mixin_remove_members_with_reason(GObject * obj,const GArray * contacts,const gchar * message,guint reason,GError ** error)653 tp_group_mixin_remove_members_with_reason (GObject *obj,
654 const GArray *contacts,
655 const gchar *message,
656 guint reason,
657 GError **error)
658 {
659 TpGroupMixinClass *mixin_cls = TP_GROUP_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj));
660 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
661 guint i;
662 TpHandle handle;
663
664 /* reject invalid handles */
665 if (!tp_handles_are_valid (mixin->handle_repo, contacts, FALSE, error))
666 return FALSE;
667
668 /* check removing is allowed by flags */
669 for (i = 0; i < contacts->len; i++)
670 {
671 handle = g_array_index (contacts, TpHandle, i);
672
673 if (mixin_cls->priv->allow_self_removal &&
674 handle == mixin->self_handle &&
675 (tp_handle_set_is_member (mixin->members, handle) ||
676 tp_handle_set_is_member (mixin->remote_pending, handle) ||
677 tp_handle_set_is_member (mixin->local_pending, handle)))
678 {
679 /* don't check the flags - attempting to remove the self-handle
680 * is explicitly always allowed by this channel */
681 }
682 else if (tp_handle_set_is_member (mixin->members, handle))
683 {
684 if ((mixin->group_flags & TP_CHANNEL_GROUP_FLAG_CAN_REMOVE) == 0)
685 {
686 DEBUG ("handle %u cannot be removed from members without "
687 "GROUP_FLAG_CAN_REMOVE", handle);
688
689 g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED,
690 "handle %u cannot be removed from members without "
691 "GROUP_FLAG_CAN_REMOVE", handle);
692
693 return FALSE;
694 }
695 }
696 else if (tp_handle_set_is_member (mixin->remote_pending, handle))
697 {
698 if ((mixin->group_flags & TP_CHANNEL_GROUP_FLAG_CAN_RESCIND) == 0)
699 {
700 DEBUG ("handle %u cannot be removed from remote pending "
701 "without GROUP_FLAG_CAN_RESCIND", handle);
702
703 g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED,
704 "handle %u cannot be removed from remote pending without "
705 "GROUP_FLAG_CAN_RESCIND", handle);
706
707 return FALSE;
708 }
709 }
710 else if (!tp_handle_set_is_member (mixin->local_pending, handle))
711 {
712 DEBUG ("handle %u is not a current or pending member",
713 handle);
714 /* we'll skip this handle during the second pass */
715 }
716 }
717
718 /* remove handle by handle */
719 for (i = 0; i < contacts->len; i++)
720 {
721 handle = g_array_index (contacts, TpHandle, i);
722
723 if (!tp_handle_set_is_member (mixin->members, handle) &&
724 !tp_handle_set_is_member (mixin->remote_pending, handle) &&
725 !tp_handle_set_is_member (mixin->local_pending, handle))
726 continue;
727
728 if (mixin_cls->priv->remove_with_reason != NULL)
729 {
730 if (!mixin_cls->priv->remove_with_reason (obj, handle, message,
731 reason, error))
732 {
733 return FALSE;
734 }
735 }
736 else if (mixin_cls->remove_member != NULL)
737 {
738 if (!mixin_cls->remove_member (obj, handle, message, error))
739 {
740 return FALSE;
741 }
742 }
743 else
744 {
745 g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
746 "Removing contacts from this Group channel is not possible");
747 return FALSE;
748 }
749 }
750
751 return TRUE;
752 }
753
754 static void
tp_group_mixin_remove_members_with_reason_async(TpSvcChannelInterfaceGroup * obj,const GArray * contacts,const gchar * message,guint reason,DBusGMethodInvocation * context)755 tp_group_mixin_remove_members_with_reason_async
756 (TpSvcChannelInterfaceGroup *obj,
757 const GArray *contacts,
758 const gchar *message,
759 guint reason,
760 DBusGMethodInvocation *context)
761 {
762 GError *error = NULL;
763
764 if (tp_group_mixin_remove_members_with_reason ((GObject *) obj, contacts,
765 message, reason, &error))
766 {
767 tp_svc_channel_interface_group_return_from_remove_members_with_reason
768 (context);
769 }
770 else
771 {
772 dbus_g_method_return_error (context, error);
773 g_error_free (error);
774 }
775 }
776
777 static void
tp_group_mixin_remove_members_async(TpSvcChannelInterfaceGroup * obj,const GArray * contacts,const gchar * message,DBusGMethodInvocation * context)778 tp_group_mixin_remove_members_async (TpSvcChannelInterfaceGroup *obj,
779 const GArray *contacts,
780 const gchar *message,
781 DBusGMethodInvocation *context)
782 {
783 GError *error = NULL;
784
785 if (tp_group_mixin_remove_members_with_reason ((GObject *) obj, contacts,
786 message, TP_CHANNEL_GROUP_CHANGE_REASON_NONE, &error))
787 {
788 tp_svc_channel_interface_group_return_from_remove_members (context);
789 }
790 else
791 {
792 dbus_g_method_return_error (context, error);
793 g_error_free (error);
794 }
795 }
796
797 /**
798 * tp_group_mixin_get_members: (skip)
799 * @obj: An object implementing the group interface using this mixin
800 * @ret: Used to return a newly-allocated GArray of guint contact handles
801 * @error: Unused
802 *
803 * Get the group's current members
804 *
805 * Returns: %TRUE
806 */
807 gboolean
tp_group_mixin_get_members(GObject * obj,GArray ** ret,GError ** error)808 tp_group_mixin_get_members (GObject *obj,
809 GArray **ret,
810 GError **error)
811 {
812 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
813
814 *ret = tp_handle_set_to_array (mixin->members);
815
816 return TRUE;
817 }
818
819 static void
tp_group_mixin_get_members_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)820 tp_group_mixin_get_members_async (TpSvcChannelInterfaceGroup *obj,
821 DBusGMethodInvocation *context)
822 {
823 GArray *ret;
824 GError *error = NULL;
825
826 if (tp_group_mixin_get_members ((GObject *) obj, &ret, &error))
827 {
828 tp_svc_channel_interface_group_return_from_get_members (
829 context, ret);
830 g_array_unref (ret);
831 }
832 else
833 {
834 dbus_g_method_return_error (context, error);
835 g_error_free (error);
836 }
837 }
838
839 /**
840 * tp_group_mixin_get_local_pending_members: (skip)
841 * @obj: An object implementing the group interface using this mixin
842 * @ret: Used to return a newly-allocated GArray of guint contact handles
843 * @error: Unused
844 *
845 * Get the group's local-pending members.
846 *
847 * Returns: %TRUE
848 */
849 gboolean
tp_group_mixin_get_local_pending_members(GObject * obj,GArray ** ret,GError ** error)850 tp_group_mixin_get_local_pending_members (GObject *obj,
851 GArray **ret,
852 GError **error)
853 {
854 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
855
856 *ret = tp_handle_set_to_array (mixin->local_pending);
857
858 return TRUE;
859 }
860
861 static void
tp_group_mixin_get_local_pending_members_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)862 tp_group_mixin_get_local_pending_members_async (TpSvcChannelInterfaceGroup *obj,
863 DBusGMethodInvocation *context)
864 {
865 GArray *ret;
866 GError *error = NULL;
867
868 if (tp_group_mixin_get_local_pending_members ((GObject *) obj, &ret, &error))
869 {
870 tp_svc_channel_interface_group_return_from_get_local_pending_members (
871 context, ret);
872 g_array_unref (ret);
873 }
874 else
875 {
876 dbus_g_method_return_error (context, error);
877 g_error_free (error);
878 }
879 }
880
881 typedef struct {
882 TpGroupMixin *mixin;
883 GPtrArray *array;
884 } _mixin_and_array_of_info;
885
886 static void
local_pending_members_with_info_foreach(TpHandleSet * set,TpHandle i,gpointer userdata)887 local_pending_members_with_info_foreach (TpHandleSet *set,
888 TpHandle i,
889 gpointer userdata)
890 {
891 _mixin_and_array_of_info *data = userdata;
892 TpGroupMixinPrivate *priv = data->mixin->priv;
893 GType info_type = TP_STRUCT_TYPE_LOCAL_PENDING_INFO;
894 GValue entry = { 0, };
895 LocalPendingInfo *info = g_hash_table_lookup (priv->local_pending_info,
896 GUINT_TO_POINTER(i));
897 g_assert (info != NULL);
898
899 g_value_init (&entry, info_type);
900 g_value_take_boxed (&entry, dbus_g_type_specialized_construct (info_type));
901
902 dbus_g_type_struct_set (&entry,
903 0, i,
904 1, info->actor,
905 2, info->reason,
906 3, info->message,
907 G_MAXUINT);
908
909 g_ptr_array_add (data->array, g_value_get_boxed (&entry));
910 }
911
912 /**
913 * tp_group_mixin_get_local_pending_members_with_info: (skip)
914 * @obj: An object implementing the group interface using this mixin
915 * @ret: Used to return a newly-allocated GPtrArray of D-Bus structures each
916 * containing the handle of a local-pending contact, the handle of a contact
917 * responsible for adding them to the group (or 0), the reason code
918 * and a related message (e.g. their request to join the group)
919 * @error: Unused
920 *
921 * Get the group's local-pending members and information about their
922 * requests to join the channel.
923 *
924 * Returns: %TRUE
925 */
926 gboolean
tp_group_mixin_get_local_pending_members_with_info(GObject * obj,GPtrArray ** ret,GError ** error)927 tp_group_mixin_get_local_pending_members_with_info (
928 GObject *obj,
929 GPtrArray **ret,
930 GError **error)
931 {
932 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
933 _mixin_and_array_of_info data = { mixin, NULL };
934
935 *ret = g_ptr_array_new ();
936 data.array = *ret;
937
938 tp_handle_set_foreach (mixin->local_pending,
939 local_pending_members_with_info_foreach, &data);
940
941 return TRUE;
942 }
943
944 static void
tp_group_mixin_get_local_pending_members_with_info_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)945 tp_group_mixin_get_local_pending_members_with_info_async (
946 TpSvcChannelInterfaceGroup *obj,
947 DBusGMethodInvocation *context)
948 {
949 GPtrArray *ret;
950 GError *error = NULL;
951
952 if (tp_group_mixin_get_local_pending_members_with_info ((GObject *) obj,
953 &ret, &error))
954 {
955 guint i;
956 tp_svc_channel_interface_group_return_from_get_local_pending_members_with_info (
957 context, ret);
958 for (i = 0 ; i < ret->len; i++) {
959 tp_value_array_free (g_ptr_array_index (ret,i));
960 }
961 g_ptr_array_unref (ret);
962 }
963 else
964 {
965 dbus_g_method_return_error (context, error);
966 g_error_free (error);
967 }
968 }
969
970 /**
971 * tp_group_mixin_get_remote_pending_members: (skip)
972 * @obj: An object implementing the group interface using this mixin
973 * @ret: Used to return a newly-allocated GArray of guint representing the
974 * handles of the group's remote pending members
975 * @error: Unused
976 *
977 * Get the group's remote-pending members.
978 *
979 * Returns: %TRUE
980 */
981 gboolean
tp_group_mixin_get_remote_pending_members(GObject * obj,GArray ** ret,GError ** error)982 tp_group_mixin_get_remote_pending_members (GObject *obj,
983 GArray **ret,
984 GError **error)
985 {
986 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
987
988 *ret = tp_handle_set_to_array (mixin->remote_pending);
989
990 return TRUE;
991 }
992
993 static void
tp_group_mixin_get_remote_pending_members_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)994 tp_group_mixin_get_remote_pending_members_async (TpSvcChannelInterfaceGroup *obj,
995 DBusGMethodInvocation *context)
996 {
997 GArray *ret;
998 GError *error = NULL;
999
1000 if (tp_group_mixin_get_remote_pending_members ((GObject *) obj,
1001 &ret, &error))
1002 {
1003 tp_svc_channel_interface_group_return_from_get_remote_pending_members (
1004 context, ret);
1005 g_array_unref (ret);
1006 }
1007 else
1008 {
1009 dbus_g_method_return_error (context, error);
1010 g_error_free (error);
1011 }
1012 }
1013
1014 /**
1015 * tp_group_mixin_get_all_members: (skip)
1016 * @obj: An object implementing the group interface using this mixin
1017 * @members: Used to return a newly-allocated GArray of guint representing
1018 * the handles of the group's members
1019 * @local_pending: Used to return a newly-allocated GArray of guint
1020 * representing the handles of the group's local pending members
1021 * @remote_pending: Used to return a newly-allocated GArray of guint
1022 * representing the handles of the group's remote pending members
1023 * @error: Unused
1024 *
1025 * Get the group's current and pending members.
1026 *
1027 * Returns: %TRUE
1028 */
1029 gboolean
tp_group_mixin_get_all_members(GObject * obj,GArray ** members,GArray ** local_pending,GArray ** remote_pending,GError ** error)1030 tp_group_mixin_get_all_members (GObject *obj,
1031 GArray **members,
1032 GArray **local_pending,
1033 GArray **remote_pending,
1034 GError **error)
1035 {
1036 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
1037
1038 *members = tp_handle_set_to_array (mixin->members);
1039 *local_pending = tp_handle_set_to_array (mixin->local_pending);
1040 *remote_pending = tp_handle_set_to_array (mixin->remote_pending);
1041
1042 return TRUE;
1043 }
1044
1045 static void
tp_group_mixin_get_all_members_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)1046 tp_group_mixin_get_all_members_async (TpSvcChannelInterfaceGroup *obj,
1047 DBusGMethodInvocation *context)
1048 {
1049 GArray *mem, *local, *remote;
1050 GError *error = NULL;
1051
1052 if (tp_group_mixin_get_all_members ((GObject *) obj, &mem, &local, &remote,
1053 &error))
1054 {
1055 tp_svc_channel_interface_group_return_from_get_all_members (
1056 context, mem, local, remote);
1057 g_array_unref (mem);
1058 g_array_unref (local);
1059 g_array_unref (remote);
1060 }
1061 else
1062 {
1063 dbus_g_method_return_error (context, error);
1064 g_error_free (error);
1065 }
1066 }
1067
1068 /**
1069 * tp_group_mixin_get_handle_owners: (skip)
1070 * @obj: An object implementing the group interface with this mixin
1071 * @handles: An array of guint representing locally valid handles
1072 * @ret: Used to return an array of guint representing globally valid
1073 * handles, or 0 where unavailable, if %TRUE is returned
1074 * @error: Used to return an error if %FALSE is returned
1075 *
1076 * If the mixin has the flag %TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES,
1077 * return the global owners of the given local handles, or 0 where
1078 * unavailable.
1079 *
1080 * Returns: %TRUE (setting @ret) on success, %FALSE (setting @error) on
1081 * failure
1082 */
1083 gboolean
tp_group_mixin_get_handle_owners(GObject * obj,const GArray * handles,GArray ** ret,GError ** error)1084 tp_group_mixin_get_handle_owners (GObject *obj,
1085 const GArray *handles,
1086 GArray **ret,
1087 GError **error)
1088 {
1089 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
1090 TpGroupMixinPrivate *priv = mixin->priv;
1091 guint i;
1092
1093 if ((mixin->group_flags &
1094 TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES) == 0)
1095 {
1096 g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1097 "channel doesn't have channel specific handles");
1098
1099 return FALSE;
1100 }
1101
1102 if (!tp_handles_are_valid (mixin->handle_repo, handles, FALSE, error))
1103 {
1104 return FALSE;
1105 }
1106
1107 *ret = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), handles->len);
1108
1109 for (i = 0; i < handles->len; i++)
1110 {
1111 TpHandle local_handle = g_array_index (handles, TpHandle, i);
1112 TpHandle owner_handle;
1113
1114 if (!tp_handle_set_is_member (mixin->members, local_handle))
1115 {
1116 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1117 "handle %u is not a member", local_handle);
1118
1119 g_array_unref (*ret);
1120 *ret = NULL;
1121
1122 return FALSE;
1123 }
1124
1125 owner_handle = GPOINTER_TO_UINT (
1126 g_hash_table_lookup (priv->handle_owners,
1127 GUINT_TO_POINTER (local_handle)));
1128
1129 g_array_append_val (*ret, owner_handle);
1130 }
1131
1132 return TRUE;
1133 }
1134
1135 static void
tp_group_mixin_get_handle_owners_async(TpSvcChannelInterfaceGroup * obj,const GArray * handles,DBusGMethodInvocation * context)1136 tp_group_mixin_get_handle_owners_async (TpSvcChannelInterfaceGroup *obj,
1137 const GArray *handles,
1138 DBusGMethodInvocation *context)
1139 {
1140 GArray *ret;
1141 GError *error = NULL;
1142
1143 if (tp_group_mixin_get_handle_owners ((GObject *) obj, handles,
1144 &ret, &error))
1145 {
1146 tp_svc_channel_interface_group_return_from_get_handle_owners (
1147 context, ret);
1148 g_array_unref (ret);
1149 }
1150 else
1151 {
1152 dbus_g_method_return_error (context, error);
1153 g_error_free (error);
1154 }
1155 }
1156
1157 #define GFTS_APPEND_FLAG_IF_SET(flag) \
1158 if (flags & flag) \
1159 { \
1160 if (i++ > 0) \
1161 g_string_append (str, "|"); \
1162 g_string_append (str, #flag + 22); \
1163 flags &= ~flag; \
1164 }
1165
1166 static gchar *
group_flags_to_string(TpChannelGroupFlags flags)1167 group_flags_to_string (TpChannelGroupFlags flags)
1168 {
1169 gint i = 0;
1170 GString *str;
1171
1172 str = g_string_new ("[");
1173
1174 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CAN_ADD);
1175 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CAN_REMOVE);
1176 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CAN_RESCIND);
1177 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD);
1178 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE);
1179 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_ACCEPT);
1180 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_REJECT);
1181 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MESSAGE_RESCIND);
1182 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES);
1183 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_ONLY_ONE_GROUP);
1184 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_HANDLE_OWNERS_NOT_AVAILABLE);
1185 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_PROPERTIES);
1186 GFTS_APPEND_FLAG_IF_SET (TP_CHANNEL_GROUP_FLAG_MEMBERS_CHANGED_DETAILED);
1187
1188 /* Print out any remaining flags that weren't removed in the above cases
1189 * numerically.
1190 */
1191 if (flags != 0)
1192 {
1193 if (i > 0)
1194 g_string_append (str, "|");
1195
1196 g_string_append_printf (str, "%u", flags);
1197 }
1198
1199 g_string_append (str, "]");
1200
1201 return g_string_free (str, FALSE);
1202 }
1203
1204 /**
1205 * tp_group_mixin_change_flags: (skip)
1206 * @obj: An object implementing the groups interface using this mixin
1207 * @add: Flags to be added
1208 * @del: Flags to be removed
1209 *
1210 * Request a change to be made to the flags. If any flags were actually
1211 * set or cleared, emits the GroupFlagsChanged signal with the changes.
1212 *
1213 * It is an error to set any of the same bits in both @add and @del.
1214 *
1215 * Changed in 0.7.7: the signal is not emitted if adding @add and
1216 * removing @del had no effect on the existing group flags.
1217 */
1218 void
tp_group_mixin_change_flags(GObject * obj,TpChannelGroupFlags add,TpChannelGroupFlags del)1219 tp_group_mixin_change_flags (GObject *obj,
1220 TpChannelGroupFlags add,
1221 TpChannelGroupFlags del)
1222 {
1223 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
1224 TpChannelGroupFlags added, removed;
1225
1226 /* It's meaningless to want to add and remove the same capability */
1227 g_return_if_fail ((add & del) == 0);
1228
1229 added = add & ~mixin->group_flags;
1230 mixin->group_flags |= added;
1231
1232 removed = del & mixin->group_flags;
1233 mixin->group_flags &= ~removed;
1234
1235 if (added == 0 && removed == 0)
1236 {
1237 DEBUG ("No change: %u includes all the bits of %u and none of %u",
1238 mixin->group_flags, add, del);
1239 }
1240 else
1241 {
1242 gchar *str_added, *str_removed, *str_flags;
1243
1244 if (DEBUGGING)
1245 {
1246 str_added = group_flags_to_string (added);
1247 str_removed = group_flags_to_string (removed);
1248 str_flags = group_flags_to_string (mixin->group_flags);
1249
1250 DEBUG ("emitting group flags changed\n"
1251 " added : %s\n"
1252 " removed : %s\n"
1253 " flags now: %s\n",
1254 str_added, str_removed, str_flags);
1255
1256 g_free (str_added);
1257 g_free (str_removed);
1258 g_free (str_flags);
1259 }
1260
1261 tp_svc_channel_interface_group_emit_group_flags_changed (obj, added,
1262 removed);
1263 if (mixin->priv->externals != NULL)
1264 {
1265 guint i;
1266
1267 for (i = 0; i < mixin->priv->externals->len; i++)
1268 {
1269 tp_svc_channel_interface_group_emit_group_flags_changed
1270 ((GObject *) g_ptr_array_index (mixin->priv->externals, i),
1271 added, removed);
1272 }
1273 }
1274 }
1275 }
1276
1277 static gchar *
member_array_to_string(TpHandleRepoIface * repo,const GArray * array)1278 member_array_to_string (TpHandleRepoIface *repo,
1279 const GArray *array)
1280 {
1281 GString *str;
1282 guint i;
1283
1284 str = g_string_new ("[");
1285
1286 for (i = 0; i < array->len; i++)
1287 {
1288 TpHandle handle;
1289 const gchar *handle_str;
1290
1291 handle = g_array_index (array, guint, i);
1292 handle_str = tp_handle_inspect (repo, handle);
1293
1294 g_string_append_printf (str, "%s%u (%s)",
1295 /* indent to: " remote_pending: [" */
1296 (i > 0) ? "\n "
1297 : "",
1298 handle, handle_str);
1299 }
1300
1301 g_string_append (str, "]");
1302
1303 return g_string_free (str, FALSE);
1304 }
1305
1306 static GArray *remove_handle_owners_if_exist (GObject *obj,
1307 GArray *array) G_GNUC_WARN_UNUSED_RESULT;
1308
1309 typedef struct {
1310 TpGroupMixin *mixin;
1311 LocalPendingInfo *info;
1312 } _mixin_and_info;
1313
1314 static void
local_pending_added_foreach(guint i,gpointer userdata)1315 local_pending_added_foreach (guint i,
1316 gpointer userdata)
1317 {
1318 _mixin_and_info *data = userdata;
1319 TpGroupMixinPrivate *priv = data->mixin->priv;
1320
1321 g_hash_table_insert (priv->local_pending_info,
1322 GUINT_TO_POINTER (i),
1323 local_pending_info_new (data->mixin->handle_repo,
1324 data->info->actor, data->info->reason, data->info->message));
1325 }
1326
1327 static void
local_pending_added(TpGroupMixin * mixin,const TpIntset * added,TpHandle actor,guint reason,const gchar * message)1328 local_pending_added (TpGroupMixin *mixin,
1329 const TpIntset *added,
1330 TpHandle actor,
1331 guint reason,
1332 const gchar *message)
1333 {
1334 LocalPendingInfo info;
1335 _mixin_and_info data = { mixin, &info };
1336 info.actor = actor;
1337 info.reason = reason;
1338 info.message = message;
1339
1340 tp_intset_foreach (added, local_pending_added_foreach, &data);
1341 }
1342
1343 static void
local_pending_remove_foreach(guint i,gpointer userdata)1344 local_pending_remove_foreach (guint i,
1345 gpointer userdata)
1346 {
1347 TpGroupMixin *mixin = (TpGroupMixin *) userdata;
1348 TpGroupMixinPrivate *priv = mixin->priv;
1349
1350 g_hash_table_remove (priv->local_pending_info, GUINT_TO_POINTER(i));
1351 }
1352
1353 static void
local_pending_remove(TpGroupMixin * mixin,TpIntset * removed)1354 local_pending_remove (TpGroupMixin *mixin,
1355 TpIntset *removed)
1356 {
1357 tp_intset_foreach (removed, local_pending_remove_foreach, mixin);
1358 }
1359
1360
1361 static void
add_members_in_array(GHashTable * contact_ids,TpHandleRepoIface * repo,const GArray * handles)1362 add_members_in_array (GHashTable *contact_ids,
1363 TpHandleRepoIface *repo,
1364 const GArray *handles)
1365 {
1366 guint i;
1367
1368 for (i = 0; i < handles->len; i++)
1369 {
1370 TpHandle handle = g_array_index (handles, TpHandle, i);
1371 const gchar *id = tp_handle_inspect (repo, handle);
1372
1373 g_hash_table_insert (contact_ids, GUINT_TO_POINTER (handle), (gchar *) id);
1374 }
1375 }
1376
1377
1378 static gboolean
maybe_add_contact_ids(TpGroupMixin * mixin,const GArray * add,const GArray * local_pending,const GArray * remote_pending,TpHandle actor,GHashTable * details)1379 maybe_add_contact_ids (TpGroupMixin *mixin,
1380 const GArray *add,
1381 const GArray *local_pending,
1382 const GArray *remote_pending,
1383 TpHandle actor,
1384 GHashTable *details)
1385 {
1386 GHashTable *contact_ids;
1387
1388 /* If the library user had its own ideas about which members' IDs to include
1389 * in the change details, we'll leave that intact.
1390 */
1391 if (tp_asv_lookup (details, "contact-ids") != NULL)
1392 return FALSE;
1393
1394 /* The library user didn't include the new members' IDs in details; let's add
1395 * the IDs of the handles being added to the group (but not removed, as per
1396 * the spec) and of the actor.
1397 */
1398 contact_ids = g_hash_table_new (NULL, NULL);
1399
1400 add_members_in_array (contact_ids, mixin->handle_repo, add);
1401 add_members_in_array (contact_ids, mixin->handle_repo, local_pending);
1402 add_members_in_array (contact_ids, mixin->handle_repo, remote_pending);
1403
1404 if (actor != 0)
1405 {
1406 const gchar *id = tp_handle_inspect (mixin->handle_repo, actor);
1407
1408 g_hash_table_insert (contact_ids, GUINT_TO_POINTER (actor), (gchar *) id);
1409 }
1410
1411 g_hash_table_insert (details, "contact-ids",
1412 tp_g_value_slice_new_take_boxed (TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP,
1413 contact_ids));
1414
1415 return TRUE;
1416 }
1417
1418
1419 static void
remove_contact_ids(GHashTable * details)1420 remove_contact_ids (GHashTable *details)
1421 {
1422 GValue *contact_ids_v = g_hash_table_lookup (details, "contact-ids");
1423
1424 g_assert (contact_ids_v != NULL);
1425 g_hash_table_steal (details, "contact-ids");
1426
1427 tp_g_value_slice_free (contact_ids_v);
1428 }
1429
1430
1431 static void
emit_members_changed_signals(GObject * channel,const gchar * message,const GArray * add,const GArray * del,const GArray * local_pending,const GArray * remote_pending,TpHandle actor,TpChannelGroupChangeReason reason,const GHashTable * details)1432 emit_members_changed_signals (GObject *channel,
1433 const gchar *message,
1434 const GArray *add,
1435 const GArray *del,
1436 const GArray *local_pending,
1437 const GArray *remote_pending,
1438 TpHandle actor,
1439 TpChannelGroupChangeReason reason,
1440 const GHashTable *details)
1441 {
1442 TpGroupMixin *mixin = TP_GROUP_MIXIN (channel);
1443 GHashTable *details_ = (GHashTable *) details; /* Cast the pain away! */
1444 gboolean added_contact_ids;
1445
1446 if (DEBUGGING)
1447 {
1448 gchar *add_str, *rem_str, *local_str, *remote_str;
1449
1450 add_str = member_array_to_string (mixin->handle_repo, add);
1451 rem_str = member_array_to_string (mixin->handle_repo, del);
1452 local_str = member_array_to_string (mixin->handle_repo, local_pending);
1453 remote_str = member_array_to_string (mixin->handle_repo, remote_pending);
1454
1455 DEBUG ("emitting members changed\n"
1456 " message : \"%s\"\n"
1457 " added : %s\n"
1458 " removed : %s\n"
1459 " local_pending : %s\n"
1460 " remote_pending: %s\n"
1461 " actor : %u\n"
1462 " reason : %u: %s\n",
1463 message, add_str, rem_str, local_str, remote_str,
1464 actor, reason, group_change_reason_str (reason));
1465
1466 g_free (add_str);
1467 g_free (rem_str);
1468 g_free (local_str);
1469 g_free (remote_str);
1470 }
1471
1472 added_contact_ids = maybe_add_contact_ids (mixin, add, local_pending,
1473 remote_pending, actor, details_);
1474
1475 tp_svc_channel_interface_group_emit_members_changed (channel, message,
1476 add, del, local_pending, remote_pending, actor, reason);
1477 tp_svc_channel_interface_group_emit_members_changed_detailed (channel,
1478 add, del, local_pending, remote_pending, details_);
1479
1480 if (mixin->priv->externals != NULL)
1481 {
1482 guint i;
1483
1484 for (i = 0; i < mixin->priv->externals->len; i++)
1485 {
1486 GObject *external = g_ptr_array_index (mixin->priv->externals, i);
1487
1488 tp_svc_channel_interface_group_emit_members_changed (external,
1489 message, add, del, local_pending, remote_pending, actor, reason);
1490 tp_svc_channel_interface_group_emit_members_changed_detailed (
1491 external, add, del, local_pending, remote_pending, details_);
1492 }
1493 }
1494
1495 if (added_contact_ids)
1496 remove_contact_ids (details_);
1497 }
1498
1499
1500 static gboolean
change_members(GObject * obj,const gchar * message,const TpIntset * add,const TpIntset * del,const TpIntset * add_local_pending,const TpIntset * add_remote_pending,TpHandle actor,TpChannelGroupChangeReason reason,const GHashTable * details)1501 change_members (GObject *obj,
1502 const gchar *message,
1503 const TpIntset *add,
1504 const TpIntset *del,
1505 const TpIntset *add_local_pending,
1506 const TpIntset *add_remote_pending,
1507 TpHandle actor,
1508 TpChannelGroupChangeReason reason,
1509 const GHashTable *details)
1510 {
1511 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
1512 TpIntset *new_add, *new_remove, *new_local_pending,
1513 *new_remote_pending, *tmp, *tmp2, *empty;
1514 gboolean ret;
1515
1516 empty = tp_intset_new ();
1517
1518 if (message == NULL)
1519 message = "";
1520
1521 if (add == NULL)
1522 add = empty;
1523
1524 if (del == NULL)
1525 del = empty;
1526
1527 if (add_local_pending == NULL)
1528 add_local_pending = empty;
1529
1530 if (add_remote_pending == NULL)
1531 add_remote_pending = empty;
1532
1533 /* remember the actor handle before any handle unreffing happens */
1534 if (actor)
1535 {
1536 tp_handle_set_add (mixin->priv->actors, actor);
1537 }
1538
1539 /* members + add */
1540 new_add = tp_handle_set_update (mixin->members, add);
1541
1542 /* members - del */
1543 new_remove = tp_handle_set_difference_update (mixin->members, del);
1544
1545 /* members - add_local_pending */
1546 tmp = tp_handle_set_difference_update (mixin->members, add_local_pending);
1547 tp_intset_destroy (tmp);
1548
1549 /* members - add_remote_pending */
1550 tmp = tp_handle_set_difference_update (mixin->members, add_remote_pending);
1551 tp_intset_destroy (tmp);
1552
1553
1554 /* local pending + add_local_pending */
1555 new_local_pending = tp_handle_set_update (mixin->local_pending,
1556 add_local_pending);
1557 local_pending_added (mixin, add_local_pending, actor, reason, message);
1558
1559 /* local pending - add */
1560 tmp = tp_handle_set_difference_update (mixin->local_pending, add);
1561 local_pending_remove (mixin, tmp);
1562 tp_intset_destroy (tmp);
1563
1564 /* local pending - del */
1565 tmp = tp_handle_set_difference_update (mixin->local_pending, del);
1566 local_pending_remove (mixin, tmp);
1567
1568 tmp2 = tp_intset_union (new_remove, tmp);
1569 tp_intset_destroy (new_remove);
1570 tp_intset_destroy (tmp);
1571 new_remove = tmp2;
1572
1573 /* local pending - add_remote_pending */
1574 tmp = tp_handle_set_difference_update (mixin->local_pending,
1575 add_remote_pending);
1576 local_pending_remove (mixin, tmp);
1577 tp_intset_destroy (tmp);
1578
1579
1580 /* remote pending + add_remote_pending */
1581 new_remote_pending = tp_handle_set_update (mixin->remote_pending,
1582 add_remote_pending);
1583
1584 /* remote pending - add */
1585 tmp = tp_handle_set_difference_update (mixin->remote_pending, add);
1586 tp_intset_destroy (tmp);
1587
1588 /* remote pending - del */
1589 tmp = tp_handle_set_difference_update (mixin->remote_pending, del);
1590 tmp2 = tp_intset_union (new_remove, tmp);
1591 tp_intset_destroy (new_remove);
1592 tp_intset_destroy (tmp);
1593 new_remove = tmp2;
1594
1595 /* remote pending - local_pending */
1596 tmp = tp_handle_set_difference_update (mixin->remote_pending,
1597 add_local_pending);
1598 tp_intset_destroy (tmp);
1599
1600 if (tp_intset_size (new_add) > 0 ||
1601 tp_intset_size (new_remove) > 0 ||
1602 tp_intset_size (new_local_pending) > 0 ||
1603 tp_intset_size (new_remote_pending) > 0)
1604 {
1605 GArray *arr_add, *arr_remove, *arr_local, *arr_remote;
1606 GArray *arr_owners_removed;
1607
1608 /* translate intsets to arrays */
1609 arr_add = tp_intset_to_array (new_add);
1610 arr_remove = tp_intset_to_array (new_remove);
1611 arr_local = tp_intset_to_array (new_local_pending);
1612 arr_remote = tp_intset_to_array (new_remote_pending);
1613
1614 /* remove any handle owner mappings */
1615 arr_owners_removed = remove_handle_owners_if_exist (obj, arr_remove);
1616
1617 /* emit signals */
1618 emit_members_changed_signals (obj, message, arr_add, arr_remove,
1619 arr_local, arr_remote, actor, reason, details);
1620
1621 if (arr_owners_removed->len > 0)
1622 {
1623 GHashTable *empty_hash_table = g_hash_table_new (NULL, NULL);
1624
1625 tp_svc_channel_interface_group_emit_handle_owners_changed (obj,
1626 empty_hash_table, arr_owners_removed);
1627 tp_svc_channel_interface_group_emit_handle_owners_changed_detailed (
1628 obj, empty_hash_table, arr_owners_removed, empty_hash_table);
1629
1630 if (mixin->priv->externals != NULL)
1631 {
1632 guint i;
1633
1634 for (i = 0; i < mixin->priv->externals->len; i++)
1635 {
1636 tp_svc_channel_interface_group_emit_handle_owners_changed (
1637 g_ptr_array_index (mixin->priv->externals, i),
1638 empty_hash_table, arr_owners_removed);
1639 tp_svc_channel_interface_group_emit_handle_owners_changed_detailed (
1640 g_ptr_array_index (mixin->priv->externals, i),
1641 empty_hash_table, arr_owners_removed, empty_hash_table);
1642 }
1643 }
1644
1645 g_hash_table_unref (empty_hash_table);
1646 }
1647
1648 /* free arrays */
1649 g_array_unref (arr_add);
1650 g_array_unref (arr_remove);
1651 g_array_unref (arr_local);
1652 g_array_unref (arr_remote);
1653 g_array_unref (arr_owners_removed);
1654
1655 ret = TRUE;
1656 }
1657 else
1658 {
1659 DEBUG ("not emitting signal, nothing changed");
1660
1661 ret = FALSE;
1662 }
1663
1664 /* free intsets */
1665 tp_intset_destroy (new_add);
1666 tp_intset_destroy (new_remove);
1667 tp_intset_destroy (new_local_pending);
1668 tp_intset_destroy (new_remote_pending);
1669 tp_intset_destroy (empty);
1670
1671 return ret;
1672 }
1673
1674
1675 /**
1676 * tp_group_mixin_change_members: (skip)
1677 * @obj: An object implementing the group interface using this mixin
1678 * @message: A message to be sent to the affected contacts if possible;
1679 * %NULL is allowed, and is mapped to an empty string
1680 * @add: A set of contact handles to be added to the members (if not
1681 * already present) and removed from local pending and remote pending
1682 * (if present)
1683 * @del: A set of contact handles to be removed from members,
1684 * local pending or remote pending, wherever they are present
1685 * @add_local_pending: A set of contact handles to be added to local pending,
1686 * and removed from members and remote pending
1687 * @add_remote_pending: A set of contact handles to be added to remote pending,
1688 * and removed from members and local pending
1689 * @actor: The handle of the contact responsible for this change
1690 * @reason: The reason for this change
1691 *
1692 * Change the sets of members as given by the arguments, and emit the
1693 * MembersChanged and MembersChangedDetailed signals if the changes were not a
1694 * no-op.
1695 *
1696 * This function must be called in response to events on the underlying
1697 * IM protocol, and must not be called in direct response to user input;
1698 * it does not respect the permissions flags, but changes the group directly.
1699 *
1700 * If any two of add, del, add_local_pending and add_remote_pending have
1701 * a non-empty intersection, the result is undefined. Don't do that.
1702 *
1703 * Each of the TpIntset arguments may be %NULL, which is treated as
1704 * equivalent to an empty set.
1705 *
1706 * Returns: %TRUE if the group was changed and the MembersChanged(Detailed)
1707 * signals were emitted; %FALSE if nothing actually changed and the signals
1708 * were suppressed.
1709 */
1710 gboolean
tp_group_mixin_change_members(GObject * obj,const gchar * message,const TpIntset * add,const TpIntset * del,const TpIntset * add_local_pending,const TpIntset * add_remote_pending,TpHandle actor,TpChannelGroupChangeReason reason)1711 tp_group_mixin_change_members (GObject *obj,
1712 const gchar *message,
1713 const TpIntset *add,
1714 const TpIntset *del,
1715 const TpIntset *add_local_pending,
1716 const TpIntset *add_remote_pending,
1717 TpHandle actor,
1718 TpChannelGroupChangeReason reason)
1719 {
1720 GHashTable *details = g_hash_table_new_full (g_str_hash, g_str_equal,
1721 NULL, (GDestroyNotify) tp_g_value_slice_free);
1722 gboolean ret;
1723
1724 if (actor != 0)
1725 {
1726 g_hash_table_insert (details, "actor",
1727 tp_g_value_slice_new_uint (actor));
1728 }
1729
1730 if (reason != TP_CHANNEL_GROUP_CHANGE_REASON_NONE)
1731 {
1732 g_hash_table_insert (details, "change-reason",
1733 tp_g_value_slice_new_uint (reason));
1734 }
1735
1736 if (message != NULL && message[0] != '\0')
1737 {
1738 g_hash_table_insert (details, "message",
1739 tp_g_value_slice_new_string (message));
1740 }
1741
1742 ret = change_members (obj, message, add, del, add_local_pending,
1743 add_remote_pending, actor, reason, details);
1744
1745 g_hash_table_unref (details);
1746 return ret;
1747 }
1748
1749 /**
1750 * tp_group_mixin_change_members_detailed: (skip)
1751 * @obj: An object implementing the group interface using this mixin
1752 * @add: A set of contact handles to be added to the members (if not
1753 * already present) and removed from local pending and remote pending
1754 * (if present)
1755 * @del: A set of contact handles to be removed from members,
1756 * local pending or remote pending, wherever they are present
1757 * @add_local_pending: A set of contact handles to be added to local pending,
1758 * and removed from members and remote pending
1759 * @add_remote_pending: A set of contact handles to be added to remote pending,
1760 * and removed from members and local pending
1761 * @details: a map from strings to GValues detailing the change
1762 *
1763 * Change the sets of members as given by the arguments, and emit the
1764 * MembersChanged and MembersChangedDetailed signals if the changes were not a
1765 * no-op.
1766 *
1767 * This function must be called in response to events on the underlying
1768 * IM protocol, and must not be called in direct response to user input;
1769 * it does not respect the permissions flags, but changes the group directly.
1770 *
1771 * If any two of add, del, add_local_pending and add_remote_pending have
1772 * a non-empty intersection, the result is undefined. Don't do that.
1773 *
1774 * Each of the TpIntset arguments may be %NULL, which is treated as
1775 * equivalent to an empty set.
1776 *
1777 * details may contain, among other entries, the well-known
1778 * keys (and corresponding type, wrapped in a GValue) defined by the
1779 * Group.MembersChangedDetailed signal's specification; these include "actor"
1780 * (a handle as G_TYPE_UINT), "change-reason" (an element of
1781 * #TpChannelGroupChangeReason as G_TYPE_UINT), "message" (G_TYPE_STRING),
1782 * "error" (G_TYPE_STRING), "debug-message" (G_TYPE_STRING).
1783 *
1784 * If all of the information in details could be passed to
1785 * tp_group_mixin_change_members() then calling this function instead provides
1786 * no benefit. Calling this function without setting
1787 * #TP_CHANNEL_GROUP_FLAG_MEMBERS_CHANGED_DETAILED with
1788 * tp_group_mixin_change_members() first is not very useful, as clients will
1789 * not know to listen for MembersChangedDetailed and thus will miss the
1790 * details.
1791 *
1792 * Returns: %TRUE if the group was changed and the MembersChanged(Detailed)
1793 * signals were emitted; %FALSE if nothing actually changed and the signals
1794 * were suppressed.
1795 *
1796 * Since: 0.7.21
1797 */
1798 gboolean
tp_group_mixin_change_members_detailed(GObject * obj,const TpIntset * add,const TpIntset * del,const TpIntset * add_local_pending,const TpIntset * add_remote_pending,const GHashTable * details)1799 tp_group_mixin_change_members_detailed (GObject *obj,
1800 const TpIntset *add,
1801 const TpIntset *del,
1802 const TpIntset *add_local_pending,
1803 const TpIntset *add_remote_pending,
1804 const GHashTable *details)
1805 {
1806 const gchar *message;
1807 TpHandle actor;
1808 TpChannelGroupChangeReason reason;
1809 gboolean valid;
1810
1811 g_return_val_if_fail (details != NULL, FALSE);
1812
1813 /* For each detail we're extracting for the benefit of old-school
1814 * MembersChanged, warn if it's present but badly typed.
1815 */
1816
1817 message = tp_asv_get_string (details, "message");
1818 g_warn_if_fail (message != NULL || tp_asv_lookup (details, "message") == NULL);
1819
1820 /* change_members will cry (via tp_handle_set_add) if actor is non-zero and
1821 * invalid.
1822 */
1823 actor = tp_asv_get_uint32 (details, "actor", &valid);
1824 g_warn_if_fail (valid || tp_asv_lookup (details, "actor") == NULL);
1825
1826 reason = tp_asv_get_uint32 (details, "change-reason", &valid);
1827 g_warn_if_fail (valid || tp_asv_lookup (details, "change-reason") == NULL);
1828
1829 return change_members (obj, message, add, del, add_local_pending,
1830 add_remote_pending, actor, reason, details);
1831 }
1832
1833 /**
1834 * tp_group_mixin_add_handle_owner: (skip)
1835 * @obj: A GObject implementing the group interface with this mixin
1836 * @local_handle: A contact handle valid within this group (may not be 0)
1837 * @owner_handle: A contact handle valid globally, or 0 if the owner of the
1838 * @local_handle is unknown
1839 *
1840 * Note that the given local handle is an alias within this group
1841 * for the given globally-valid handle. It will be returned from subsequent
1842 * GetHandleOwner queries where appropriate.
1843 *
1844 * Changed in 0.7.10: The @owner_handle may be 0. To comply with telepathy-spec
1845 * 0.17.6, before adding any channel-specific handle to the members,
1846 * local-pending members or remote-pending members, you must call either
1847 * this function or tp_group_mixin_add_handle_owners().
1848 */
1849 void
tp_group_mixin_add_handle_owner(GObject * obj,TpHandle local_handle,TpHandle owner_handle)1850 tp_group_mixin_add_handle_owner (GObject *obj,
1851 TpHandle local_handle,
1852 TpHandle owner_handle)
1853 {
1854 GHashTable *tmp;
1855
1856 g_return_if_fail (local_handle != 0);
1857
1858 tmp = g_hash_table_new (g_direct_hash, g_direct_equal);
1859 g_hash_table_insert (tmp, GUINT_TO_POINTER (local_handle),
1860 GUINT_TO_POINTER (owner_handle));
1861
1862 tp_group_mixin_add_handle_owners (obj, tmp);
1863
1864 g_hash_table_unref (tmp);
1865 }
1866
1867
1868 static void
add_us_mapping_for_handleset(GHashTable * map,TpHandleRepoIface * repo,TpHandleSet * handles)1869 add_us_mapping_for_handleset (GHashTable *map,
1870 TpHandleRepoIface *repo,
1871 TpHandleSet *handles)
1872 {
1873 TpIntset *set;
1874 TpIntsetFastIter iter;
1875 TpHandle handle;
1876
1877 set = tp_handle_set_peek (handles);
1878 tp_intset_fast_iter_init (&iter, set);
1879 while (tp_intset_fast_iter_next (&iter, &handle))
1880 g_hash_table_insert (map, GUINT_TO_POINTER (handle),
1881 (gchar *) tp_handle_inspect (repo, handle));
1882 }
1883
1884 static void
add_us_mapping_for_owners_map(GHashTable * map,TpHandleRepoIface * repo,GHashTable * owners)1885 add_us_mapping_for_owners_map (GHashTable *map,
1886 TpHandleRepoIface *repo,
1887 GHashTable *owners)
1888 {
1889 GHashTableIter iter;
1890 gpointer key, value;
1891
1892 g_hash_table_iter_init (&iter, owners);
1893 while (g_hash_table_iter_next (&iter, &key, &value))
1894 {
1895 TpHandle local_handle = GPOINTER_TO_UINT (key);
1896 TpHandle owner_handle = GPOINTER_TO_UINT (value);
1897
1898 g_hash_table_insert (map, key,
1899 (gchar *) tp_handle_inspect (repo, local_handle));
1900
1901 if (owner_handle != 0)
1902 g_hash_table_insert (map, value,
1903 (gchar *) tp_handle_inspect (repo, owner_handle));
1904 }
1905 }
1906
1907 static void
add_handle_owners_helper(gpointer key,gpointer value,gpointer user_data)1908 add_handle_owners_helper (gpointer key,
1909 gpointer value,
1910 gpointer user_data)
1911 {
1912 TpHandle local_handle = GPOINTER_TO_UINT (key);
1913 TpGroupMixin *mixin = user_data;
1914
1915 g_return_if_fail (local_handle != 0);
1916
1917 g_hash_table_insert (mixin->priv->handle_owners, key, value);
1918 }
1919
1920 /**
1921 * tp_group_mixin_add_handle_owners: (skip)
1922 * @obj: A GObject implementing the group interface with this mixin
1923 * @local_to_owner_handle: A map from contact handles valid within this group
1924 * (which may not be 0) to either contact handles valid globally, or 0 if the
1925 * owner of the corresponding key is unknown; all handles are stored using
1926 * GUINT_TO_POINTER
1927 *
1928 * Note that the given local handles are aliases within this group
1929 * for the given globally-valid handles.
1930 *
1931 * To comply with telepathy-spec 0.17.6, before adding any channel-specific
1932 * handle to the members, local-pending members or remote-pending members, you
1933 * must call either this function or tp_group_mixin_add_handle_owner().
1934 *
1935 * Since: 0.7.10
1936 */
1937 void
tp_group_mixin_add_handle_owners(GObject * obj,GHashTable * local_to_owner_handle)1938 tp_group_mixin_add_handle_owners (GObject *obj,
1939 GHashTable *local_to_owner_handle)
1940 {
1941 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
1942 GArray *empty_array;
1943 GHashTable *ids = g_hash_table_new (NULL, NULL);
1944
1945 if (g_hash_table_size (local_to_owner_handle) == 0)
1946 return;
1947
1948 empty_array = g_array_sized_new (FALSE, FALSE, sizeof (guint), 0);
1949
1950 g_hash_table_foreach (local_to_owner_handle, add_handle_owners_helper,
1951 mixin);
1952
1953 tp_svc_channel_interface_group_emit_handle_owners_changed (obj,
1954 local_to_owner_handle, empty_array);
1955
1956 add_us_mapping_for_owners_map (ids, mixin->handle_repo, local_to_owner_handle);
1957 tp_svc_channel_interface_group_emit_handle_owners_changed_detailed (obj,
1958 local_to_owner_handle, empty_array, ids);
1959
1960 g_array_unref (empty_array);
1961 g_hash_table_unref (ids);
1962 }
1963
1964
1965 static GArray *
remove_handle_owners_if_exist(GObject * obj,GArray * array)1966 remove_handle_owners_if_exist (GObject *obj,
1967 GArray *array)
1968 {
1969 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
1970 TpGroupMixinPrivate *priv = mixin->priv;
1971 guint i;
1972 GArray *ret;
1973
1974 ret = g_array_sized_new (FALSE, FALSE, sizeof (guint), array->len);
1975
1976 for (i = 0; i < array->len; i++)
1977 {
1978 TpHandle handle = g_array_index (array, guint, i);
1979 gpointer local_handle, owner_handle;
1980
1981 g_assert (handle != 0);
1982
1983 if (g_hash_table_lookup_extended (priv->handle_owners,
1984 GUINT_TO_POINTER (handle),
1985 &local_handle,
1986 &owner_handle))
1987 {
1988 g_assert (GPOINTER_TO_UINT (local_handle) == handle);
1989 g_array_append_val (ret, handle);
1990 g_hash_table_remove (priv->handle_owners, GUINT_TO_POINTER (handle));
1991 }
1992 }
1993
1994 return ret;
1995 }
1996
1997 static GHashTable *
dup_member_identifiers(GObject * obj)1998 dup_member_identifiers (GObject *obj)
1999 {
2000 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
2001 GHashTable *ret = g_hash_table_new (NULL, NULL);
2002
2003 g_hash_table_insert (ret, GUINT_TO_POINTER (mixin->self_handle),
2004 (gchar *) tp_handle_inspect (mixin->handle_repo, mixin->self_handle));
2005
2006 add_us_mapping_for_handleset (ret, mixin->handle_repo, mixin->priv->actors);
2007 add_us_mapping_for_handleset (ret, mixin->handle_repo, mixin->members);
2008 add_us_mapping_for_handleset (ret, mixin->handle_repo, mixin->local_pending);
2009 add_us_mapping_for_handleset (ret, mixin->handle_repo, mixin->remote_pending);
2010
2011 add_us_mapping_for_owners_map (ret, mixin->handle_repo,
2012 mixin->priv->handle_owners);
2013
2014 return ret;
2015 }
2016
2017 /**
2018 * tp_group_mixin_iface_init: (skip)
2019 * @g_iface: A #TpSvcChannelInterfaceGroupClass
2020 * @iface_data: Unused
2021 *
2022 * Fill in the vtable entries needed to implement the group interface using
2023 * this mixin. This function should usually be called via
2024 * G_IMPLEMENT_INTERFACE.
2025 */
2026 void
tp_group_mixin_iface_init(gpointer g_iface,gpointer iface_data)2027 tp_group_mixin_iface_init (gpointer g_iface, gpointer iface_data)
2028 {
2029 TpSvcChannelInterfaceGroupClass *klass = g_iface;
2030
2031 #define IMPLEMENT(x) tp_svc_channel_interface_group_implement_##x (klass,\
2032 tp_group_mixin_##x##_async)
2033 IMPLEMENT(add_members);
2034 IMPLEMENT(get_all_members);
2035 IMPLEMENT(get_group_flags);
2036 IMPLEMENT(get_handle_owners);
2037 IMPLEMENT(get_local_pending_members);
2038 IMPLEMENT(get_local_pending_members_with_info);
2039 IMPLEMENT(get_members);
2040 IMPLEMENT(get_remote_pending_members);
2041 IMPLEMENT(get_self_handle);
2042 IMPLEMENT(remove_members);
2043 IMPLEMENT(remove_members_with_reason);
2044 #undef IMPLEMENT
2045 }
2046
2047
2048 enum {
2049 MIXIN_DP_GROUP_FLAGS,
2050 MIXIN_DP_HANDLE_OWNERS,
2051 MIXIN_DP_LOCAL_PENDING_MEMBERS,
2052 MIXIN_DP_MEMBERS,
2053 MIXIN_DP_REMOTE_PENDING_MEMBERS,
2054 MIXIN_DP_SELF_HANDLE,
2055 MIXIN_DP_MEMBER_IDENTIFIERS,
2056 NUM_MIXIN_DBUS_PROPERTIES
2057 };
2058
2059
2060 /**
2061 * tp_group_mixin_get_dbus_property: (skip)
2062 * @object: An object with this mixin
2063 * @interface: Must be %TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
2064 * @name: A quark representing the D-Bus property name, either
2065 * "GroupFlags", "HandleOwners", "LocalPendingMembers", "Members",
2066 * "RemotePendingMembers" or "SelfHandle"
2067 * @value: A GValue pre-initialized to the right type, into which to put the
2068 * value
2069 * @unused: Ignored
2070 *
2071 * An implementation of #TpDBusPropertiesMixinGetter which assumes that the
2072 * @object has the group mixin. It can only be used for the Group interface.
2073 *
2074 * Since: 0.7.10
2075 */
2076 void
tp_group_mixin_get_dbus_property(GObject * object,GQuark interface,GQuark name,GValue * value,gpointer unused G_GNUC_UNUSED)2077 tp_group_mixin_get_dbus_property (GObject *object,
2078 GQuark interface,
2079 GQuark name,
2080 GValue *value,
2081 gpointer unused G_GNUC_UNUSED)
2082 {
2083 TpGroupMixin *mixin;
2084 static GQuark q[NUM_MIXIN_DBUS_PROPERTIES] = { 0 };
2085
2086 if (G_UNLIKELY (q[0] == 0))
2087 {
2088 q[MIXIN_DP_GROUP_FLAGS] = g_quark_from_static_string ("GroupFlags");
2089 q[MIXIN_DP_HANDLE_OWNERS] = g_quark_from_static_string ("HandleOwners");
2090 q[MIXIN_DP_LOCAL_PENDING_MEMBERS] = g_quark_from_static_string (
2091 "LocalPendingMembers");
2092 q[MIXIN_DP_MEMBERS] = g_quark_from_static_string ("Members");
2093 q[MIXIN_DP_REMOTE_PENDING_MEMBERS] = g_quark_from_static_string (
2094 "RemotePendingMembers");
2095 q[MIXIN_DP_SELF_HANDLE] = g_quark_from_static_string ("SelfHandle");
2096 q[MIXIN_DP_MEMBER_IDENTIFIERS] = g_quark_from_static_string ("MemberIdentifiers");
2097 }
2098
2099 g_return_if_fail (object != NULL);
2100 mixin = TP_GROUP_MIXIN (object);
2101 g_return_if_fail (mixin != NULL);
2102 g_return_if_fail (interface == TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP);
2103 g_return_if_fail (name != 0);
2104 g_return_if_fail (value != NULL);
2105
2106 if (name == q[MIXIN_DP_GROUP_FLAGS])
2107 {
2108 g_return_if_fail (G_VALUE_HOLDS_UINT (value));
2109 g_value_set_uint (value, mixin->group_flags);
2110 }
2111 else if (name == q[MIXIN_DP_HANDLE_OWNERS])
2112 {
2113 g_return_if_fail (G_VALUE_HOLDS (value, TP_HASH_TYPE_HANDLE_OWNER_MAP));
2114 g_value_set_boxed (value, mixin->priv->handle_owners);
2115 }
2116 else if (name == q[MIXIN_DP_LOCAL_PENDING_MEMBERS])
2117 {
2118 GPtrArray *ret = NULL;
2119 gboolean success;
2120
2121 g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
2122 success = tp_group_mixin_get_local_pending_members_with_info (object,
2123 &ret, NULL);
2124 g_assert (success); /* as of 0.7.8, cannot fail */
2125 g_value_take_boxed (value, ret);
2126 }
2127 else if (name == q[MIXIN_DP_MEMBERS])
2128 {
2129 GArray *ret = NULL;
2130 gboolean success;
2131
2132 g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
2133 success = tp_group_mixin_get_members (object, &ret, NULL);
2134 g_assert (success); /* as of 0.7.8, cannot fail */
2135 g_value_take_boxed (value, ret);
2136 }
2137 else if (name == q[MIXIN_DP_REMOTE_PENDING_MEMBERS])
2138 {
2139 GArray *ret = NULL;
2140 gboolean success;
2141
2142 g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
2143 success = tp_group_mixin_get_remote_pending_members (object,
2144 &ret, NULL);
2145 g_assert (success); /* as of 0.7.8, cannot fail */
2146 g_value_take_boxed (value, ret);
2147 }
2148 else if (name == q[MIXIN_DP_SELF_HANDLE])
2149 {
2150 g_return_if_fail (G_VALUE_HOLDS_UINT (value));
2151 g_value_set_uint (value, mixin->self_handle);
2152 }
2153 else if (name == q[MIXIN_DP_MEMBER_IDENTIFIERS])
2154 {
2155 g_return_if_fail (G_VALUE_HOLDS (value, TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP));
2156 g_value_take_boxed (value, dup_member_identifiers (object));
2157 }
2158 else
2159 {
2160 g_return_if_reached ();
2161 }
2162 }
2163
2164 static TpDBusPropertiesMixinPropImpl known_group_props[] = {
2165 { "GroupFlags", NULL, NULL },
2166 { "HandleOwners", NULL, NULL },
2167 { "LocalPendingMembers", NULL, NULL },
2168 { "Members", NULL, NULL },
2169 { "RemotePendingMembers", NULL, NULL },
2170 { "SelfHandle", NULL, NULL },
2171 { "MemberIdentifiers", NULL, NULL },
2172 { NULL }
2173 };
2174
2175 /**
2176 * tp_group_mixin_init_dbus_properties: (skip)
2177 * @cls: The class of an object with this mixin
2178 *
2179 * Set up #TpDBusPropertiesMixinClass to use this mixin's implementation of
2180 * the Group interface's properties.
2181 *
2182 * This uses tp_group_mixin_get_dbus_property() as the property getter and
2183 * sets up a list of the supported properties for it. Having called this, you
2184 * should add #TP_CHANNEL_GROUP_FLAG_PROPERTIES to any channels of this class
2185 * with tp_group_mixin_change_flags() to indicate that the DBus properties are
2186 * available.
2187 *
2188 * Since: 0.7.10
2189 */
2190 void
tp_group_mixin_init_dbus_properties(GObjectClass * cls)2191 tp_group_mixin_init_dbus_properties (GObjectClass *cls)
2192 {
2193
2194 tp_dbus_properties_mixin_implement_interface (cls,
2195 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP, tp_group_mixin_get_dbus_property,
2196 NULL, known_group_props);
2197 }
2198
2199
2200 #define TP_EXTERNAL_GROUP_MIXIN_OBJ(o) \
2201 ((GObject *) g_object_get_qdata (o, \
2202 _external_group_mixin_get_obj_quark ()))
2203
2204 static GQuark
_external_group_mixin_get_obj_quark(void)2205 _external_group_mixin_get_obj_quark (void)
2206 {
2207 static GQuark quark = 0;
2208 if (!quark)
2209 quark = g_quark_from_static_string
2210 ("TpExternalGroupMixinQuark");
2211 return quark;
2212 }
2213
2214 /**
2215 * tp_external_group_mixin_init: (skip)
2216 * @obj: An object implementing the groups interface using an external group
2217 * mixin
2218 * @obj_with_mixin: A GObject with the group mixin
2219 *
2220 * Fill in the qdata needed to implement the group interface using
2221 * the group mixin of another object. This function should usually be called
2222 * in the instance constructor.
2223 *
2224 * Since: 0.5.13
2225 */
2226 void
tp_external_group_mixin_init(GObject * obj,GObject * obj_with_mixin)2227 tp_external_group_mixin_init (GObject *obj, GObject *obj_with_mixin)
2228 {
2229 g_object_ref (obj_with_mixin);
2230 g_object_set_qdata (obj, _external_group_mixin_get_obj_quark (),
2231 obj_with_mixin);
2232 tp_group_mixin_add_external (obj_with_mixin, obj);
2233 }
2234
2235 /**
2236 * tp_external_group_mixin_finalize: (skip)
2237 * @obj: An object implementing the groups interface using an external group
2238 * mixin
2239 *
2240 * Remove the external group mixin. This function should usually be called
2241 * in the dispose or finalize function.
2242 *
2243 * Since: 0.5.13
2244 */
2245 void
tp_external_group_mixin_finalize(GObject * obj)2246 tp_external_group_mixin_finalize (GObject *obj)
2247 {
2248 GObject *obj_with_mixin = g_object_steal_qdata (obj,
2249 _external_group_mixin_get_obj_quark ());
2250
2251 tp_group_mixin_remove_external (obj_with_mixin, obj);
2252 g_object_unref (obj_with_mixin);
2253 }
2254
2255 /**
2256 * tp_external_group_mixin_init_dbus_properties: (skip)
2257 * @cls: The class of an object with this mixin
2258 *
2259 * Set up #TpDBusPropertiesMixinClass to use this mixin's implementation of
2260 * the Group interface's properties.
2261 *
2262 * This uses tp_group_mixin_get_dbus_property() as the property getter and
2263 * sets up a list of the supported properties for it. Having called this, you
2264 * should add #TP_CHANNEL_GROUP_FLAG_PROPERTIES to channels containing the
2265 * mixin used by this class with tp_group_mixin_change_flags() to indicate that
2266 * the DBus properties are available.
2267 *
2268 * Since: 0.7.10
2269 */
2270 void
tp_external_group_mixin_init_dbus_properties(GObjectClass * cls)2271 tp_external_group_mixin_init_dbus_properties (GObjectClass *cls)
2272 {
2273
2274 tp_dbus_properties_mixin_implement_interface (cls,
2275 TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP,
2276 tp_external_group_mixin_get_dbus_property,
2277 NULL, known_group_props);
2278 }
2279
2280 /**
2281 * tp_external_group_mixin_get_dbus_property: (skip)
2282 * @object: An object with this mixin
2283 * @interface: Must be %TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP
2284 * @name: A quark representing the D-Bus property name, either
2285 * "GroupFlags", "HandleOwners", "LocalPendingMembers", "Members",
2286 * "RemotePendingMembers" or "SelfHandle"
2287 * @value: A GValue pre-initialized to the right type, into which to put the
2288 * value
2289 * @unused: Ignored
2290 *
2291 * An implementation of #TpDBusPropertiesMixinGetter which assumes that the
2292 * @object has the external group mixin. It can only be used for the Group
2293 * interface.
2294 *
2295 * Since: 0.7.10
2296 */
2297 void
tp_external_group_mixin_get_dbus_property(GObject * object,GQuark interface,GQuark name,GValue * value,gpointer unused G_GNUC_UNUSED)2298 tp_external_group_mixin_get_dbus_property (GObject *object,
2299 GQuark interface,
2300 GQuark name,
2301 GValue *value,
2302 gpointer unused G_GNUC_UNUSED)
2303 {
2304 GObject *group = TP_EXTERNAL_GROUP_MIXIN_OBJ (object);
2305
2306 if (group != NULL)
2307 {
2308 tp_group_mixin_get_dbus_property (group, interface, name, value, NULL);
2309 }
2310 else if (G_VALUE_HOLDS_BOXED (value))
2311 {
2312 /* for certain boxed types we need to supply an empty value */
2313
2314 if (G_VALUE_HOLDS (value, TP_HASH_TYPE_HANDLE_OWNER_MAP))
2315 g_value_take_boxed (value, g_hash_table_new (NULL, NULL));
2316 else if (G_VALUE_HOLDS (value, TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP))
2317 g_value_take_boxed (value, g_hash_table_new (NULL, NULL));
2318 else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_UINT_ARRAY))
2319 g_value_take_boxed (value, g_array_sized_new (FALSE, FALSE,
2320 sizeof (guint), 0));
2321 else if (G_VALUE_HOLDS (value, TP_ARRAY_TYPE_LOCAL_PENDING_INFO_LIST))
2322 g_value_take_boxed (value, g_ptr_array_sized_new (0));
2323 }
2324 }
2325
2326 #define EXTERNAL_OR_DIE(var) \
2327 GObject *var = TP_EXTERNAL_GROUP_MIXIN_OBJ ((GObject *) obj); \
2328 \
2329 if (var == NULL) \
2330 { \
2331 GError na = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "I'm sure I " \
2332 "had a group object around here somewhere?" };\
2333 \
2334 dbus_g_method_return_error (context, &na); \
2335 return; \
2336 } \
2337
2338 static void
tp_external_group_mixin_add_members_async(TpSvcChannelInterfaceGroup * obj,const GArray * contacts,const gchar * message,DBusGMethodInvocation * context)2339 tp_external_group_mixin_add_members_async (TpSvcChannelInterfaceGroup *obj,
2340 const GArray *contacts,
2341 const gchar *message,
2342 DBusGMethodInvocation *context)
2343 {
2344 EXTERNAL_OR_DIE (group)
2345 tp_group_mixin_add_members_async ((TpSvcChannelInterfaceGroup *) group,
2346 contacts, message, context);
2347 }
2348
2349 static void
tp_external_group_mixin_get_self_handle_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)2350 tp_external_group_mixin_get_self_handle_async (TpSvcChannelInterfaceGroup *obj,
2351 DBusGMethodInvocation *context)
2352 {
2353 EXTERNAL_OR_DIE (group)
2354 tp_group_mixin_get_self_handle_async ((TpSvcChannelInterfaceGroup *) group,
2355 context);
2356 }
2357
2358 static void
tp_external_group_mixin_get_group_flags_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)2359 tp_external_group_mixin_get_group_flags_async (TpSvcChannelInterfaceGroup *obj,
2360 DBusGMethodInvocation *context)
2361 {
2362 EXTERNAL_OR_DIE (group)
2363 tp_group_mixin_get_group_flags_async ((TpSvcChannelInterfaceGroup *) group,
2364 context);
2365 }
2366
2367 static void
tp_external_group_mixin_get_members_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)2368 tp_external_group_mixin_get_members_async (TpSvcChannelInterfaceGroup *obj,
2369 DBusGMethodInvocation *context)
2370 {
2371 EXTERNAL_OR_DIE (group)
2372 tp_group_mixin_get_members_async ((TpSvcChannelInterfaceGroup *) group,
2373 context);
2374 }
2375
2376 static void
tp_external_group_mixin_get_local_pending_members_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)2377 tp_external_group_mixin_get_local_pending_members_async
2378 (TpSvcChannelInterfaceGroup *obj, DBusGMethodInvocation *context)
2379 {
2380 EXTERNAL_OR_DIE (group)
2381 tp_group_mixin_get_local_pending_members_async
2382 ((TpSvcChannelInterfaceGroup *) group, context);
2383 }
2384
2385 static void
tp_external_group_mixin_get_local_pending_members_with_info_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)2386 tp_external_group_mixin_get_local_pending_members_with_info_async
2387 (TpSvcChannelInterfaceGroup *obj, DBusGMethodInvocation *context)
2388 {
2389 EXTERNAL_OR_DIE (group)
2390 tp_group_mixin_get_local_pending_members_with_info_async
2391 ((TpSvcChannelInterfaceGroup *) group, context);
2392 }
2393
2394 static void
tp_external_group_mixin_get_remote_pending_members_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)2395 tp_external_group_mixin_get_remote_pending_members_async
2396 (TpSvcChannelInterfaceGroup *obj, DBusGMethodInvocation *context)
2397 {
2398 EXTERNAL_OR_DIE (group)
2399 tp_group_mixin_get_remote_pending_members_async
2400 ((TpSvcChannelInterfaceGroup *) group, context);
2401 }
2402
2403 static void
tp_external_group_mixin_get_all_members_async(TpSvcChannelInterfaceGroup * obj,DBusGMethodInvocation * context)2404 tp_external_group_mixin_get_all_members_async (TpSvcChannelInterfaceGroup *obj,
2405 DBusGMethodInvocation *context)
2406 {
2407 EXTERNAL_OR_DIE (group)
2408 tp_group_mixin_get_all_members_async ((TpSvcChannelInterfaceGroup *) group,
2409 context);
2410 }
2411
2412 static void
tp_external_group_mixin_get_handle_owners_async(TpSvcChannelInterfaceGroup * obj,const GArray * handles,DBusGMethodInvocation * context)2413 tp_external_group_mixin_get_handle_owners_async
2414 (TpSvcChannelInterfaceGroup *obj,
2415 const GArray *handles,
2416 DBusGMethodInvocation *context)
2417 {
2418 EXTERNAL_OR_DIE (group)
2419 tp_group_mixin_get_handle_owners_async ((TpSvcChannelInterfaceGroup *) group,
2420 handles, context);
2421 }
2422
2423 static void
tp_external_group_mixin_remove_members_async(TpSvcChannelInterfaceGroup * obj,const GArray * contacts,const gchar * message,DBusGMethodInvocation * context)2424 tp_external_group_mixin_remove_members_async (TpSvcChannelInterfaceGroup *obj,
2425 const GArray *contacts,
2426 const gchar *message,
2427 DBusGMethodInvocation *context)
2428 {
2429 EXTERNAL_OR_DIE (group)
2430 tp_group_mixin_remove_members_with_reason_async
2431 ((TpSvcChannelInterfaceGroup *) group, contacts, message,
2432 TP_CHANNEL_GROUP_CHANGE_REASON_NONE, context);
2433 }
2434
2435
2436 static void
tp_external_group_mixin_remove_members_with_reason_async(TpSvcChannelInterfaceGroup * obj,const GArray * contacts,const gchar * message,guint reason,DBusGMethodInvocation * context)2437 tp_external_group_mixin_remove_members_with_reason_async
2438 (TpSvcChannelInterfaceGroup *obj,
2439 const GArray *contacts,
2440 const gchar *message,
2441 guint reason,
2442 DBusGMethodInvocation *context)
2443 {
2444 EXTERNAL_OR_DIE (group)
2445 tp_group_mixin_remove_members_with_reason_async
2446 ((TpSvcChannelInterfaceGroup *) group, contacts, message, reason,
2447 context);
2448 }
2449 /**
2450 * tp_external_group_mixin_iface_init: (skip)
2451 * @g_iface: A #TpSvcChannelInterfaceGroupClass
2452 * @iface_data: Unused
2453 *
2454 * Fill in the vtable entries needed to implement the group interface using
2455 * the group mixin of another object. This function should usually be called
2456 * via G_IMPLEMENT_INTERFACE.
2457 *
2458 * Since: 0.5.13
2459 */
2460 void
tp_external_group_mixin_iface_init(gpointer g_iface,gpointer iface_data)2461 tp_external_group_mixin_iface_init (gpointer g_iface,
2462 gpointer iface_data)
2463 {
2464 TpSvcChannelInterfaceGroupClass *klass = g_iface;
2465
2466 #define IMPLEMENT(x) tp_svc_channel_interface_group_implement_##x (klass,\
2467 tp_external_group_mixin_##x##_async)
2468 IMPLEMENT(add_members);
2469 IMPLEMENT(get_all_members);
2470 IMPLEMENT(get_group_flags);
2471 IMPLEMENT(get_handle_owners);
2472 IMPLEMENT(get_local_pending_members);
2473 IMPLEMENT(get_local_pending_members_with_info);
2474 IMPLEMENT(get_members);
2475 IMPLEMENT(get_remote_pending_members);
2476 IMPLEMENT(get_self_handle);
2477 IMPLEMENT(remove_members);
2478 IMPLEMENT(remove_members_with_reason);
2479 #undef IMPLEMENT
2480 }
2481