]> git.0d.be Git - empathy.git/blob - libempathy/gossip-chatroom-manager.c
Fix warning when selecting all accounts.
[empathy.git] / libempathy / gossip-chatroom-manager.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2004-2007 Imendio AB
4  * Copyright (C) 2007 Collabora Ltd.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  * Authors: Xavier Claessens <xclaesse@gmail.com>
22  *          Martyn Russell <martyn@imendio.com>
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33
34 #include "gossip-debug.h"
35 #include "gossip-chatroom-manager.h"
36 #include "gossip-utils.h"
37
38 #define DEBUG_DOMAIN "ChatroomManager"
39
40 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CHATROOM_MANAGER, GossipChatroomManagerPriv))
41
42 #define CHATROOMS_XML_FILENAME "chatrooms.xml"
43 #define CHATROOMS_DTD_FILENAME "gossip-chatroom-manager.dtd"
44
45 struct _GossipChatroomManagerPriv {
46         GList *chatrooms;
47 };
48
49 static void     gossip_chatroom_manager_class_init (GossipChatroomManagerClass *klass);
50 static void     gossip_chatroom_manager_init       (GossipChatroomManager      *manager);
51 static void     chatroom_manager_finalize          (GObject                    *object);
52 static gboolean chatroom_manager_get_all           (GossipChatroomManager      *manager);
53 static gboolean chatroom_manager_file_parse        (GossipChatroomManager      *manager,
54                                                     const gchar                *filename);
55 static void     chatroom_manager_parse_chatroom    (GossipChatroomManager      *manager,
56                                                     xmlNodePtr                  node);
57 static gboolean chatroom_manager_file_save         (GossipChatroomManager      *manager);
58
59 enum {
60         CHATROOM_ADDED,
61         CHATROOM_REMOVED,
62         LAST_SIGNAL
63 };
64
65 static guint signals[LAST_SIGNAL];
66
67 G_DEFINE_TYPE (GossipChatroomManager, gossip_chatroom_manager, G_TYPE_OBJECT);
68
69 static void
70 gossip_chatroom_manager_class_init (GossipChatroomManagerClass *klass)
71 {
72         GObjectClass *object_class = G_OBJECT_CLASS (klass);
73
74         object_class->finalize = chatroom_manager_finalize;
75
76         signals[CHATROOM_ADDED] =
77                 g_signal_new ("chatroom-added",
78                               G_TYPE_FROM_CLASS (klass),
79                               G_SIGNAL_RUN_LAST,
80                               0,
81                               NULL, NULL,
82                               g_cclosure_marshal_VOID__OBJECT,
83                               G_TYPE_NONE,
84                               1, GOSSIP_TYPE_CHATROOM);
85         signals[CHATROOM_REMOVED] =
86                 g_signal_new ("chatroom-removed",
87                               G_TYPE_FROM_CLASS (klass),
88                               G_SIGNAL_RUN_LAST,
89                               0,
90                               NULL, NULL,
91                               g_cclosure_marshal_VOID__OBJECT,
92                               G_TYPE_NONE,
93                               1, GOSSIP_TYPE_CHATROOM);
94
95         g_type_class_add_private (object_class,
96                                   sizeof (GossipChatroomManagerPriv));
97 }
98
99 static void
100 gossip_chatroom_manager_init (GossipChatroomManager *manager)
101 {
102 }
103
104 static void
105 chatroom_manager_finalize (GObject *object)
106 {
107         GossipChatroomManagerPriv *priv;
108
109         priv = GET_PRIV (object);
110
111         g_list_foreach (priv->chatrooms, (GFunc) g_object_unref, NULL);
112         g_list_free (priv->chatrooms);
113
114         (G_OBJECT_CLASS (gossip_chatroom_manager_parent_class)->finalize) (object);
115 }
116
117 GossipChatroomManager *
118 gossip_chatroom_manager_new (void)
119 {
120         static GossipChatroomManager *manager = NULL;
121
122         if (!manager) {
123                 GossipChatroomManagerPriv *priv;
124
125                 manager = g_object_new (GOSSIP_TYPE_CHATROOM_MANAGER, NULL);
126                 priv = GET_PRIV (manager);
127                 chatroom_manager_get_all (manager);
128         
129                 g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager);
130         } else {
131                 g_object_ref (manager);
132         }
133
134         return manager;
135 }
136
137 gboolean
138 gossip_chatroom_manager_add (GossipChatroomManager *manager,
139                              GossipChatroom        *chatroom)
140 {
141         GossipChatroomManagerPriv *priv;
142
143         g_return_val_if_fail (GOSSIP_IS_CHATROOM_MANAGER (manager), FALSE);
144         g_return_val_if_fail (GOSSIP_IS_CHATROOM (chatroom), FALSE);
145
146         priv = GET_PRIV (manager);
147
148         /* don't add more than once */
149         if (!gossip_chatroom_manager_find (manager,
150                                            gossip_chatroom_get_account (chatroom),
151                                            gossip_chatroom_get_room (chatroom))) {
152                 priv->chatrooms = g_list_append (priv->chatrooms, g_object_ref (chatroom));
153                 chatroom_manager_file_save (manager);
154
155                 g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
156
157                 return TRUE;
158         }
159
160         return FALSE;
161 }
162
163 void
164 gossip_chatroom_manager_remove (GossipChatroomManager *manager,
165                                 GossipChatroom        *chatroom)
166 {
167         GossipChatroomManagerPriv *priv;
168         GList                     *l;
169
170         g_return_if_fail (GOSSIP_IS_CHATROOM_MANAGER (manager));
171         g_return_if_fail (GOSSIP_IS_CHATROOM (chatroom));
172
173         priv = GET_PRIV (manager);
174
175         for (l = priv->chatrooms; l; l = l->next) {
176                 GossipChatroom *this_chatroom;
177
178                 this_chatroom = l->data;
179
180                 if (gossip_chatroom_equal (chatroom, this_chatroom)) {
181                         priv->chatrooms = g_list_delete_link (priv->chatrooms, l);
182
183                         chatroom_manager_file_save (manager);
184
185                         g_signal_emit (manager, signals[CHATROOM_REMOVED], 0, this_chatroom);
186                         g_object_unref (this_chatroom);
187                         break;
188                 }
189         }
190 }
191
192 GossipChatroom *
193 gossip_chatroom_manager_find (GossipChatroomManager *manager,
194                               McAccount             *account,
195                               const gchar           *room)
196 {
197         GossipChatroomManagerPriv *priv;
198         GList                     *l;
199
200         g_return_val_if_fail (GOSSIP_IS_CHATROOM_MANAGER (manager), NULL);
201         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
202         g_return_val_if_fail (room != NULL, NULL);
203
204         priv = GET_PRIV (manager);
205
206         for (l = priv->chatrooms; l; l = l->next) {
207                 GossipChatroom *chatroom;
208                 McAccount      *this_account;
209                 const gchar    *this_room;
210
211                 chatroom = l->data;
212                 this_account = gossip_chatroom_get_account (chatroom);
213                 this_room = gossip_chatroom_get_room (chatroom);
214
215                 if (gossip_account_equal (account, this_account) &&
216                     strcmp (this_room, room) == 0) {
217                         return chatroom;
218                 }
219         }
220
221         return NULL;
222 }
223
224 GList *
225 gossip_chatroom_manager_get_chatrooms (GossipChatroomManager *manager,
226                                        McAccount             *account)
227 {
228         GossipChatroomManagerPriv *priv;
229         GList                     *chatrooms, *l;
230
231         g_return_val_if_fail (GOSSIP_IS_CHATROOM_MANAGER (manager), NULL);
232
233         priv = GET_PRIV (manager);
234
235         if (!account) {
236                 return g_list_copy (priv->chatrooms);
237         }
238
239         chatrooms = NULL;
240         for (l = priv->chatrooms; l; l = l->next) {
241                 GossipChatroom *chatroom;
242
243                 chatroom = l->data;
244
245                 if (gossip_account_equal (account,
246                                           gossip_chatroom_get_account (chatroom))) {
247                         chatrooms = g_list_append (chatrooms, chatroom);
248                 }
249         }
250
251         return chatrooms;
252 }
253
254 guint
255 gossip_chatroom_manager_get_count (GossipChatroomManager *manager,
256                                    McAccount             *account)
257 {
258         GossipChatroomManagerPriv *priv;
259         GList                     *l;
260         guint                      count = 0;
261
262         g_return_val_if_fail (GOSSIP_IS_CHATROOM_MANAGER (manager), 0);
263
264         priv = GET_PRIV (manager);
265
266         if (!account) {
267                 return g_list_length (priv->chatrooms);
268         }
269
270         for (l = priv->chatrooms; l; l = l->next) {
271                 GossipChatroom *chatroom;
272
273                 chatroom = l->data;
274
275                 if (gossip_account_equal (account,
276                                           gossip_chatroom_get_account (chatroom))) {
277                         count++;
278                 }
279         }
280
281         return count;
282 }
283
284 void
285 gossip_chatroom_manager_store (GossipChatroomManager *manager)
286 {
287         g_return_if_fail (GOSSIP_IS_CHATROOM_MANAGER (manager));
288
289         chatroom_manager_file_save (manager);
290 }
291
292 /*
293  * API to save/load and parse the chatrooms file.
294  */
295
296 static gboolean
297 chatroom_manager_get_all (GossipChatroomManager *manager)
298 {
299         GossipChatroomManagerPriv *priv;
300         gchar                     *dir;
301         gchar                     *file_with_path = NULL;
302
303         priv = GET_PRIV (manager);
304
305         dir = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, NULL);
306         if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
307                 g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
308         }
309
310         file_with_path = g_build_filename (dir, CHATROOMS_XML_FILENAME, NULL);
311         g_free (dir);
312
313         /* read file in */
314         if (g_file_test (file_with_path, G_FILE_TEST_EXISTS) &&
315             !chatroom_manager_file_parse (manager, file_with_path)) {
316                 g_free (file_with_path);
317                 return FALSE;
318         }
319
320         g_free (file_with_path);
321
322         return TRUE;
323 }
324
325 static gboolean
326 chatroom_manager_file_parse (GossipChatroomManager *manager,
327                              const gchar           *filename)
328 {
329         GossipChatroomManagerPriv *priv;
330         xmlParserCtxtPtr           ctxt;
331         xmlDocPtr                  doc;
332         xmlNodePtr                 chatrooms;
333         xmlNodePtr                 node;
334
335         priv = GET_PRIV (manager);
336
337         gossip_debug (DEBUG_DOMAIN, "Attempting to parse file:'%s'...", filename);
338
339         ctxt = xmlNewParserCtxt ();
340
341         /* Parse and validate the file. */
342         doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
343         if (!doc) {
344                 g_warning ("Failed to parse file:'%s'", filename);
345                 xmlFreeParserCtxt (ctxt);
346                 return FALSE;
347         }
348
349         if (!gossip_xml_validate (doc, CHATROOMS_DTD_FILENAME)) {
350                 g_warning ("Failed to validate file:'%s'", filename);
351                 xmlFreeDoc(doc);
352                 xmlFreeParserCtxt (ctxt);
353                 return FALSE;
354         }
355
356         /* The root node, chatrooms. */
357         chatrooms = xmlDocGetRootElement (doc);
358
359         for (node = chatrooms->children; node; node = node->next) {
360                 if (strcmp ((gchar *) node->name, "chatroom") == 0) {
361                         chatroom_manager_parse_chatroom (manager, node);
362                 }
363         }
364
365         gossip_debug (DEBUG_DOMAIN,
366                       "Parsed %d chatrooms",
367                       g_list_length (priv->chatrooms));
368
369         xmlFreeDoc(doc);
370         xmlFreeParserCtxt (ctxt);
371
372         return TRUE;
373 }
374
375 static void
376 chatroom_manager_parse_chatroom (GossipChatroomManager *manager,
377                                  xmlNodePtr             node)
378 {
379         GossipChatroomManagerPriv *priv;
380         GossipChatroom            *chatroom;
381         McAccount                 *account;
382         xmlNodePtr                 child;
383         gchar                     *str;
384         gchar                     *name;
385         gchar                     *room;
386         gchar                     *account_id;
387         gboolean                   auto_connect;
388
389         priv = GET_PRIV (manager);
390
391         /* default values. */
392         name = NULL;
393         room = NULL;
394         auto_connect = TRUE;
395         account_id = NULL;
396
397         for (child = node->children; child; child = child->next) {
398                 gchar *tag;
399
400                 if (xmlNodeIsText (child)) {
401                         continue;
402                 }
403
404                 tag = (gchar *) child->name;
405                 str = (gchar *) xmlNodeGetContent (child);
406
407                 if (strcmp (tag, "name") == 0) {
408                         name = g_strdup (str);
409                 }
410                 else if (strcmp (tag, "room") == 0) {
411                         room = g_strdup (str);
412                 }
413                 else if (strcmp (tag, "auto_connect") == 0) {
414                         if (strcmp (str, "yes") == 0) {
415                                 auto_connect = TRUE;
416                         } else {
417                                 auto_connect = FALSE;
418                         }
419                 }
420                 else if (strcmp (tag, "account") == 0) {
421                         account_id = g_strdup (str);
422                 }
423
424                 xmlFree (str);
425         }
426
427         account = mc_account_lookup (account_id);
428         if (!account) {
429                 g_free (name);
430                 g_free (room);
431                 g_free (account_id);
432                 return;
433         }
434
435         chatroom = gossip_chatroom_new_full (account,
436                                              room,
437                                              name,
438                                              auto_connect);
439
440         priv->chatrooms = g_list_prepend (priv->chatrooms, chatroom);
441
442         g_object_unref (account);
443         g_free (name);
444         g_free (room);
445         g_free (account_id);
446 }
447
448 static gboolean
449 chatroom_manager_file_save (GossipChatroomManager *manager)
450 {
451         GossipChatroomManagerPriv *priv;
452         xmlDocPtr                  doc;
453         xmlNodePtr                 root;
454         GList                     *l;
455         gchar                     *dir;
456         gchar                     *file;
457
458         priv = GET_PRIV (manager);
459
460         dir = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, NULL);
461         if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
462                 g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
463         }
464
465         file = g_build_filename (dir, CHATROOMS_XML_FILENAME, NULL);
466         g_free (dir);
467
468         doc = xmlNewDoc ("1.0");
469         root = xmlNewNode (NULL, "chatrooms");
470         xmlDocSetRootElement (doc, root);
471
472         for (l = priv->chatrooms; l; l = l->next) {
473                 GossipChatroom *chatroom;
474                 xmlNodePtr      node;
475                 const gchar    *account_id;
476
477                 chatroom = l->data;
478                 account_id = mc_account_get_unique_name (gossip_chatroom_get_account (chatroom));
479
480                 node = xmlNewChild (root, NULL, "chatroom", NULL);
481                 xmlNewTextChild (node, NULL, "name", gossip_chatroom_get_name (chatroom));
482                 xmlNewTextChild (node, NULL, "room", gossip_chatroom_get_room (chatroom));
483                 xmlNewTextChild (node, NULL, "account", account_id);
484                 xmlNewTextChild (node, NULL, "auto_connect", gossip_chatroom_get_auto_connect (chatroom) ? "yes" : "no");
485         }
486
487         /* Make sure the XML is indented properly */
488         xmlIndentTreeOutput = 1;
489
490         gossip_debug (DEBUG_DOMAIN, "Saving file:'%s'", file);
491         xmlSaveFormatFileEnc (file, doc, "utf-8", 1);
492         xmlFreeDoc (doc);
493
494         xmlCleanupParser ();
495         xmlMemoryDump ();
496
497         g_free (file);
498
499         return TRUE;
500 }