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 |
|