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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301 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
;
99 if (!empathy_chatroom_is_favorite (chatroom
)) {
103 account_id
= mc_account_get_unique_name (empathy_chatroom_get_account (chatroom
));
105 node
= xmlNewChild (root
, NULL
, "chatroom", NULL
);
106 xmlNewTextChild (node
, NULL
, "name", empathy_chatroom_get_name (chatroom
));
107 xmlNewTextChild (node
, NULL
, "room", empathy_chatroom_get_room (chatroom
));
108 xmlNewTextChild (node
, NULL
, "account", account_id
);
109 xmlNewTextChild (node
, NULL
, "auto_connect",
110 empathy_chatroom_get_auto_connect (chatroom
) ? "yes" : "no");
113 /* Make sure the XML is indented properly */
114 xmlIndentTreeOutput
= 1;
116 DEBUG ("Saving file:'%s'", priv
->file
);
117 xmlSaveFormatFileEnc (priv
->file
, doc
, "utf-8", 1);
127 save_timeout (EmpathyChatroomManager
*self
)
129 EmpathyChatroomManagerPriv
*priv
= GET_PRIV (self
);
131 priv
->save_timer_id
= 0;
132 chatroom_manager_file_save (self
);
138 reset_save_timeout (EmpathyChatroomManager
*self
)
140 EmpathyChatroomManagerPriv
*priv
= GET_PRIV (self
);
142 if (priv
->save_timer_id
> 0)
144 g_source_remove (priv
->save_timer_id
);
147 priv
->save_timer_id
= g_timeout_add_seconds (SAVE_TIMER
,
148 (GSourceFunc
) save_timeout
, self
);
152 chatroom_changed_cb (EmpathyChatroom
*chatroom
,
154 EmpathyChatroomManager
*self
)
156 reset_save_timeout (self
);
160 add_chatroom (EmpathyChatroomManager
*self
,
161 EmpathyChatroom
*chatroom
)
163 EmpathyChatroomManagerPriv
*priv
= GET_PRIV (self
);
165 priv
->chatrooms
= g_list_prepend (priv
->chatrooms
, g_object_ref (chatroom
));
167 g_signal_connect (chatroom
, "notify",
168 G_CALLBACK (chatroom_changed_cb
), self
);
172 chatroom_manager_parse_chatroom (EmpathyChatroomManager
*manager
,
175 EmpathyChatroomManagerPriv
*priv
;
176 EmpathyChatroom
*chatroom
;
183 gboolean auto_connect
;
185 priv
= GET_PRIV (manager
);
187 /* default values. */
193 for (child
= node
->children
; child
; child
= child
->next
) {
196 if (xmlNodeIsText (child
)) {
200 tag
= (gchar
*) child
->name
;
201 str
= (gchar
*) xmlNodeGetContent (child
);
203 if (strcmp (tag
, "name") == 0) {
204 name
= g_strdup (str
);
206 else if (strcmp (tag
, "room") == 0) {
207 room
= g_strdup (str
);
209 else if (strcmp (tag
, "auto_connect") == 0) {
210 if (strcmp (str
, "yes") == 0) {
213 auto_connect
= FALSE
;
216 else if (strcmp (tag
, "account") == 0) {
217 account_id
= g_strdup (str
);
223 account
= mc_account_lookup (account_id
);
231 chatroom
= empathy_chatroom_new_full (account
, room
, name
, auto_connect
);
232 empathy_chatroom_set_favorite (chatroom
, TRUE
);
233 add_chatroom (manager
, chatroom
);
234 g_signal_emit (manager
, signals
[CHATROOM_ADDED
], 0, chatroom
);
236 g_object_unref (account
);
243 chatroom_manager_file_parse (EmpathyChatroomManager
*manager
,
244 const gchar
*filename
)
246 EmpathyChatroomManagerPriv
*priv
;
247 xmlParserCtxtPtr ctxt
;
249 xmlNodePtr chatrooms
;
252 priv
= GET_PRIV (manager
);
254 DEBUG ("Attempting to parse file:'%s'...", filename
);
256 ctxt
= xmlNewParserCtxt ();
258 /* Parse and validate the file. */
259 doc
= xmlCtxtReadFile (ctxt
, filename
, NULL
, 0);
261 g_warning ("Failed to parse file:'%s'", filename
);
262 xmlFreeParserCtxt (ctxt
);
266 if (!empathy_xml_validate (doc
, CHATROOMS_DTD_FILENAME
)) {
267 g_warning ("Failed to validate file:'%s'", filename
);
269 xmlFreeParserCtxt (ctxt
);
273 /* The root node, chatrooms. */
274 chatrooms
= xmlDocGetRootElement (doc
);
276 for (node
= chatrooms
->children
; node
; node
= node
->next
) {
277 if (strcmp ((gchar
*) node
->name
, "chatroom") == 0) {
278 chatroom_manager_parse_chatroom (manager
, node
);
282 DEBUG ("Parsed %d chatrooms", g_list_length (priv
->chatrooms
));
285 xmlFreeParserCtxt (ctxt
);
291 chatroom_manager_get_all (EmpathyChatroomManager
*manager
)
293 EmpathyChatroomManagerPriv
*priv
;
295 priv
= GET_PRIV (manager
);
298 if (g_file_test (priv
->file
, G_FILE_TEST_EXISTS
) &&
299 !chatroom_manager_file_parse (manager
, priv
->file
)) {
307 empathy_chatroom_manager_get_property (GObject
*object
,
312 EmpathyChatroomManager
*self
= EMPATHY_CHATROOM_MANAGER (object
);
313 EmpathyChatroomManagerPriv
*priv
= GET_PRIV (self
);
318 g_value_set_string (value
, priv
->file
);
321 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
327 empathy_chatroom_manager_set_property (GObject
*object
,
332 EmpathyChatroomManager
*self
= EMPATHY_CHATROOM_MANAGER (object
);
333 EmpathyChatroomManagerPriv
*priv
= GET_PRIV (self
);
339 priv
->file
= g_value_dup_string (value
);
342 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
348 chatroom_manager_finalize (GObject
*object
)
350 EmpathyChatroomManager
*self
= EMPATHY_CHATROOM_MANAGER (object
);
351 EmpathyChatroomManagerPriv
*priv
;
354 priv
= GET_PRIV (object
);
356 g_object_unref (priv
->account_manager
);
358 if (priv
->save_timer_id
> 0)
360 /* have to save before destroy the object */
361 g_source_remove (priv
->save_timer_id
);
362 priv
->save_timer_id
= 0;
363 chatroom_manager_file_save (self
);
366 for (l
= priv
->chatrooms
; l
!= NULL
; l
= g_list_next (l
))
368 EmpathyChatroom
*chatroom
= l
->data
;
370 g_signal_handlers_disconnect_by_func (chatroom
, chatroom_changed_cb
,
373 g_object_unref (chatroom
);
376 g_list_free (priv
->chatrooms
);
379 (G_OBJECT_CLASS (empathy_chatroom_manager_parent_class
)->finalize
) (object
);
383 empathy_chatroom_manager_constructor (GType type
,
385 GObjectConstructParam
*props
)
388 EmpathyChatroomManager
*self
;
389 EmpathyChatroomManagerPriv
*priv
;
391 if (chatroom_manager_singleton
!= NULL
)
392 return g_object_ref (chatroom_manager_singleton
);
394 /* Parent constructor chain */
395 obj
= G_OBJECT_CLASS (empathy_chatroom_manager_parent_class
)->
396 constructor (type
, n_props
, props
);
398 self
= EMPATHY_CHATROOM_MANAGER (obj
);
399 priv
= GET_PRIV (self
);
401 chatroom_manager_singleton
= self
;
402 g_object_add_weak_pointer (obj
, (gpointer
) &chatroom_manager_singleton
);
404 priv
->account_manager
= empathy_account_manager_dup_singleton ();
406 if (priv
->file
== NULL
)
408 /* Set the default file path */
411 dir
= g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME
, NULL
);
412 if (!g_file_test (dir
, G_FILE_TEST_EXISTS
| G_FILE_TEST_IS_DIR
))
413 g_mkdir_with_parents (dir
, S_IRUSR
| S_IWUSR
| S_IXUSR
);
415 priv
->file
= g_build_filename (dir
, CHATROOMS_XML_FILENAME
, NULL
);
419 chatroom_manager_get_all (self
);
424 empathy_chatroom_manager_class_init (EmpathyChatroomManagerClass
*klass
)
426 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
427 GParamSpec
*param_spec
;
429 object_class
->constructor
= empathy_chatroom_manager_constructor
;
430 object_class
->get_property
= empathy_chatroom_manager_get_property
;
431 object_class
->set_property
= empathy_chatroom_manager_set_property
;
432 object_class
->finalize
= chatroom_manager_finalize
;
434 param_spec
= g_param_spec_string (
436 "path of the favorite file",
437 "The path of the XML file containing user's favorites",
439 G_PARAM_CONSTRUCT_ONLY
|
441 G_PARAM_STATIC_NAME
|
442 G_PARAM_STATIC_NICK
|
443 G_PARAM_STATIC_BLURB
);
444 g_object_class_install_property (object_class
, PROP_FILE
, param_spec
);
446 signals
[CHATROOM_ADDED
] = g_signal_new ("chatroom-added",
447 G_TYPE_FROM_CLASS (klass
),
450 g_cclosure_marshal_VOID__OBJECT
,
452 1, EMPATHY_TYPE_CHATROOM
);
454 signals
[CHATROOM_REMOVED
] = g_signal_new ("chatroom-removed",
455 G_TYPE_FROM_CLASS (klass
),
458 g_cclosure_marshal_VOID__OBJECT
,
460 1, EMPATHY_TYPE_CHATROOM
);
462 g_type_class_add_private (object_class
, sizeof (EmpathyChatroomManagerPriv
));
466 empathy_chatroom_manager_init (EmpathyChatroomManager
*manager
)
468 EmpathyChatroomManagerPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (manager
,
469 EMPATHY_TYPE_CHATROOM_MANAGER
, EmpathyChatroomManagerPriv
);
471 manager
->priv
= priv
;
474 EmpathyChatroomManager
*
475 empathy_chatroom_manager_dup_singleton (const gchar
*file
)
477 return EMPATHY_CHATROOM_MANAGER (g_object_new (EMPATHY_TYPE_CHATROOM_MANAGER
,
478 "file", file
, NULL
));
482 empathy_chatroom_manager_add (EmpathyChatroomManager
*manager
,
483 EmpathyChatroom
*chatroom
)
485 EmpathyChatroomManagerPriv
*priv
;
487 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager
), FALSE
);
488 g_return_val_if_fail (EMPATHY_IS_CHATROOM (chatroom
), FALSE
);
490 priv
= GET_PRIV (manager
);
492 /* don't add more than once */
493 if (!empathy_chatroom_manager_find (manager
,
494 empathy_chatroom_get_account (chatroom
),
495 empathy_chatroom_get_room (chatroom
)))
497 add_chatroom (manager
, chatroom
);
499 if (empathy_chatroom_is_favorite (chatroom
))
500 reset_save_timeout (manager
);
502 g_signal_emit (manager
, signals
[CHATROOM_ADDED
], 0, chatroom
);
510 chatroom_manager_remove_link (EmpathyChatroomManager
*manager
,
513 EmpathyChatroomManagerPriv
*priv
;
514 EmpathyChatroom
*chatroom
;
516 priv
= GET_PRIV (manager
);
520 if (empathy_chatroom_is_favorite (chatroom
))
521 reset_save_timeout (manager
);
523 g_signal_emit (manager
, signals
[CHATROOM_REMOVED
], 0, chatroom
);
524 g_signal_handlers_disconnect_by_func (chatroom
, chatroom_changed_cb
, manager
);
526 priv
->chatrooms
= g_list_delete_link (priv
->chatrooms
, l
);
527 g_object_unref (chatroom
);
531 empathy_chatroom_manager_remove (EmpathyChatroomManager
*manager
,
532 EmpathyChatroom
*chatroom
)
534 EmpathyChatroomManagerPriv
*priv
;
537 g_return_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager
));
538 g_return_if_fail (EMPATHY_IS_CHATROOM (chatroom
));
540 priv
= GET_PRIV (manager
);
542 for (l
= priv
->chatrooms
; l
; l
= l
->next
)
544 EmpathyChatroom
*this_chatroom
;
546 this_chatroom
= l
->data
;
548 if (this_chatroom
== chatroom
||
549 empathy_chatroom_equal (chatroom
, this_chatroom
))
551 chatroom_manager_remove_link (manager
, l
);
558 empathy_chatroom_manager_find (EmpathyChatroomManager
*manager
,
562 EmpathyChatroomManagerPriv
*priv
;
565 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager
), NULL
);
566 g_return_val_if_fail (MC_IS_ACCOUNT (account
), NULL
);
567 g_return_val_if_fail (room
!= NULL
, NULL
);
569 priv
= GET_PRIV (manager
);
571 for (l
= priv
->chatrooms
; l
; l
= l
->next
) {
572 EmpathyChatroom
*chatroom
;
573 McAccount
*this_account
;
574 const gchar
*this_room
;
577 this_account
= empathy_chatroom_get_account (chatroom
);
578 this_room
= empathy_chatroom_get_room (chatroom
);
580 if (this_account
&& this_room
&&
581 empathy_account_equal (account
, this_account
) &&
582 strcmp (this_room
, room
) == 0) {
591 empathy_chatroom_manager_get_chatrooms (EmpathyChatroomManager
*manager
,
594 EmpathyChatroomManagerPriv
*priv
;
595 GList
*chatrooms
, *l
;
597 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager
), NULL
);
599 priv
= GET_PRIV (manager
);
602 return g_list_copy (priv
->chatrooms
);
606 for (l
= priv
->chatrooms
; l
; l
= l
->next
) {
607 EmpathyChatroom
*chatroom
;
611 if (empathy_account_equal (account
,
612 empathy_chatroom_get_account (chatroom
))) {
613 chatrooms
= g_list_append (chatrooms
, chatroom
);
621 empathy_chatroom_manager_get_count (EmpathyChatroomManager
*manager
,
624 EmpathyChatroomManagerPriv
*priv
;
628 g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager
), 0);
630 priv
= GET_PRIV (manager
);
633 return g_list_length (priv
->chatrooms
);
636 for (l
= priv
->chatrooms
; l
; l
= l
->next
) {
637 EmpathyChatroom
*chatroom
;
641 if (empathy_account_equal (account
,
642 empathy_chatroom_get_account (chatroom
))) {
651 chatroom_manager_chat_destroyed_cb (EmpathyTpChat
*chat
,
654 EmpathyChatroomManagerPriv
*priv
= GET_PRIV (manager
);
657 for (l
= priv
->chatrooms
; l
; l
= l
->next
)
659 EmpathyChatroom
*chatroom
= l
->data
;
661 if (empathy_chatroom_get_tp_chat (chatroom
) != chat
)
664 empathy_chatroom_set_tp_chat (chatroom
, NULL
);
666 if (!empathy_chatroom_is_favorite (chatroom
))
668 /* Remove the chatroom from the list, unless it's in the list of
670 * FIXME this policy should probably not be in libempathy */
671 chatroom_manager_remove_link (manager
, l
);
679 chatroom_manager_observe_channel_cb (EmpathyDispatcher
*dispatcher
,
680 EmpathyDispatchOperation
*operation
, gpointer manager
)
682 EmpathyChatroomManagerPriv
*priv
= GET_PRIV (manager
);
683 EmpathyChatroom
*chatroom
;
686 const gchar
*roomname
;
688 TpHandleType handle_type
;
690 TpConnection
*connection
;
692 channel_type
= empathy_dispatch_operation_get_channel_type_id (operation
);
694 /* Observe Text channels to rooms only */
695 if (channel_type
!= TP_IFACE_QUARK_CHANNEL_TYPE_TEXT
)
698 channel
= empathy_dispatch_operation_get_channel (operation
);
699 tp_channel_get_handle (channel
, &handle_type
);
701 if (handle_type
!= TP_HANDLE_TYPE_ROOM
)
704 chat
= EMPATHY_TP_CHAT (
705 empathy_dispatch_operation_get_channel_wrapper (operation
));
706 connection
= empathy_tp_chat_get_connection (chat
);
707 account
= empathy_account_manager_get_account (priv
->account_manager
,
710 roomname
= empathy_tp_chat_get_id (chat
);
712 chatroom
= empathy_chatroom_manager_find (manager
, account
, roomname
);
714 if (chatroom
== NULL
)
716 chatroom
= empathy_chatroom_new_full (account
, roomname
, roomname
,
718 empathy_chatroom_set_tp_chat (chatroom
, chat
);
719 empathy_chatroom_manager_add (manager
, chatroom
);
720 g_object_unref (chatroom
);
724 empathy_chatroom_set_tp_chat (chatroom
, chat
);
727 /* A TpChat is always destroyed as it only gets unreffed after the channel
728 * has been invalidated in the dispatcher.. */
729 g_signal_connect (chat
, "destroy",
730 G_CALLBACK (chatroom_manager_chat_destroyed_cb
),
735 empathy_chatroom_manager_observe (EmpathyChatroomManager
*manager
,
736 EmpathyDispatcher
*dispatcher
)
738 g_signal_connect (dispatcher
, "observe",
739 G_CALLBACK (chatroom_manager_observe_channel_cb
), manager
);