]> git.0d.be Git - empathy.git/blob - libempathy/empathy-chatroom-manager.c
Fix uninitialized use of a variable
[empathy.git] / libempathy / empathy-chatroom-manager.c
1 /*
2  * Copyright (C) 2004-2007 Imendio AB
3  * Copyright (C) 2007-2010 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program 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  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  *          Martyn Russell <martyn@imendio.com>
22  */
23
24 #include "config.h"
25
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
32
33 #include <telepathy-glib/account-manager.h>
34 #include <telepathy-glib/interfaces.h>
35 #include <telepathy-glib/simple-observer.h>
36 #include <telepathy-glib/util.h>
37
38 #include "empathy-tp-chat.h"
39 #include "empathy-chatroom-manager.h"
40 #include "empathy-utils.h"
41
42 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
43 #include "empathy-debug.h"
44
45 #define CHATROOMS_XML_FILENAME "chatrooms.xml"
46 #define CHATROOMS_DTD_FILENAME "empathy-chatroom-manager.dtd"
47 #define SAVE_TIMER 4
48
49 static EmpathyChatroomManager *chatroom_manager_singleton = NULL;
50
51 static void observe_channels_cb (TpSimpleObserver *observer,
52     TpAccount *account,
53     TpConnection *connection,
54     GList *channels,
55     TpChannelDispatchOperation *dispatch_operation,
56     GList *requests,
57     TpObserveChannelsContext *context,
58     gpointer user_data);
59
60 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatroomManager)
61 typedef struct
62 {
63   GList *chatrooms;
64   gchar *file;
65   TpAccountManager *account_manager;
66
67   /* source id of the autosave timer */
68   gint save_timer_id;
69   gboolean ready;
70   GFileMonitor *monitor;
71   gboolean writing;
72
73   TpBaseClient *observer;
74 } EmpathyChatroomManagerPriv;
75
76 enum {
77   CHATROOM_ADDED,
78   CHATROOM_REMOVED,
79   LAST_SIGNAL
80 };
81
82 static guint signals[LAST_SIGNAL];
83
84 /* properties */
85 enum
86 {
87   PROP_FILE = 1,
88   PROP_READY,
89   LAST_PROPERTY
90 };
91
92 G_DEFINE_TYPE (EmpathyChatroomManager, empathy_chatroom_manager, G_TYPE_OBJECT);
93
94 /*
95  * API to save/load and parse the chatrooms file.
96  */
97
98 static gboolean
99 chatroom_manager_file_save (EmpathyChatroomManager *manager)
100 {
101   EmpathyChatroomManagerPriv *priv;
102   xmlDocPtr doc;
103   xmlNodePtr root;
104   GList *l;
105
106   priv = GET_PRIV (manager);
107
108   priv->writing = TRUE;
109
110   doc = xmlNewDoc ((const xmlChar *) "1.0");
111   root = xmlNewNode (NULL, (const xmlChar *) "chatrooms");
112   xmlDocSetRootElement (doc, root);
113
114   for (l = priv->chatrooms; l; l = l->next)
115     {
116       EmpathyChatroom *chatroom;
117       xmlNodePtr       node;
118       const gchar     *account_id;
119
120       chatroom = l->data;
121
122       if (!empathy_chatroom_is_favorite (chatroom))
123         continue;
124
125       account_id = tp_proxy_get_object_path (empathy_chatroom_get_account (
126             chatroom));
127
128       node = xmlNewChild (root, NULL, (const xmlChar *) "chatroom", NULL);
129       xmlNewTextChild (node, NULL, (const xmlChar *) "name",
130         (const xmlChar *) empathy_chatroom_get_name (chatroom));
131       xmlNewTextChild (node, NULL, (const xmlChar *) "room",
132         (const xmlChar *) empathy_chatroom_get_room (chatroom));
133       xmlNewTextChild (node, NULL, (const xmlChar *) "account",
134         (const xmlChar *) account_id);
135       xmlNewTextChild (node, NULL, (const xmlChar *) "auto_connect",
136         empathy_chatroom_get_auto_connect (chatroom) ?
137         (const xmlChar *) "yes" : (const xmlChar *) "no");
138       xmlNewTextChild (node, NULL, (const xmlChar *) "always_urgent",
139         empathy_chatroom_is_always_urgent (chatroom) ?
140         (const xmlChar *) "yes" : (const xmlChar *) "no");
141     }
142
143   /* Make sure the XML is indented properly */
144   xmlIndentTreeOutput = 1;
145
146   DEBUG ("Saving file:'%s'", priv->file);
147   xmlSaveFormatFileEnc (priv->file, doc, "utf-8", 1);
148   xmlFreeDoc (doc);
149
150   xmlMemoryDump ();
151
152   priv->writing = FALSE;
153   return TRUE;
154 }
155
156 static gboolean
157 save_timeout (EmpathyChatroomManager *self)
158 {
159   EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
160
161   priv->save_timer_id = 0;
162   chatroom_manager_file_save (self);
163
164   return FALSE;
165 }
166
167 static void
168 reset_save_timeout (EmpathyChatroomManager *self)
169 {
170   EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
171
172   if (priv->save_timer_id > 0)
173     g_source_remove (priv->save_timer_id);
174
175   priv->save_timer_id = g_timeout_add_seconds (SAVE_TIMER,
176       (GSourceFunc) save_timeout, self);
177 }
178
179 static void
180 chatroom_changed_cb (EmpathyChatroom *chatroom,
181     GParamSpec *spec,
182     EmpathyChatroomManager *self)
183 {
184   reset_save_timeout (self);
185 }
186
187 static void
188 add_chatroom (EmpathyChatroomManager *self,
189     EmpathyChatroom *chatroom)
190 {
191   EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
192
193   priv->chatrooms = g_list_prepend (priv->chatrooms, g_object_ref (chatroom));
194
195   g_signal_connect (chatroom, "notify",
196       G_CALLBACK (chatroom_changed_cb), self);
197 }
198
199 static void
200 chatroom_manager_parse_chatroom (EmpathyChatroomManager *manager,
201     xmlNodePtr node)
202 {
203   EmpathyChatroomManagerPriv *priv;
204   EmpathyChatroom *chatroom;
205   TpAccount *account;
206   xmlNodePtr child;
207   gchar *str;
208   gchar *name;
209   gchar *room;
210   gchar *account_id;
211   gboolean auto_connect;
212   gboolean always_urgent;
213
214   priv = GET_PRIV (manager);
215
216   /* default values. */
217   name = NULL;
218   room = NULL;
219   auto_connect = TRUE;
220   always_urgent = FALSE;
221   account_id = NULL;
222
223   for (child = node->children; child; child = child->next)
224     {
225       gchar *tag;
226
227       if (xmlNodeIsText (child))
228         continue;
229
230       tag = (gchar *) child->name;
231       str = (gchar *) xmlNodeGetContent (child);
232
233       if (strcmp (tag, "name") == 0)
234         {
235           name = g_strdup (str);
236         }
237       else if (strcmp (tag, "room") == 0)
238         {
239           room = g_strdup (str);
240         }
241       else if (strcmp (tag, "auto_connect") == 0)
242         {
243           if (strcmp (str, "yes") == 0)
244             auto_connect = TRUE;
245           else
246             auto_connect = FALSE;
247         }
248       else if (!tp_strdiff (tag, "always_urgent"))
249         {
250           if (strcmp (str, "yes") == 0)
251             always_urgent = TRUE;
252           else
253             always_urgent = FALSE;
254         }
255       else if (strcmp (tag, "account") == 0)
256         {
257           account_id = g_strdup (str);
258         }
259
260       xmlFree (str);
261     }
262
263   account = tp_account_manager_ensure_account (priv->account_manager,
264     account_id);
265   if (account == NULL)
266     {
267       g_free (name);
268       g_free (room);
269       g_free (account_id);
270       return;
271     }
272
273   chatroom = empathy_chatroom_new_full (account, room, name, auto_connect);
274   empathy_chatroom_set_favorite (chatroom, TRUE);
275   empathy_chatroom_set_always_urgent (chatroom, always_urgent);
276   add_chatroom (manager, chatroom);
277   g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
278
279   g_free (name);
280   g_free (room);
281   g_free (account_id);
282 }
283
284 static gboolean
285 chatroom_manager_file_parse (EmpathyChatroomManager *manager,
286     const gchar *filename)
287 {
288   EmpathyChatroomManagerPriv *priv;
289   xmlParserCtxtPtr ctxt;
290   xmlDocPtr doc;
291   xmlNodePtr chatrooms;
292   xmlNodePtr node;
293
294   priv = GET_PRIV (manager);
295
296   DEBUG ("Attempting to parse file:'%s'...", filename);
297
298   ctxt = xmlNewParserCtxt ();
299
300   /* Parse and validate the file. */
301   doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
302   if (doc == NULL)
303     {
304       g_warning ("Failed to parse file:'%s'", filename);
305       xmlFreeParserCtxt (ctxt);
306       return FALSE;
307     }
308
309   if (!empathy_xml_validate (doc, CHATROOMS_DTD_FILENAME))
310     {
311       g_warning ("Failed to validate file:'%s'", filename);
312       xmlFreeDoc (doc);
313       xmlFreeParserCtxt (ctxt);
314       return FALSE;
315     }
316
317   /* The root node, chatrooms. */
318   chatrooms = xmlDocGetRootElement (doc);
319
320   for (node = chatrooms->children; node; node = node->next)
321     {
322       if (strcmp ((gchar *) node->name, "chatroom") == 0)
323         chatroom_manager_parse_chatroom (manager, node);
324     }
325
326   DEBUG ("Parsed %d chatrooms", g_list_length (priv->chatrooms));
327
328   xmlFreeDoc (doc);
329   xmlFreeParserCtxt (ctxt);
330
331   return TRUE;
332 }
333
334 static gboolean
335 chatroom_manager_get_all (EmpathyChatroomManager *manager)
336 {
337   EmpathyChatroomManagerPriv *priv;
338
339   priv = GET_PRIV (manager);
340
341   /* read file in */
342   if (g_file_test (priv->file, G_FILE_TEST_EXISTS) &&
343       !chatroom_manager_file_parse (manager, priv->file))
344     return FALSE;
345
346   if (!priv->ready)
347     {
348       priv->ready = TRUE;
349       g_object_notify (G_OBJECT (manager), "ready");
350     }
351
352   return TRUE;
353 }
354
355 static void
356 empathy_chatroom_manager_get_property (GObject *object,
357     guint property_id,
358     GValue *value,
359     GParamSpec *pspec)
360 {
361   EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
362   EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
363
364   switch (property_id)
365     {
366       case PROP_FILE:
367         g_value_set_string (value, priv->file);
368         break;
369       case PROP_READY:
370         g_value_set_boolean (value, priv->ready);
371         break;
372       default:
373         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
374         break;
375     }
376 }
377
378 static void
379 empathy_chatroom_manager_set_property (GObject *object,
380     guint property_id,
381     const GValue *value,
382     GParamSpec *pspec)
383 {
384   EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
385   EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
386
387   switch (property_id)
388     {
389       case PROP_FILE:
390         g_free (priv->file);
391         priv->file = g_value_dup_string (value);
392         break;
393       default:
394         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
395         break;
396     }
397 }
398
399 static void
400 chatroom_manager_dispose (GObject *object)
401 {
402   EmpathyChatroomManagerPriv *priv;
403
404   priv = GET_PRIV (object);
405
406   tp_clear_object (&priv->observer);
407   tp_clear_object (&priv->monitor);
408
409   (G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->dispose) (object);
410 }
411
412 static void
413 clear_chatrooms (EmpathyChatroomManager *self)
414 {
415   EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
416   GList *l;
417
418   for (l = priv->chatrooms; l != NULL; l = g_list_next (l))
419     {
420       EmpathyChatroom *chatroom = l->data;
421
422       g_signal_handlers_disconnect_by_func (chatroom, chatroom_changed_cb,
423           self);
424       g_signal_emit (self, signals[CHATROOM_REMOVED], 0, chatroom);
425
426       g_object_unref (chatroom);
427     }
428
429   g_list_free (priv->chatrooms);
430   priv->chatrooms = NULL;
431 }
432
433 static void
434 chatroom_manager_finalize (GObject *object)
435 {
436   EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
437   EmpathyChatroomManagerPriv *priv;
438
439   priv = GET_PRIV (object);
440
441   g_object_unref (priv->account_manager);
442
443   if (priv->save_timer_id > 0)
444     {
445       /* have to save before destroy the object */
446       g_source_remove (priv->save_timer_id);
447       priv->save_timer_id = 0;
448       chatroom_manager_file_save (self);
449     }
450
451   clear_chatrooms (self);
452
453   g_free (priv->file);
454
455   (G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->finalize) (object);
456 }
457
458 static void
459 file_changed_cb (GFileMonitor *monitor,
460     GFile *file,
461     GFile *other_file,
462     GFileMonitorEvent event_type,
463     gpointer user_data)
464 {
465   EmpathyChatroomManager *self = user_data;
466   EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
467
468   if (event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
469     return;
470
471   if (priv->writing)
472     return;
473
474   DEBUG ("chatrooms file changed; reloading list");
475
476   clear_chatrooms (self);
477   chatroom_manager_get_all (self);
478 }
479
480 static void
481 account_manager_ready_cb (GObject *source_object,
482     GAsyncResult *result,
483     gpointer user_data)
484 {
485   EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (user_data);
486   EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
487   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
488   GError *error = NULL;
489   GFile *file = NULL;
490
491   if (!tp_account_manager_prepare_finish (manager, result, &error))
492     {
493       DEBUG ("Failed to prepare account manager: %s", error->message);
494       g_error_free (error);
495       goto out;
496     }
497
498   chatroom_manager_get_all (self);
499
500   /* Set up file monitor */
501   file = g_file_new_for_path (priv->file);
502
503   priv->monitor = g_file_monitor (file, 0, NULL, &error);
504   if (priv->monitor == NULL)
505     {
506       DEBUG ("Failed to create file monitor on %s: %s", priv->file,
507           error->message);
508
509       g_error_free (error);
510       goto out;
511     }
512
513   g_signal_connect (priv->monitor, "changed", G_CALLBACK (file_changed_cb),
514       self);
515
516 out:
517   tp_clear_object (&file);
518   g_object_unref (self);
519 }
520
521 static GObject *
522 empathy_chatroom_manager_constructor (GType type,
523     guint n_props,
524     GObjectConstructParam *props)
525 {
526   GObject *obj;
527   EmpathyChatroomManager *self;
528   EmpathyChatroomManagerPriv *priv;
529   GError *error = NULL;
530   TpDBusDaemon *dbus;
531
532   if (chatroom_manager_singleton != NULL)
533     return g_object_ref (chatroom_manager_singleton);
534
535   /* Parent constructor chain */
536   obj = G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->
537         constructor (type, n_props, props);
538
539   self = EMPATHY_CHATROOM_MANAGER (obj);
540   priv = GET_PRIV (self);
541
542   priv->ready = FALSE;
543
544   chatroom_manager_singleton = self;
545   g_object_add_weak_pointer (obj, (gpointer) &chatroom_manager_singleton);
546
547   priv->account_manager = tp_account_manager_dup ();
548
549   tp_account_manager_prepare_async (priv->account_manager, NULL,
550       account_manager_ready_cb, g_object_ref (self));
551
552   if (priv->file == NULL)
553     {
554       /* Set the default file path */
555       gchar *dir;
556
557       dir = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
558       if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
559         g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
560
561       priv->file = g_build_filename (dir, CHATROOMS_XML_FILENAME, NULL);
562       g_free (dir);
563     }
564
565   dbus = tp_dbus_daemon_dup (&error);
566   if (dbus == NULL)
567     {
568       g_warning ("Failed to get TpDBusDaemon: %s", error->message);
569
570       g_error_free (error);
571       return obj;
572     }
573
574   /* Setup a room observer */
575   priv->observer = tp_simple_observer_new (dbus, TRUE,
576       "Empathy.ChatroomManager", TRUE, observe_channels_cb, self, NULL);
577
578   g_object_unref (dbus);
579
580   tp_base_client_take_observer_filter (priv->observer, tp_asv_new (
581       TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
582         TP_IFACE_CHANNEL_TYPE_TEXT,
583       TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
584         TP_HANDLE_TYPE_ROOM,
585       NULL));
586
587   tp_base_client_add_connection_features_varargs (priv->observer,
588       TP_CONNECTION_FEATURE_CAPABILITIES, NULL);
589
590   if (!tp_base_client_register (priv->observer, &error))
591     {
592       g_critical ("Failed to register Observer: %s", error->message);
593
594       g_error_free (error);
595     }
596
597   return obj;
598 }
599
600 static void
601 empathy_chatroom_manager_class_init (EmpathyChatroomManagerClass *klass)
602 {
603   GObjectClass *object_class = G_OBJECT_CLASS (klass);
604   GParamSpec *param_spec;
605
606   object_class->constructor = empathy_chatroom_manager_constructor;
607   object_class->get_property = empathy_chatroom_manager_get_property;
608   object_class->set_property = empathy_chatroom_manager_set_property;
609   object_class->dispose = chatroom_manager_dispose;
610   object_class->finalize = chatroom_manager_finalize;
611
612   param_spec = g_param_spec_string (
613       "file",
614       "path of the favorite file",
615       "The path of the XML file containing user's favorites",
616       NULL,
617       G_PARAM_CONSTRUCT_ONLY |
618       G_PARAM_READWRITE |
619       G_PARAM_STATIC_NAME |
620       G_PARAM_STATIC_NICK |
621       G_PARAM_STATIC_BLURB);
622   g_object_class_install_property (object_class, PROP_FILE, param_spec);
623
624   param_spec = g_param_spec_boolean (
625       "ready",
626       "whether the manager is ready yet",
627       "whether the manager is ready yet",
628       FALSE,
629       G_PARAM_READABLE);
630   g_object_class_install_property (object_class, PROP_READY, param_spec);
631
632   signals[CHATROOM_ADDED] = g_signal_new ("chatroom-added",
633       G_TYPE_FROM_CLASS (klass),
634       G_SIGNAL_RUN_LAST,
635       0, NULL, NULL,
636       g_cclosure_marshal_VOID__OBJECT,
637       G_TYPE_NONE,
638       1, EMPATHY_TYPE_CHATROOM);
639
640   signals[CHATROOM_REMOVED] = g_signal_new ("chatroom-removed",
641       G_TYPE_FROM_CLASS (klass),
642       G_SIGNAL_RUN_LAST,
643       0, NULL, NULL,
644       g_cclosure_marshal_VOID__OBJECT,
645       G_TYPE_NONE,
646       1, EMPATHY_TYPE_CHATROOM);
647
648   g_type_class_add_private (object_class, sizeof (EmpathyChatroomManagerPriv));
649 }
650
651 static void
652 empathy_chatroom_manager_init (EmpathyChatroomManager *manager)
653 {
654   EmpathyChatroomManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
655       EMPATHY_TYPE_CHATROOM_MANAGER, EmpathyChatroomManagerPriv);
656
657   manager->priv = priv;
658 }
659
660 EmpathyChatroomManager *
661 empathy_chatroom_manager_dup_singleton (const gchar *file)
662 {
663   return EMPATHY_CHATROOM_MANAGER (g_object_new (EMPATHY_TYPE_CHATROOM_MANAGER,
664       "file", file, NULL));
665 }
666
667 gboolean
668 empathy_chatroom_manager_add (EmpathyChatroomManager *manager,
669     EmpathyChatroom *chatroom)
670 {
671   EmpathyChatroomManagerPriv *priv;
672
673   g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), FALSE);
674   g_return_val_if_fail (EMPATHY_IS_CHATROOM (chatroom), FALSE);
675
676   priv = GET_PRIV (manager);
677
678   /* don't add more than once */
679   if (!empathy_chatroom_manager_find (manager,
680       empathy_chatroom_get_account (chatroom),
681       empathy_chatroom_get_room (chatroom)))
682     {
683       add_chatroom (manager, chatroom);
684
685       if (empathy_chatroom_is_favorite (chatroom))
686         reset_save_timeout (manager);
687
688       g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
689       return TRUE;
690     }
691
692   return FALSE;
693 }
694
695 static void
696 chatroom_manager_remove_link (EmpathyChatroomManager *manager,
697     GList *l)
698 {
699   EmpathyChatroomManagerPriv *priv;
700   EmpathyChatroom *chatroom;
701
702   priv = GET_PRIV (manager);
703
704   chatroom = l->data;
705
706   if (empathy_chatroom_is_favorite (chatroom))
707     reset_save_timeout (manager);
708
709   priv->chatrooms = g_list_delete_link (priv->chatrooms, l);
710
711   g_signal_emit (manager, signals[CHATROOM_REMOVED], 0, chatroom);
712   g_signal_handlers_disconnect_by_func (chatroom, chatroom_changed_cb, manager);
713
714   g_object_unref (chatroom);
715 }
716
717 void
718 empathy_chatroom_manager_remove (EmpathyChatroomManager *manager,
719     EmpathyChatroom        *chatroom)
720 {
721   EmpathyChatroomManagerPriv *priv;
722   GList *l;
723
724   g_return_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager));
725   g_return_if_fail (EMPATHY_IS_CHATROOM (chatroom));
726
727   priv = GET_PRIV (manager);
728
729   for (l = priv->chatrooms; l; l = l->next)
730     {
731       EmpathyChatroom *this_chatroom;
732
733       this_chatroom = l->data;
734
735       if (this_chatroom == chatroom ||
736           empathy_chatroom_equal (chatroom, this_chatroom))
737         {
738           chatroom_manager_remove_link (manager, l);
739           break;
740         }
741     }
742 }
743
744 EmpathyChatroom *
745 empathy_chatroom_manager_find (EmpathyChatroomManager *manager,
746     TpAccount *account,
747     const gchar *room)
748 {
749   EmpathyChatroomManagerPriv *priv;
750   GList *l;
751
752   g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), NULL);
753   g_return_val_if_fail (room != NULL, NULL);
754
755   priv = GET_PRIV (manager);
756
757   for (l = priv->chatrooms; l; l = l->next)
758     {
759       EmpathyChatroom *chatroom;
760       TpAccount *this_account;
761       const gchar    *this_room;
762
763       chatroom = l->data;
764       this_account = empathy_chatroom_get_account (chatroom);
765       this_room = empathy_chatroom_get_room (chatroom);
766
767       if (this_account && this_room && account == this_account
768           && strcmp (this_room, room) == 0)
769         return chatroom;
770     }
771
772   return NULL;
773 }
774
775 EmpathyChatroom *
776 empathy_chatroom_manager_ensure_chatroom (EmpathyChatroomManager *manager,
777     TpAccount *account,
778     const gchar *room,
779     const gchar *name)
780 {
781   EmpathyChatroom *chatroom;
782
783   chatroom = empathy_chatroom_manager_find (manager, account, room);
784
785   if (chatroom)
786     {
787       return g_object_ref (chatroom);
788     }
789   else
790     {
791       chatroom = empathy_chatroom_new_full (account,
792         room,
793         name,
794         FALSE);
795       empathy_chatroom_manager_add (manager, chatroom);
796       return chatroom;
797     }
798 }
799
800 GList *
801 empathy_chatroom_manager_get_chatrooms (EmpathyChatroomManager *manager,
802     TpAccount *account)
803 {
804   EmpathyChatroomManagerPriv *priv;
805   GList *chatrooms, *l;
806
807   g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), NULL);
808
809   priv = GET_PRIV (manager);
810
811   if (!account)
812     return g_list_copy (priv->chatrooms);
813
814   chatrooms = NULL;
815   for (l = priv->chatrooms; l; l = l->next)
816     {
817       EmpathyChatroom *chatroom;
818
819       chatroom = l->data;
820
821       if (account == empathy_chatroom_get_account (chatroom))
822         chatrooms = g_list_append (chatrooms, chatroom);
823     }
824
825   return chatrooms;
826 }
827
828 static void
829 chatroom_manager_chat_destroyed_cb (EmpathyTpChat *chat,
830   gpointer manager)
831 {
832   EmpathyChatroomManagerPriv *priv = GET_PRIV (manager);
833   GList *l;
834
835   for (l = priv->chatrooms; l; l = l->next)
836     {
837       EmpathyChatroom *chatroom = l->data;
838
839       if (empathy_chatroom_get_tp_chat (chatroom) != chat)
840         continue;
841
842       empathy_chatroom_set_tp_chat (chatroom, NULL);
843
844       if (!empathy_chatroom_is_favorite (chatroom))
845         {
846           /* Remove the chatroom from the list, unless it's in the list of
847            * favourites..
848            * FIXME this policy should probably not be in libempathy */
849           chatroom_manager_remove_link (manager, l);
850         }
851
852       break;
853     }
854 }
855
856 static void
857 observe_channels_cb (TpSimpleObserver *observer,
858     TpAccount *account,
859     TpConnection *connection,
860     GList *channels,
861     TpChannelDispatchOperation *dispatch_operation,
862     GList *requests,
863     TpObserveChannelsContext *context,
864     gpointer user_data)
865 {
866   EmpathyChatroomManager *self = user_data;
867   GList *l;
868
869   for (l = channels; l != NULL; l = g_list_next (l))
870     {
871       TpChannel *channel = l->data;
872       EmpathyTpChat *tp_chat;
873       const gchar *roomname;
874       EmpathyChatroom *chatroom;
875
876       if (tp_proxy_get_invalidated (channel) != NULL)
877         continue;
878
879       tp_chat = empathy_tp_chat_new (account, channel);
880       roomname = empathy_tp_chat_get_id (tp_chat);
881       chatroom = empathy_chatroom_manager_find (self, account, roomname);
882
883       if (chatroom == NULL)
884         {
885           chatroom = empathy_chatroom_new_full (account, roomname, roomname,
886             FALSE);
887           empathy_chatroom_manager_add (self, chatroom);
888           g_object_unref (chatroom);
889         }
890
891       empathy_chatroom_set_tp_chat (chatroom, tp_chat);
892       g_object_unref (tp_chat);
893
894       /* A TpChat is always destroyed as it only gets unreffed after the channel
895        * has been invalidated in the dispatcher..  */
896       g_signal_connect (tp_chat, "destroy",
897         G_CALLBACK (chatroom_manager_chat_destroyed_cb),
898         self);
899     }
900
901   tp_observe_channels_context_accept (context);
902 }