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-utils.h"
37 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
38 #include "empathy-debug.h"
40 #define CHATROOMS_XML_FILENAME "chatrooms.xml"
41 #define CHATROOMS_DTD_FILENAME "empathy-chatroom-manager.dtd"
44 static EmpathyChatroomManager *chatroom_manager_singleton = NULL;
46 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatroomManager)
51 /* source id of the autosave timer */
53 } EmpathyChatroomManagerPriv;
61 static guint signals[LAST_SIGNAL];
70 G_DEFINE_TYPE (EmpathyChatroomManager, empathy_chatroom_manager, G_TYPE_OBJECT);
73 empathy_chatroom_manager_get_property (GObject *object,
78 EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
79 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
84 g_value_set_string (value, priv->file);
87 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
93 empathy_chatroom_manager_set_property (GObject *object,
98 EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
99 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
105 priv->file = g_value_dup_string (value);
108 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
114 empathy_chatroom_manager_constructor (GType type,
116 GObjectConstructParam *props)
119 EmpathyChatroomManager *self;
120 EmpathyChatroomManagerPriv *priv;
122 if (chatroom_manager_singleton != NULL)
123 return g_object_ref (chatroom_manager_singleton);
125 /* Parent constructor chain */
126 obj = G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->
127 constructor (type, n_props, props);
129 self = EMPATHY_CHATROOM_MANAGER (obj);
130 priv = GET_PRIV (self);
132 chatroom_manager_singleton = self;
133 g_object_add_weak_pointer (obj, (gpointer) &chatroom_manager_singleton);
135 if (priv->file == NULL)
137 /* Set the default file path */
140 dir = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, NULL);
141 if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
142 g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
144 priv->file = g_build_filename (dir, CHATROOMS_XML_FILENAME, NULL);
148 chatroom_manager_get_all (self);
153 empathy_chatroom_manager_class_init (EmpathyChatroomManagerClass *klass)
155 GObjectClass *object_class = G_OBJECT_CLASS (klass);
156 GParamSpec *param_spec;
158 object_class->constructor = empathy_chatroom_manager_constructor;
159 object_class->get_property = empathy_chatroom_manager_get_property;
160 object_class->set_property = empathy_chatroom_manager_set_property;
161 object_class->finalize = chatroom_manager_finalize;
163 param_spec = g_param_spec_string (
165 "path of the favorite file",
166 "The path of the XML file containing user's favorites",
168 G_PARAM_CONSTRUCT_ONLY |
170 G_PARAM_STATIC_NAME |
171 G_PARAM_STATIC_NICK |
172 G_PARAM_STATIC_BLURB);
173 g_object_class_install_property (object_class, PROP_FILE, param_spec);
175 signals[CHATROOM_ADDED] = g_signal_new ("chatroom-added",
176 G_TYPE_FROM_CLASS (klass),
179 g_cclosure_marshal_VOID__OBJECT,
181 1, EMPATHY_TYPE_CHATROOM);
183 signals[CHATROOM_REMOVED] = g_signal_new ("chatroom-removed",
184 G_TYPE_FROM_CLASS (klass),
187 g_cclosure_marshal_VOID__OBJECT,
189 1, EMPATHY_TYPE_CHATROOM);
191 g_type_class_add_private (object_class, sizeof (EmpathyChatroomManagerPriv));
195 empathy_chatroom_manager_init (EmpathyChatroomManager *manager)
197 EmpathyChatroomManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
198 EMPATHY_TYPE_CHATROOM_MANAGER, EmpathyChatroomManagerPriv);
200 manager->priv = priv;
204 chatroom_changed_cb (EmpathyChatroom *chatroom,
206 EmpathyChatroomManager *self)
208 reset_save_timeout (self);
212 chatroom_manager_finalize (GObject *object)
214 EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
215 EmpathyChatroomManagerPriv *priv;
218 priv = GET_PRIV (object);
220 if (priv->save_timer_id > 0)
222 /* have to save before destroy the object */
223 g_source_remove (priv->save_timer_id);
224 priv->save_timer_id = 0;
225 chatroom_manager_file_save (self);
228 for (l = priv->chatrooms; l != NULL; l = g_list_next (l))
230 EmpathyChatroom *chatroom = l->data;
232 g_signal_handlers_disconnect_by_func (chatroom, chatroom_changed_cb,
235 g_object_unref (chatroom);
238 g_list_free (priv->chatrooms);
241 (G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->finalize) (object);
244 EmpathyChatroomManager *
245 empathy_chatroom_manager_dup_singleton (const gchar *file)
247 return EMPATHY_CHATROOM_MANAGER (g_object_new (EMPATHY_TYPE_CHATROOM_MANAGER,
248 "file", file, NULL));
252 save_timeout (EmpathyChatroomManager *self)
254 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
256 priv->save_timer_id = 0;
257 chatroom_manager_file_save (self);
263 reset_save_timeout (EmpathyChatroomManager *self)
265 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
267 if (priv->save_timer_id > 0)
269 g_source_remove (priv->save_timer_id);
272 priv->save_timer_id = g_timeout_add_seconds (SAVE_TIMER,
273 (GSourceFunc) save_timeout, self);
277 add_chatroom (EmpathyChatroomManager *self,
278 EmpathyChatroom *chatroom)
280 EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
282 priv->chatrooms = g_list_prepend (priv->chatrooms, g_object_ref (chatroom));
284 g_signal_connect (chatroom, "notify",
285 G_CALLBACK (chatroom_changed_cb), self);
289 empathy_chatroom_manager_add (EmpathyChatroomManager *manager,
290 EmpathyChatroom *chatroom)
292 EmpathyChatroomManagerPriv *priv;
294 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), FALSE);
295 g_return_val_if_fail (EMPATHY_IS_CHATROOM (chatroom), FALSE);
297 priv = GET_PRIV (manager);
299 /* don't add more than once */
300 if (!empathy_chatroom_manager_find (manager,
301 empathy_chatroom_get_account (chatroom),
302 empathy_chatroom_get_room (chatroom)))
306 g_object_get (chatroom, "favorite", &favorite, NULL);
307 add_chatroom (manager, chatroom);
310 reset_save_timeout (manager);
312 g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
320 empathy_chatroom_manager_remove (EmpathyChatroomManager *manager,
321 EmpathyChatroom *chatroom)
323 EmpathyChatroomManagerPriv *priv;
326 g_return_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager));
327 g_return_if_fail (EMPATHY_IS_CHATROOM (chatroom));
329 priv = GET_PRIV (manager);
331 for (l = priv->chatrooms; l; l = l->next)
333 EmpathyChatroom *this_chatroom;
335 this_chatroom = l->data;
337 if (this_chatroom == chatroom ||
338 empathy_chatroom_equal (chatroom, this_chatroom))
342 priv->chatrooms = g_list_delete_link (priv->chatrooms, l);
343 g_object_get (chatroom, "favorite", &favorite, NULL);
345 reset_save_timeout (manager);
347 g_signal_emit (manager, signals[CHATROOM_REMOVED], 0, this_chatroom);
348 g_signal_handlers_disconnect_by_func (chatroom, chatroom_changed_cb,
351 g_object_unref (this_chatroom);
358 empathy_chatroom_manager_find (EmpathyChatroomManager *manager,
362 EmpathyChatroomManagerPriv *priv;
365 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), NULL);
366 g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
367 g_return_val_if_fail (room != NULL, NULL);
369 priv = GET_PRIV (manager);
371 for (l = priv->chatrooms; l; l = l->next) {
372 EmpathyChatroom *chatroom;
373 McAccount *this_account;
374 const gchar *this_room;
377 this_account = empathy_chatroom_get_account (chatroom);
378 this_room = empathy_chatroom_get_room (chatroom);
380 if (this_account && this_room &&
381 empathy_account_equal (account, this_account) &&
382 strcmp (this_room, room) == 0) {
391 empathy_chatroom_manager_get_chatrooms (EmpathyChatroomManager *manager,
394 EmpathyChatroomManagerPriv *priv;
395 GList *chatrooms, *l;
397 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), NULL);
399 priv = GET_PRIV (manager);
402 return g_list_copy (priv->chatrooms);
406 for (l = priv->chatrooms; l; l = l->next) {
407 EmpathyChatroom *chatroom;
411 if (empathy_account_equal (account,
412 empathy_chatroom_get_account (chatroom))) {
413 chatrooms = g_list_append (chatrooms, chatroom);
421 empathy_chatroom_manager_get_count (EmpathyChatroomManager *manager,
424 EmpathyChatroomManagerPriv *priv;
428 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), 0);
430 priv = GET_PRIV (manager);
433 return g_list_length (priv->chatrooms);
436 for (l = priv->chatrooms; l; l = l->next) {
437 EmpathyChatroom *chatroom;
441 if (empathy_account_equal (account,
442 empathy_chatroom_get_account (chatroom))) {
451 * API to save/load and parse the chatrooms file.
455 chatroom_manager_get_all (EmpathyChatroomManager *manager)
457 EmpathyChatroomManagerPriv *priv;
459 priv = GET_PRIV (manager);
462 if (g_file_test (priv->file, G_FILE_TEST_EXISTS) &&
463 !chatroom_manager_file_parse (manager, priv->file)) {
471 chatroom_manager_file_parse (EmpathyChatroomManager *manager,
472 const gchar *filename)
474 EmpathyChatroomManagerPriv *priv;
475 xmlParserCtxtPtr ctxt;
477 xmlNodePtr chatrooms;
480 priv = GET_PRIV (manager);
482 DEBUG ("Attempting to parse file:'%s'...", filename);
484 ctxt = xmlNewParserCtxt ();
486 /* Parse and validate the file. */
487 doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
489 g_warning ("Failed to parse file:'%s'", filename);
490 xmlFreeParserCtxt (ctxt);
494 if (!empathy_xml_validate (doc, CHATROOMS_DTD_FILENAME)) {
495 g_warning ("Failed to validate file:'%s'", filename);
497 xmlFreeParserCtxt (ctxt);
501 /* The root node, chatrooms. */
502 chatrooms = xmlDocGetRootElement (doc);
504 for (node = chatrooms->children; node; node = node->next) {
505 if (strcmp ((gchar *) node->name, "chatroom") == 0) {
506 chatroom_manager_parse_chatroom (manager, node);
510 DEBUG ("Parsed %d chatrooms", g_list_length (priv->chatrooms));
513 xmlFreeParserCtxt (ctxt);
519 chatroom_manager_parse_chatroom (EmpathyChatroomManager *manager,
522 EmpathyChatroomManagerPriv *priv;
523 EmpathyChatroom *chatroom;
530 gboolean auto_connect;
532 priv = GET_PRIV (manager);
534 /* default values. */
540 for (child = node->children; child; child = child->next) {
543 if (xmlNodeIsText (child)) {
547 tag = (gchar *) child->name;
548 str = (gchar *) xmlNodeGetContent (child);
550 if (strcmp (tag, "name") == 0) {
551 name = g_strdup (str);
553 else if (strcmp (tag, "room") == 0) {
554 room = g_strdup (str);
556 else if (strcmp (tag, "auto_connect") == 0) {
557 if (strcmp (str, "yes") == 0) {
560 auto_connect = FALSE;
563 else if (strcmp (tag, "account") == 0) {
564 account_id = g_strdup (str);
570 account = mc_account_lookup (account_id);
578 chatroom = empathy_chatroom_new_full (account, room, name, auto_connect);
579 g_object_set (chatroom, "favorite", TRUE, NULL);
580 add_chatroom (manager, chatroom);
581 g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
583 g_object_unref (account);
590 chatroom_manager_file_save (EmpathyChatroomManager *manager)
592 EmpathyChatroomManagerPriv *priv;
597 priv = GET_PRIV (manager);
599 doc = xmlNewDoc ("1.0");
600 root = xmlNewNode (NULL, "chatrooms");
601 xmlDocSetRootElement (doc, root);
603 for (l = priv->chatrooms; l; l = l->next) {
604 EmpathyChatroom *chatroom;
606 const gchar *account_id;
611 g_object_get (chatroom, "favorite", &favorite, NULL);
616 account_id = mc_account_get_unique_name (empathy_chatroom_get_account (chatroom));
618 node = xmlNewChild (root, NULL, "chatroom", NULL);
619 xmlNewTextChild (node, NULL, "name", empathy_chatroom_get_name (chatroom));
620 xmlNewTextChild (node, NULL, "room", empathy_chatroom_get_room (chatroom));
621 xmlNewTextChild (node, NULL, "account", account_id);
622 xmlNewTextChild (node, NULL, "auto_connect",
623 empathy_chatroom_get_auto_connect (chatroom) ? "yes" : "no");
626 /* Make sure the XML is indented properly */
627 xmlIndentTreeOutput = 1;
629 DEBUG ("Saving file:'%s'", priv->file);
630 xmlSaveFormatFileEnc (priv->file, doc, "utf-8", 1);
640 chatroom_manager_chat_destroyed_cb (EmpathyTpChat *chat,
643 EmpathyChatroomManager *manager = EMPATHY_CHATROOM_MANAGER (user_data);
644 McAccount *account = empathy_tp_chat_get_account (chat);
645 EmpathyChatroom *chatroom;
646 const gchar *roomname;
649 roomname = empathy_tp_chat_get_id (chat);
650 chatroom = empathy_chatroom_manager_find (manager, account, roomname);
652 if (chatroom == NULL)
655 g_object_set (chatroom, "tp-chat", NULL, NULL);
656 g_object_get (chatroom, "favorite", &favorite, NULL);
660 /* Remove the chatroom from the list, unless it's in the list of
662 * FIXME this policy should probably not be in libempathy */
663 empathy_chatroom_manager_remove (manager, chatroom);
668 chatroom_manager_observe_channel_cb (EmpathyDispatcher *dispatcher,
669 EmpathyDispatchOperation *operation, gpointer user_data)
671 EmpathyChatroomManager *manager = EMPATHY_CHATROOM_MANAGER (user_data);
672 EmpathyChatroom *chatroom;
675 const gchar *roomname;
677 TpHandleType handle_type;
680 channel_type = empathy_dispatch_operation_get_channel_type_id (operation);
682 /* Observe Text channels to rooms only */
683 if (channel_type != TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
686 channel = empathy_dispatch_operation_get_channel (operation);
687 tp_channel_get_handle (channel, &handle_type);
689 if (handle_type != TP_HANDLE_TYPE_ROOM)
692 chat = EMPATHY_TP_CHAT (
693 empathy_dispatch_operation_get_channel_wrapper (operation));
694 account = empathy_tp_chat_get_account (chat);
696 roomname = empathy_tp_chat_get_id (chat);
698 chatroom = empathy_chatroom_manager_find (manager, account, roomname);
700 if (chatroom == NULL)
702 chatroom = empathy_chatroom_new_full (account, roomname, roomname,
704 g_object_set (G_OBJECT (chatroom), "tp-chat", chat, NULL);
705 empathy_chatroom_manager_add (manager, chatroom);
706 g_object_unref (chatroom);
710 g_object_set (G_OBJECT (chatroom), "tp-chat", chat, NULL);
713 /* A TpChat is always destroyed as it only gets unreffed after the channel
714 * has been invalidated in the dispatcher.. */
715 g_signal_connect (chat, "destroy",
716 G_CALLBACK (chatroom_manager_chat_destroyed_cb),
721 empathy_chatroom_manager_observe (EmpathyChatroomManager *manager,
722 EmpathyDispatcher *dispatcher)
724 g_signal_connect (dispatcher, "observe",
725 G_CALLBACK (chatroom_manager_observe_channel_cb), manager);