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