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