]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-roomlist.c
Do not call g_array_free if the array is NULL
[empathy.git] / libempathy / empathy-tp-roomlist.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  * 
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <telepathy-glib/channel.h>
27 #include <telepathy-glib/dbus.h>
28 #include <telepathy-glib/util.h>
29
30 #include <libmissioncontrol/mission-control.h>
31
32 #include "empathy-tp-roomlist.h"
33 #include "empathy-chatroom.h"
34 #include "empathy-utils.h"
35
36 #define DEBUG_FLAG EMPATHY_DEBUG_TP
37 #include "empathy-debug.h"
38
39 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpRoomlist)
40 typedef struct {
41         TpConnection *connection;
42         TpChannel    *channel;
43         McAccount    *account;
44         gboolean      is_listing;
45 } EmpathyTpRoomlistPriv;
46
47 enum {
48         NEW_ROOM,
49         DESTROY,
50         LAST_SIGNAL
51 };
52
53 enum {
54         PROP_0,
55         PROP_CONNECTION,
56         PROP_IS_LISTING,
57 };
58
59 static guint signals[LAST_SIGNAL];
60
61 G_DEFINE_TYPE (EmpathyTpRoomlist, empathy_tp_roomlist, G_TYPE_OBJECT);
62
63 static void
64 tp_roomlist_listing_cb (TpChannel *channel,
65                         gboolean   listing,
66                         gpointer   user_data,
67                         GObject   *list)
68 {
69         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
70
71         DEBUG ("Listing: %s", listing ? "Yes" : "No");
72         priv->is_listing = listing;
73         g_object_notify (list, "is-listing");
74 }
75
76 static void
77 tp_roomlist_chatrooms_free (gpointer data)
78 {
79         GSList *chatrooms = data;
80
81         g_slist_foreach (chatrooms, (GFunc) g_object_unref, NULL);
82         g_slist_free (chatrooms);
83 }
84
85 static void
86 tp_roomlist_inspect_handles_cb (TpConnection *connection,
87                                 const gchar **names,
88                                 const GError *error,
89                                 gpointer      user_data,
90                                 GObject      *list)
91 {
92         GSList *chatrooms = user_data;
93
94         while (*names) {
95                 EmpathyChatroom *chatroom = chatrooms->data;
96
97                 empathy_chatroom_set_room (chatroom, *names);
98                 g_signal_emit (list, signals[NEW_ROOM], 0, chatroom);
99
100                 names++;
101                 chatrooms = chatrooms->next;
102         }
103 }
104
105 static void
106 tp_roomlist_got_rooms_cb (TpChannel       *channel,
107                           const GPtrArray *rooms,
108                           gpointer         user_data,
109                           GObject         *list)
110 {
111         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
112         EmpathyChatroom       *chatroom;
113         guint                  i;
114         GArray                *handles = NULL;
115         GSList                *chatrooms = NULL;
116
117         for (i = 0; i < rooms->len; i++) {
118                 const GValue *room_name_value;
119                 const GValue *handle_name_value;
120                 GValueArray  *room_struct;
121                 guint         handle;
122                 const gchar  *channel_type;
123                 GHashTable   *info;
124
125                 /* Get information */
126                 room_struct = g_ptr_array_index (rooms, i);
127                 handle = g_value_get_uint (g_value_array_get_nth (room_struct, 0));
128                 channel_type = g_value_get_string (g_value_array_get_nth (room_struct, 1));
129                 info = g_value_get_boxed (g_value_array_get_nth (room_struct, 2));
130                 room_name_value = g_hash_table_lookup (info, "name");
131                 handle_name_value = g_hash_table_lookup (info, "handle-name");
132
133                 if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) {
134                         continue;
135                 }
136
137                 chatroom = empathy_chatroom_new (priv->account);
138
139                 if (room_name_value != NULL) {
140                         empathy_chatroom_set_name (chatroom,
141                                                    g_value_get_string (room_name_value));
142                 }
143
144                 if (handle_name_value != NULL) {
145                         empathy_chatroom_set_room (chatroom,
146                                                    g_value_get_string (handle_name_value));
147
148                         /* We have the room ID, we can directly emit it */
149                         g_signal_emit (list, signals[NEW_ROOM], 0, chatroom);
150                         g_object_unref (chatroom);
151                 } else {
152                         /* We don't have the room ID, we'll inspect all handles
153                          * at once and then emit rooms */
154                         if (handles == NULL) {
155                                 handles = g_array_new (FALSE, FALSE, sizeof (guint));
156                         }
157
158                         g_array_append_val (handles, handle);
159                         chatrooms = g_slist_prepend (chatrooms, chatroom);
160                 }
161         }
162
163         if (handles != NULL) {
164                 chatrooms = g_slist_reverse (chatrooms);
165                 tp_cli_connection_call_inspect_handles (priv->connection, -1,
166                                                        TP_HANDLE_TYPE_ROOM,
167                                                        handles,
168                                                        tp_roomlist_inspect_handles_cb,
169                                                        chatrooms,
170                                                        tp_roomlist_chatrooms_free,
171                                                        list);
172                 g_array_free (handles, TRUE);
173         }
174 }
175
176 static void
177 tp_roomlist_get_listing_rooms_cb (TpChannel    *channel,
178                                   gboolean      is_listing,
179                                   const GError *error,
180                                   gpointer      user_data,
181                                   GObject      *list)
182 {
183         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
184
185         if (error) {
186                 DEBUG ("Error geting listing rooms: %s", error->message);
187                 return;
188         }
189
190         priv->is_listing = is_listing;
191         g_object_notify (list, "is-listing");
192 }
193
194 static void
195 tp_roomlist_invalidated_cb (TpChannel         *channel,
196                             guint              domain,
197                             gint               code,
198                             gchar             *message,
199                             EmpathyTpRoomlist *list)
200 {
201         DEBUG ("Channel invalidated: %s", message);
202         g_signal_emit (list, signals[DESTROY], 0);
203 }
204
205 static void
206 tp_roomlist_request_channel_cb (TpConnection *connection,
207                                 const gchar  *object_path,
208                                 const GError *error,
209                                 gpointer      user_data,
210                                 GObject      *list)
211 {
212         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
213
214         if (error) {
215                 DEBUG ("Error requesting channel: %s", error->message);
216                 return;
217         }
218
219         priv->channel = tp_channel_new (priv->connection, object_path,
220                                         TP_IFACE_CHANNEL_TYPE_ROOM_LIST,
221                                         TP_HANDLE_TYPE_NONE,
222                                         0, NULL);
223         tp_channel_run_until_ready (priv->channel, NULL, NULL);
224
225         g_signal_connect (priv->channel, "invalidated",
226                           G_CALLBACK (tp_roomlist_invalidated_cb),
227                           list);
228
229         tp_cli_channel_type_room_list_connect_to_listing_rooms (priv->channel,
230                                                                 tp_roomlist_listing_cb,
231                                                                 NULL, NULL,
232                                                                 G_OBJECT (list),
233                                                                 NULL);
234         tp_cli_channel_type_room_list_connect_to_got_rooms (priv->channel,
235                                                             tp_roomlist_got_rooms_cb,
236                                                             NULL, NULL,
237                                                             G_OBJECT (list),
238                                                             NULL);
239
240         tp_cli_channel_type_room_list_call_get_listing_rooms (priv->channel, -1,
241                                                               tp_roomlist_get_listing_rooms_cb,
242                                                               NULL, NULL,
243                                                               G_OBJECT (list));
244 }
245
246 static void
247 tp_roomlist_finalize (GObject *object)
248 {
249         EmpathyTpRoomlistPriv *priv = GET_PRIV (object);
250
251         if (priv->channel) {
252                 DEBUG ("Closing channel...");
253                 g_signal_handlers_disconnect_by_func (priv->channel,
254                                                       tp_roomlist_invalidated_cb,
255                                                       object);
256                 tp_cli_channel_call_close (priv->channel, -1,
257                                            NULL, NULL, NULL, NULL);
258                 g_object_unref (priv->channel);
259         }
260
261         if (priv->account) {
262                 g_object_unref (priv->account);
263         }
264         if (priv->connection) {
265                 g_object_unref (priv->connection);
266         }
267
268         G_OBJECT_CLASS (empathy_tp_roomlist_parent_class)->finalize (object);
269 }
270
271 static void
272 tp_roomlist_constructed (GObject *list)
273 {
274         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
275         MissionControl        *mc;
276
277         mc = empathy_mission_control_new ();
278         priv->account = mission_control_get_account_for_tpconnection (mc,
279                                                                       priv->connection,
280                                                                       NULL);
281         g_object_unref (mc);
282
283         tp_cli_connection_call_request_channel (priv->connection, -1,
284                                                 TP_IFACE_CHANNEL_TYPE_ROOM_LIST,
285                                                 TP_HANDLE_TYPE_NONE,
286                                                 0,
287                                                 TRUE,
288                                                 tp_roomlist_request_channel_cb,
289                                                 NULL, NULL,
290                                                 list);
291 }
292
293 static void
294 tp_roomlist_get_property (GObject    *object,
295                           guint       param_id,
296                           GValue     *value,
297                           GParamSpec *pspec)
298 {
299         EmpathyTpRoomlistPriv *priv = GET_PRIV (object);
300
301         switch (param_id) {
302         case PROP_CONNECTION:
303                 g_value_set_object (value, priv->connection);
304                 break;
305         case PROP_IS_LISTING:
306                 g_value_set_boolean (value, priv->is_listing);
307                 break;
308         default:
309                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
310                 break;
311         };
312 }
313
314 static void
315 tp_roomlist_set_property (GObject      *object,
316                           guint         param_id,
317                           const GValue *value,
318                           GParamSpec   *pspec)
319 {
320         EmpathyTpRoomlistPriv *priv = GET_PRIV (object);
321
322         switch (param_id) {
323         case PROP_CONNECTION:
324                 priv->connection = g_object_ref (g_value_get_object (value));
325                 break;
326         default:
327                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
328                 break;
329         };
330 }
331
332 static void
333 empathy_tp_roomlist_class_init (EmpathyTpRoomlistClass *klass)
334 {
335         GObjectClass *object_class = G_OBJECT_CLASS (klass);
336
337         object_class->finalize = tp_roomlist_finalize;
338         object_class->constructed = tp_roomlist_constructed;
339         object_class->get_property = tp_roomlist_get_property;
340         object_class->set_property = tp_roomlist_set_property;
341
342         g_object_class_install_property (object_class,
343                                          PROP_CONNECTION,
344                                          g_param_spec_object ("connection",
345                                                               "The Connection",
346                                                               "The connection on which it lists rooms",
347                                                               TP_TYPE_CONNECTION,
348                                                               G_PARAM_READWRITE |
349                                                               G_PARAM_CONSTRUCT_ONLY));
350         g_object_class_install_property (object_class,
351                                          PROP_IS_LISTING,
352                                          g_param_spec_boolean ("is-listing",
353                                                                "Is listing",
354                                                                "Are we listing rooms",
355                                                                FALSE,
356                                                                G_PARAM_READABLE));
357
358         signals[NEW_ROOM] =
359                 g_signal_new ("new-room",
360                               G_TYPE_FROM_CLASS (klass),
361                               G_SIGNAL_RUN_LAST,
362                               0,
363                               NULL, NULL,
364                               g_cclosure_marshal_VOID__OBJECT,
365                               G_TYPE_NONE,
366                               1, EMPATHY_TYPE_CHATROOM);
367
368         signals[DESTROY] =
369                 g_signal_new ("destroy",
370                               G_TYPE_FROM_CLASS (klass),
371                               G_SIGNAL_RUN_LAST,
372                               0,
373                               NULL, NULL,
374                               g_cclosure_marshal_VOID__VOID,
375                               G_TYPE_NONE,
376                               0);
377
378         g_type_class_add_private (object_class, sizeof (EmpathyTpRoomlistPriv));
379 }
380
381 static void
382 empathy_tp_roomlist_init (EmpathyTpRoomlist *list)
383 {
384         EmpathyTpRoomlistPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (list,
385                 EMPATHY_TYPE_TP_ROOMLIST, EmpathyTpRoomlistPriv);
386
387         list->priv = priv;
388 }
389
390 EmpathyTpRoomlist *
391 empathy_tp_roomlist_new (McAccount *account)
392 {
393         EmpathyTpRoomlist *list;
394         MissionControl    *mc;
395         TpConnection      *connection;
396
397         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
398
399         mc = empathy_mission_control_new ();
400         connection = mission_control_get_tpconnection (mc, account, NULL);
401
402         list = g_object_new (EMPATHY_TYPE_TP_ROOMLIST,
403                              "connection", connection,
404                              NULL);
405
406         g_object_unref (mc);
407         g_object_unref (connection);
408
409         return list;
410 }
411
412 gboolean
413 empathy_tp_roomlist_is_listing (EmpathyTpRoomlist *list)
414 {
415         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
416
417         g_return_val_if_fail (EMPATHY_IS_TP_ROOMLIST (list), FALSE);
418
419         return priv->is_listing;
420 }
421
422 void
423 empathy_tp_roomlist_start (EmpathyTpRoomlist *list)
424 {
425         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
426
427         g_return_if_fail (EMPATHY_IS_TP_ROOMLIST (list));
428         g_return_if_fail (TP_IS_CHANNEL (priv->channel));
429
430         tp_cli_channel_type_room_list_call_list_rooms (priv->channel, -1,
431                                                        NULL, NULL, NULL, NULL);
432 }
433
434 void
435 empathy_tp_roomlist_stop (EmpathyTpRoomlist *list)
436 {
437         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
438
439         g_return_if_fail (EMPATHY_IS_TP_ROOMLIST (list));
440         g_return_if_fail (TP_IS_CHANNEL (priv->channel));
441
442         tp_cli_channel_type_room_list_call_stop_listing (priv->channel, -1,
443                                                          NULL, NULL, NULL, NULL);
444 }
445