]> git.0d.be Git - empathy.git/blob - libempathy/empathy-chatroom-manager.c
Keep a priv pointer in the object struct instead of using G_TYPE_INSTANCE_GET_PRIVATE...
[empathy.git] / libempathy / empathy-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-2008 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 "empathy-chatroom-manager.h"
35 #include "empathy-utils.h"
36
37 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
38 #include "empathy-debug.h"
39
40 #define CHATROOMS_XML_FILENAME "chatrooms.xml"
41 #define CHATROOMS_DTD_FILENAME "empathy-chatroom-manager.dtd"
42
43 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatroomManager)
44 typedef struct {
45         GList      *chatrooms;
46 } EmpathyChatroomManagerPriv;
47
48 static void     chatroom_manager_finalize          (GObject                    *object);
49 static gboolean chatroom_manager_get_all           (EmpathyChatroomManager      *manager);
50 static gboolean chatroom_manager_file_parse        (EmpathyChatroomManager      *manager,
51                                                     const gchar                *filename);
52 static void     chatroom_manager_parse_chatroom    (EmpathyChatroomManager      *manager,
53                                                     xmlNodePtr                  node);
54 static gboolean chatroom_manager_file_save         (EmpathyChatroomManager      *manager);
55
56 enum {
57         CHATROOM_ADDED,
58         CHATROOM_REMOVED,
59         LAST_SIGNAL
60 };
61
62 static guint signals[LAST_SIGNAL];
63
64 G_DEFINE_TYPE (EmpathyChatroomManager, empathy_chatroom_manager, G_TYPE_OBJECT);
65
66 static void
67 empathy_chatroom_manager_class_init (EmpathyChatroomManagerClass *klass)
68 {
69         GObjectClass *object_class = G_OBJECT_CLASS (klass);
70
71         object_class->finalize = chatroom_manager_finalize;
72
73         signals[CHATROOM_ADDED] =
74                 g_signal_new ("chatroom-added",
75                               G_TYPE_FROM_CLASS (klass),
76                               G_SIGNAL_RUN_LAST,
77                               0,
78                               NULL, NULL,
79                               g_cclosure_marshal_VOID__OBJECT,
80                               G_TYPE_NONE,
81                               1, EMPATHY_TYPE_CHATROOM);
82         signals[CHATROOM_REMOVED] =
83                 g_signal_new ("chatroom-removed",
84                               G_TYPE_FROM_CLASS (klass),
85                               G_SIGNAL_RUN_LAST,
86                               0,
87                               NULL, NULL,
88                               g_cclosure_marshal_VOID__OBJECT,
89                               G_TYPE_NONE,
90                               1, EMPATHY_TYPE_CHATROOM);
91
92         g_type_class_add_private (object_class,
93                                   sizeof (EmpathyChatroomManagerPriv));
94 }
95
96 static void
97 empathy_chatroom_manager_init (EmpathyChatroomManager *manager)
98 {
99         EmpathyChatroomManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
100                 EMPATHY_TYPE_CHATROOM_MANAGER, EmpathyChatroomManagerPriv);
101
102         manager->priv = priv;
103 }
104
105 static void
106 chatroom_manager_finalize (GObject *object)
107 {
108         EmpathyChatroomManagerPriv *priv;
109
110         priv = GET_PRIV (object);
111
112         g_list_foreach (priv->chatrooms, (GFunc) g_object_unref, NULL);
113         g_list_free (priv->chatrooms);
114
115         (G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->finalize) (object);
116 }
117
118 EmpathyChatroomManager *
119 empathy_chatroom_manager_new (void)
120 {
121         static EmpathyChatroomManager *manager = NULL;
122
123         if (!manager) {
124                 EmpathyChatroomManagerPriv *priv;
125
126                 manager = g_object_new (EMPATHY_TYPE_CHATROOM_MANAGER, NULL);
127                 priv = GET_PRIV (manager);
128                 chatroom_manager_get_all (manager);
129         
130                 g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager);
131         } else {
132                 g_object_ref (manager);
133         }
134
135         return manager;
136 }
137
138 gboolean
139 empathy_chatroom_manager_add (EmpathyChatroomManager *manager,
140                              EmpathyChatroom        *chatroom)
141 {
142         EmpathyChatroomManagerPriv *priv;
143
144         g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), FALSE);
145         g_return_val_if_fail (EMPATHY_IS_CHATROOM (chatroom), FALSE);
146
147         priv = GET_PRIV (manager);
148
149         /* don't add more than once */
150         if (!empathy_chatroom_manager_find (manager,
151                                            empathy_chatroom_get_account (chatroom),
152                                            empathy_chatroom_get_room (chatroom))) {
153                 priv->chatrooms = g_list_prepend (priv->chatrooms, g_object_ref (chatroom));
154                 chatroom_manager_file_save (manager);
155
156                 g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
157
158                 return TRUE;
159         }
160
161         return FALSE;
162 }
163
164 void
165 empathy_chatroom_manager_remove (EmpathyChatroomManager *manager,
166                                 EmpathyChatroom        *chatroom)
167 {
168         EmpathyChatroomManagerPriv *priv;
169         GList                     *l;
170
171         g_return_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager));
172         g_return_if_fail (EMPATHY_IS_CHATROOM (chatroom));
173
174         priv = GET_PRIV (manager);
175
176         for (l = priv->chatrooms; l; l = l->next) {
177                 EmpathyChatroom *this_chatroom;
178
179                 this_chatroom = l->data;
180
181                 if (empathy_chatroom_equal (chatroom, this_chatroom)) {
182                         priv->chatrooms = g_list_delete_link (priv->chatrooms, l);
183
184                         chatroom_manager_file_save (manager);
185
186                         g_signal_emit (manager, signals[CHATROOM_REMOVED], 0, this_chatroom);
187                         g_object_unref (this_chatroom);
188                         break;
189                 }
190         }
191 }
192
193 EmpathyChatroom *
194 empathy_chatroom_manager_find (EmpathyChatroomManager *manager,
195                               McAccount             *account,
196                               const gchar           *room)
197 {
198         EmpathyChatroomManagerPriv *priv;
199         GList                     *l;
200
201         g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), NULL);
202         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
203         g_return_val_if_fail (room != NULL, NULL);
204
205         priv = GET_PRIV (manager);
206
207         for (l = priv->chatrooms; l; l = l->next) {
208                 EmpathyChatroom *chatroom;
209                 McAccount      *this_account;
210                 const gchar    *this_room;
211
212                 chatroom = l->data;
213                 this_account = empathy_chatroom_get_account (chatroom);
214                 this_room = empathy_chatroom_get_room (chatroom);
215
216                 if (this_account && this_room &&
217                     empathy_account_equal (account, this_account) &&
218                     strcmp (this_room, room) == 0) {
219                         return chatroom;
220                 }
221         }
222
223         return NULL;
224 }
225
226 GList *
227 empathy_chatroom_manager_get_chatrooms (EmpathyChatroomManager *manager,
228                                        McAccount             *account)
229 {
230         EmpathyChatroomManagerPriv *priv;
231         GList                     *chatrooms, *l;
232
233         g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), NULL);
234
235         priv = GET_PRIV (manager);
236
237         if (!account) {
238                 return g_list_copy (priv->chatrooms);
239         }
240
241         chatrooms = NULL;
242         for (l = priv->chatrooms; l; l = l->next) {
243                 EmpathyChatroom *chatroom;
244
245                 chatroom = l->data;
246
247                 if (empathy_account_equal (account,
248                                           empathy_chatroom_get_account (chatroom))) {
249                         chatrooms = g_list_append (chatrooms, chatroom);
250                 }
251         }
252
253         return chatrooms;
254 }
255
256 guint
257 empathy_chatroom_manager_get_count (EmpathyChatroomManager *manager,
258                                    McAccount             *account)
259 {
260         EmpathyChatroomManagerPriv *priv;
261         GList                     *l;
262         guint                      count = 0;
263
264         g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), 0);
265
266         priv = GET_PRIV (manager);
267
268         if (!account) {
269                 return g_list_length (priv->chatrooms);
270         }
271
272         for (l = priv->chatrooms; l; l = l->next) {
273                 EmpathyChatroom *chatroom;
274
275                 chatroom = l->data;
276
277                 if (empathy_account_equal (account,
278                                           empathy_chatroom_get_account (chatroom))) {
279                         count++;
280                 }
281         }
282
283         return count;
284 }
285
286 void
287 empathy_chatroom_manager_store (EmpathyChatroomManager *manager)
288 {
289         g_return_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager));
290
291         chatroom_manager_file_save (manager);
292 }
293
294 /*
295  * API to save/load and parse the chatrooms file.
296  */
297
298 static gboolean
299 chatroom_manager_get_all (EmpathyChatroomManager *manager)
300 {
301         EmpathyChatroomManagerPriv *priv;
302         gchar                     *dir;
303         gchar                     *file_with_path = NULL;
304
305         priv = GET_PRIV (manager);
306
307         dir = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, NULL);
308         if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
309                 g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
310         }
311
312         file_with_path = g_build_filename (dir, CHATROOMS_XML_FILENAME, NULL);
313         g_free (dir);
314
315         /* read file in */
316         if (g_file_test (file_with_path, G_FILE_TEST_EXISTS) &&
317             !chatroom_manager_file_parse (manager, file_with_path)) {
318                 g_free (file_with_path);
319                 return FALSE;
320         }
321
322         g_free (file_with_path);
323
324         return TRUE;
325 }
326
327 static gboolean
328 chatroom_manager_file_parse (EmpathyChatroomManager *manager,
329                              const gchar           *filename)
330 {
331         EmpathyChatroomManagerPriv *priv;
332         xmlParserCtxtPtr           ctxt;
333         xmlDocPtr                  doc;
334         xmlNodePtr                 chatrooms;
335         xmlNodePtr                 node;
336
337         priv = GET_PRIV (manager);
338
339         DEBUG ("Attempting to parse file:'%s'...", filename);
340
341         ctxt = xmlNewParserCtxt ();
342
343         /* Parse and validate the file. */
344         doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
345         if (!doc) {
346                 g_warning ("Failed to parse file:'%s'", filename);
347                 xmlFreeParserCtxt (ctxt);
348                 return FALSE;
349         }
350
351         if (!empathy_xml_validate (doc, CHATROOMS_DTD_FILENAME)) {
352                 g_warning ("Failed to validate file:'%s'", filename);
353                 xmlFreeDoc(doc);
354                 xmlFreeParserCtxt (ctxt);
355                 return FALSE;
356         }
357
358         /* The root node, chatrooms. */
359         chatrooms = xmlDocGetRootElement (doc);
360
361         for (node = chatrooms->children; node; node = node->next) {
362                 if (strcmp ((gchar *) node->name, "chatroom") == 0) {
363                         chatroom_manager_parse_chatroom (manager, node);
364                 }
365         }
366
367         DEBUG ("Parsed %d chatrooms", 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 (EmpathyChatroomManager *manager,
377                                  xmlNodePtr             node)
378 {
379         EmpathyChatroomManagerPriv *priv;
380         EmpathyChatroom            *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 = empathy_chatroom_new_full (account, room, name, auto_connect);
436         priv->chatrooms = g_list_prepend (priv->chatrooms, chatroom);
437         g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
438
439         g_object_unref (account);
440         g_free (name);
441         g_free (room);
442         g_free (account_id);
443 }
444
445 static gboolean
446 chatroom_manager_file_save (EmpathyChatroomManager *manager)
447 {
448         EmpathyChatroomManagerPriv *priv;
449         xmlDocPtr                  doc;
450         xmlNodePtr                 root;
451         GList                     *l;
452         gchar                     *dir;
453         gchar                     *file;
454
455         priv = GET_PRIV (manager);
456
457         dir = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, NULL);
458         if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
459                 g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR);
460         }
461
462         file = g_build_filename (dir, CHATROOMS_XML_FILENAME, NULL);
463         g_free (dir);
464
465         doc = xmlNewDoc ("1.0");
466         root = xmlNewNode (NULL, "chatrooms");
467         xmlDocSetRootElement (doc, root);
468
469         for (l = priv->chatrooms; l; l = l->next) {
470                 EmpathyChatroom *chatroom;
471                 xmlNodePtr      node;
472                 const gchar    *account_id;
473
474                 chatroom = l->data;
475                 account_id = mc_account_get_unique_name (empathy_chatroom_get_account (chatroom));
476
477                 node = xmlNewChild (root, NULL, "chatroom", NULL);
478                 xmlNewTextChild (node, NULL, "name", empathy_chatroom_get_name (chatroom));
479                 xmlNewTextChild (node, NULL, "room", empathy_chatroom_get_room (chatroom));
480                 xmlNewTextChild (node, NULL, "account", account_id);
481                 xmlNewTextChild (node, NULL, "auto_connect", empathy_chatroom_get_auto_connect (chatroom) ? "yes" : "no");
482         }
483
484         /* Make sure the XML is indented properly */
485         xmlIndentTreeOutput = 1;
486
487         DEBUG ("Saving file:'%s'", file);
488         xmlSaveFormatFileEnc (file, doc, "utf-8", 1);
489         xmlFreeDoc (doc);
490
491         xmlCleanupParser ();
492         xmlMemoryDump ();
493
494         g_free (file);
495
496         return TRUE;
497 }