2 * Copyright (C) 2004-2007 Imendio AB
3 * Copyright (C) 2007-2009 Collabora Ltd.
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.
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.
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., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Authors: Xavier Claessens <xclaesse@gmail.com>
21 * Martyn Russell <martyn@imendio.com>
27 #include <sys/types.h>
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
33 #include "empathy-tp-chat.h"
34 #include "empathy-chatroom-manager.h"
35 #include "empathy-account-manager.h"
36 #include "empathy-utils.h"
38 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
39 #include "empathy-debug.h"
41 #define CHATROOMS_XML_FILENAME "chatrooms.xml"
42 #define CHATROOMS_DTD_FILENAME "empathy-chatroom-manager.dtd"
45 static EmpathyChatroomManager *chatroom_manager_singleton = NULL;
47 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatroomManager)
52 EmpathyAccountManager *account_manager;
53 /* source id of the autosave timer */
55 } EmpathyChatroomManagerPriv;
63 static guint signals[LAST_SIGNAL];
72 G_DEFINE_TYPE (EmpathyChatroomManager, empathy_chatroom_manager, G_TYPE_OBJECT);
75 * API to save/load and parse the chatrooms file.
79 chatroom_manager_file_save (EmpathyChatroomManager *manager)
81 EmpathyChatroomManagerPriv *priv;
86 priv = GET_PRIV (manager);
88 doc = xmlNewDoc ("1.0");
89 root = xmlNewNode (NULL, "chatrooms");
90 xmlDocSetRootElement (doc, root);
92 for (l = priv->chatrooms; l; l = l->next) {
93 EmpathyChatroom *chatroom;
95 const gchar *account_id;
100 g_object_get (chatroom, "favorite", &favorite, NULL);
105 account_id = mc_account_get_unique_name (empathy_chatroom_get_account (chatroom));
107 node = xmlNewChild (root, NULL, "chatroom", NULL);
108 xmlNewTextChild (node, NULL, "name", empathy_chatroom_get_name (chatroom));
109 xmlNewTextChild (node, NULL, "room", empathy_chatroom_get_room (chatroom));
110 xmlNewTextChild (node, NULL, "account", account_id);
111 xmlNewTextChild (node, NULL, "auto_connect",
112 empathy_chatroom_get_auto_connect (chatroom) ? "yes" : "no");
115 /* Make sure the XML is indented properly */
116 xmlIndentTreeOutput = 1;
118 DEBUG ("Saving file:'%s'", priv->file);
119 xmlSaveFormatFileEnc (priv->file, doc, "utf-8", 1);
129 save_timeout (EmpathyChatroomManager *self)
131 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
133 priv->save_timer_id = 0;
134 chatroom_manager_file_save (self);
140 reset_save_timeout (EmpathyChatroomManager *self)
142 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
144 if (priv->save_timer_id > 0)
146 g_source_remove (priv->save_timer_id);
149 priv->save_timer_id = g_timeout_add_seconds (SAVE_TIMER,
150 (GSourceFunc) save_timeout, self);
154 chatroom_changed_cb (EmpathyChatroom *chatroom,
156 EmpathyChatroomManager *self)
158 reset_save_timeout (self);
162 add_chatroom (EmpathyChatroomManager *self,
163 EmpathyChatroom *chatroom)
165 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
167 priv->chatrooms = g_list_prepend (priv->chatrooms, g_object_ref (chatroom));
169 g_signal_connect (chatroom, "notify",
170 G_CALLBACK (chatroom_changed_cb), self);
174 chatroom_manager_parse_chatroom (EmpathyChatroomManager *manager,
177 EmpathyChatroomManagerPriv *priv;
178 EmpathyChatroom *chatroom;
185 gboolean auto_connect;
187 priv = GET_PRIV (manager);
189 /* default values. */
195 for (child = node->children; child; child = child->next) {
198 if (xmlNodeIsText (child)) {
202 tag = (gchar *) child->name;
203 str = (gchar *) xmlNodeGetContent (child);
205 if (strcmp (tag, "name") == 0) {
206 name = g_strdup (str);
208 else if (strcmp (tag, "room") == 0) {
209 room = g_strdup (str);
211 else if (strcmp (tag, "auto_connect") == 0) {
212 if (strcmp (str, "yes") == 0) {
215 auto_connect = FALSE;
218 else if (strcmp (tag, "account") == 0) {
219 account_id = g_strdup (str);
225 account = mc_account_lookup (account_id);
233 chatroom = empathy_chatroom_new_full (account, room, name, auto_connect);
234 g_object_set (chatroom, "favorite", TRUE, NULL);
235 add_chatroom (manager, chatroom);
236 g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
238 g_object_unref (account);
245 chatroom_manager_file_parse (EmpathyChatroomManager *manager,
246 const gchar *filename)
248 EmpathyChatroomManagerPriv *priv;
249 xmlParserCtxtPtr ctxt;
251 xmlNodePtr chatrooms;
254 priv = GET_PRIV (manager);
256 DEBUG ("Attempting to parse file:'%s'...", filename);
258 ctxt = xmlNewParserCtxt ();
260 /* Parse and validate the file. */
261 doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
263 g_warning ("Failed to parse file:'%s'", filename);
264 xmlFreeParserCtxt (ctxt);
268 if (!empathy_xml_validate (doc, CHATROOMS_DTD_FILENAME)) {
269 g_warning ("Failed to validate file:'%s'", filename);
271 xmlFreeParserCtxt (ctxt);
275 /* The root node, chatrooms. */
276 chatrooms = xmlDocGetRootElement (doc);
278 for (node = chatrooms->children; node; node = node->next) {
279 if (strcmp ((gchar *) node->name, "chatroom") == 0) {
280 chatroom_manager_parse_chatroom (manager, node);
284 DEBUG ("Parsed %d chatrooms", g_list_length (priv->chatrooms));
287 xmlFreeParserCtxt (ctxt);
293 chatroom_manager_get_all (EmpathyChatroomManager *manager)
295 EmpathyChatroomManagerPriv *priv;
297 priv = GET_PRIV (manager);
300 if (g_file_test (priv->file, G_FILE_TEST_EXISTS) &&
301 !chatroom_manager_file_parse (manager, priv->file)) {
309 empathy_chatroom_manager_get_property (GObject *object,
314 EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
315 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
320 g_value_set_string (value, priv->file);
323 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
329 empathy_chatroom_manager_set_property (GObject *object,
334 EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
335 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
341 priv->file = g_value_dup_string (value);
344 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
350 chatroom_manager_finalize (GObject *object)
352 EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
353 EmpathyChatroomManagerPriv *priv;
356 priv = GET_PRIV (object);
358 g_object_unref (priv->account_manager);
360 if (priv->save_timer_id > 0)
362 /* have to save before destroy the object */
363 g_source_remove (priv->save_timer_id);
364 priv->save_timer_id = 0;
365 chatroom_manager_file_save (self);
368 for (l = priv->chatrooms; l != NULL; l = g_list_next (l))
370 EmpathyChatroom *chatroom = l->data;
372 g_signal_handlers_disconnect_by_func (chatroom, chatroom_changed_cb,
375 g_object_unref (chatroom);
378 g_list_free (priv->chatrooms);
381 (G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->finalize) (object);
385 empathy_chatroom_manager_constructor (GType type,
387 GObjectConstructParam *props)
390 EmpathyChatroomManager *self;
391 EmpathyChatroomManagerPriv *priv;
393 if (chatroom_manager_singleton != NULL)
394 return g_object_ref (chatroom_manager_singleton);
396 /* Parent constructor chain */
397 obj = G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->
398 constructor (type, n_props, props);
400 self = EMPATHY_CHATROOM_MANAGER (obj);
401 priv = GET_PRIV (self);
403 chatroom_manager_singleton = self;
404 g_object_add_weak_pointer (obj, (gpointer) &chatroom_manager_singleton);
406 priv->account_manager = empathy_account_manager_dup_singleton ();
408 if (priv->file == NULL)
410 /* Set the default file path */
413 dir = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, NULL);
414 if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
415 g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
417 priv->file = g_build_filename (dir, CHATROOMS_XML_FILENAME, NULL);
421 chatroom_manager_get_all (self);
426 empathy_chatroom_manager_class_init (EmpathyChatroomManagerClass *klass)
428 GObjectClass *object_class = G_OBJECT_CLASS (klass);
429 GParamSpec *param_spec;
431 object_class->constructor = empathy_chatroom_manager_constructor;
432 object_class->get_property = empathy_chatroom_manager_get_property;
433 object_class->set_property = empathy_chatroom_manager_set_property;
434 object_class->finalize = chatroom_manager_finalize;
436 param_spec = g_param_spec_string (
438 "path of the favorite file",
439 "The path of the XML file containing user's favorites",
441 G_PARAM_CONSTRUCT_ONLY |
443 G_PARAM_STATIC_NAME |
444 G_PARAM_STATIC_NICK |
445 G_PARAM_STATIC_BLURB);
446 g_object_class_install_property (object_class, PROP_FILE, param_spec);
448 signals[CHATROOM_ADDED] = g_signal_new ("chatroom-added",
449 G_TYPE_FROM_CLASS (klass),
452 g_cclosure_marshal_VOID__OBJECT,
454 1, EMPATHY_TYPE_CHATROOM);
456 signals[CHATROOM_REMOVED] = g_signal_new ("chatroom-removed",
457 G_TYPE_FROM_CLASS (klass),
460 g_cclosure_marshal_VOID__OBJECT,
462 1, EMPATHY_TYPE_CHATROOM);
464 g_type_class_add_private (object_class, sizeof (EmpathyChatroomManagerPriv));
468 empathy_chatroom_manager_init (EmpathyChatroomManager *manager)
470 EmpathyChatroomManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
471 EMPATHY_TYPE_CHATROOM_MANAGER, EmpathyChatroomManagerPriv);
473 manager->priv = priv;
476 EmpathyChatroomManager *
477 empathy_chatroom_manager_dup_singleton (const gchar *file)
479 return EMPATHY_CHATROOM_MANAGER (g_object_new (EMPATHY_TYPE_CHATROOM_MANAGER,
480 "file", file, NULL));
484 empathy_chatroom_manager_add (EmpathyChatroomManager *manager,
485 EmpathyChatroom *chatroom)
487 EmpathyChatroomManagerPriv *priv;
489 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), FALSE);
490 g_return_val_if_fail (EMPATHY_IS_CHATROOM (chatroom), FALSE);
492 priv = GET_PRIV (manager);
494 /* don't add more than once */
495 if (!empathy_chatroom_manager_find (manager,
496 empathy_chatroom_get_account (chatroom),
497 empathy_chatroom_get_room (chatroom)))
501 g_object_get (chatroom, "favorite", &favorite, NULL);
502 add_chatroom (manager, chatroom);
505 reset_save_timeout (manager);
507 g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
515 empathy_chatroom_manager_remove (EmpathyChatroomManager *manager,
516 EmpathyChatroom *chatroom)
518 EmpathyChatroomManagerPriv *priv;
521 g_return_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager));
522 g_return_if_fail (EMPATHY_IS_CHATROOM (chatroom));
524 priv = GET_PRIV (manager);
526 for (l = priv->chatrooms; l; l = l->next)
528 EmpathyChatroom *this_chatroom;
530 this_chatroom = l->data;
532 if (this_chatroom == chatroom ||
533 empathy_chatroom_equal (chatroom, this_chatroom))
537 priv->chatrooms = g_list_delete_link (priv->chatrooms, l);
538 g_object_get (chatroom, "favorite", &favorite, NULL);
540 reset_save_timeout (manager);
542 g_signal_emit (manager, signals[CHATROOM_REMOVED], 0, this_chatroom);
543 g_signal_handlers_disconnect_by_func (chatroom, chatroom_changed_cb,
546 g_object_unref (this_chatroom);
553 empathy_chatroom_manager_find (EmpathyChatroomManager *manager,
557 EmpathyChatroomManagerPriv *priv;
560 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), NULL);
561 g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
562 g_return_val_if_fail (room != NULL, NULL);
564 priv = GET_PRIV (manager);
566 for (l = priv->chatrooms; l; l = l->next) {
567 EmpathyChatroom *chatroom;
568 McAccount *this_account;
569 const gchar *this_room;
572 this_account = empathy_chatroom_get_account (chatroom);
573 this_room = empathy_chatroom_get_room (chatroom);
575 if (this_account && this_room &&
576 empathy_account_equal (account, this_account) &&
577 strcmp (this_room, room) == 0) {
586 empathy_chatroom_manager_get_chatrooms (EmpathyChatroomManager *manager,
589 EmpathyChatroomManagerPriv *priv;
590 GList *chatrooms, *l;
592 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), NULL);
594 priv = GET_PRIV (manager);
597 return g_list_copy (priv->chatrooms);
601 for (l = priv->chatrooms; l; l = l->next) {
602 EmpathyChatroom *chatroom;
606 if (empathy_account_equal (account,
607 empathy_chatroom_get_account (chatroom))) {
608 chatrooms = g_list_append (chatrooms, chatroom);
616 empathy_chatroom_manager_get_count (EmpathyChatroomManager *manager,
619 EmpathyChatroomManagerPriv *priv;
623 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), 0);
625 priv = GET_PRIV (manager);
628 return g_list_length (priv->chatrooms);
631 for (l = priv->chatrooms; l; l = l->next) {
632 EmpathyChatroom *chatroom;
636 if (empathy_account_equal (account,
637 empathy_chatroom_get_account (chatroom))) {
646 chatroom_manager_chat_destroyed_cb (EmpathyTpChat *chat,
649 EmpathyChatroomManagerPriv *priv = GET_PRIV (manager);
651 TpConnection *connection;
652 EmpathyChatroom *chatroom;
653 const gchar *roomname;
656 connection = empathy_tp_chat_get_connection (chat);
657 account = empathy_account_manager_get_account (priv->account_manager,
659 roomname = empathy_tp_chat_get_id (chat);
660 chatroom = empathy_chatroom_manager_find (manager, account, roomname);
662 if (chatroom == NULL)
665 g_object_set (chatroom, "tp-chat", NULL, NULL);
666 g_object_get (chatroom, "favorite", &favorite, NULL);
670 /* Remove the chatroom from the list, unless it's in the list of
672 * FIXME this policy should probably not be in libempathy */
673 empathy_chatroom_manager_remove (manager, chatroom);
678 chatroom_manager_observe_channel_cb (EmpathyDispatcher *dispatcher,
679 EmpathyDispatchOperation *operation, gpointer manager)
681 EmpathyChatroomManagerPriv *priv = GET_PRIV (manager);
682 EmpathyChatroom *chatroom;
685 const gchar *roomname;
687 TpHandleType handle_type;
689 TpConnection *connection;
691 channel_type = empathy_dispatch_operation_get_channel_type_id (operation);
693 /* Observe Text channels to rooms only */
694 if (channel_type != TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
697 channel = empathy_dispatch_operation_get_channel (operation);
698 tp_channel_get_handle (channel, &handle_type);
700 if (handle_type != TP_HANDLE_TYPE_ROOM)
703 chat = EMPATHY_TP_CHAT (
704 empathy_dispatch_operation_get_channel_wrapper (operation));
705 connection = empathy_tp_chat_get_connection (chat);
706 account = empathy_account_manager_get_account (priv->account_manager,
709 roomname = empathy_tp_chat_get_id (chat);
711 chatroom = empathy_chatroom_manager_find (manager, account, roomname);
713 if (chatroom == NULL)
715 chatroom = empathy_chatroom_new_full (account, roomname, roomname,
717 g_object_set (G_OBJECT (chatroom), "tp-chat", chat, NULL);
718 empathy_chatroom_manager_add (manager, chatroom);
719 g_object_unref (chatroom);
723 g_object_set (G_OBJECT (chatroom), "tp-chat", chat, NULL);
726 /* A TpChat is always destroyed as it only gets unreffed after the channel
727 * has been invalidated in the dispatcher.. */
728 g_signal_connect (chat, "destroy",
729 G_CALLBACK (chatroom_manager_chat_destroyed_cb),
734 empathy_chatroom_manager_observe (EmpathyChatroomManager *manager,
735 EmpathyDispatcher *dispatcher)
737 g_signal_connect (dispatcher, "observe",
738 G_CALLBACK (chatroom_manager_observe_channel_cb), manager);