]> git.0d.be Git - empathy.git/blob - libempathy/empathy-log-store-empathy.c
6485d007942a5450e306aa143651bd556e21dc38
[empathy.git] / libempathy / empathy-log-store-empathy.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2003-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  *          Jonny Lamb <jonny.lamb@collabora.co.uk>
23  */
24
25 #include <config.h>
26
27 #include <string.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <glib/gstdio.h>
31
32 #include "empathy-log-store.h"
33 #include "empathy-log-store-empathy.h"
34 #include "empathy-log-manager.h"
35 #include "empathy-contact.h"
36 #include "empathy-time.h"
37 #include "empathy-utils.h"
38
39 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
40 #include "empathy-debug.h"
41
42 #define LOG_DIR_CREATE_MODE       (S_IRUSR | S_IWUSR | S_IXUSR)
43 #define LOG_FILE_CREATE_MODE      (S_IRUSR | S_IWUSR)
44 #define LOG_DIR_CHATROOMS         "chatrooms"
45 #define LOG_FILENAME_SUFFIX       ".log"
46 #define LOG_TIME_FORMAT_FULL      "%Y%m%dT%H:%M:%S"
47 #define LOG_TIME_FORMAT           "%Y%m%d"
48 #define LOG_HEADER \
49     "<?xml version='1.0' encoding='utf-8'?>\n" \
50     "<?xml-stylesheet type=\"text/xsl\" href=\"empathy-log.xsl\"?>\n" \
51     "<log>\n"
52
53 #define LOG_FOOTER \
54     "</log>\n"
55
56
57 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyLogStoreEmpathy)
58 typedef struct
59 {
60   gchar *basedir;
61   gchar *name;
62 } EmpathyLogStoreEmpathyPriv;
63
64 static void log_store_iface_init (gpointer g_iface,gpointer iface_data);
65
66 G_DEFINE_TYPE_WITH_CODE (EmpathyLogStoreEmpathy, empathy_log_store_empathy,
67     G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_LOG_STORE,
68       log_store_iface_init));
69
70 static void
71 log_store_empathy_finalize (GObject *object)
72 {
73   EmpathyLogStoreEmpathy *self = EMPATHY_LOG_STORE_EMPATHY (object);
74   EmpathyLogStoreEmpathyPriv *priv = GET_PRIV (self);
75
76   g_free (priv->basedir);
77   g_free (priv->name);
78 }
79
80 static void
81 empathy_log_store_empathy_class_init (EmpathyLogStoreEmpathyClass *klass)
82 {
83   GObjectClass *object_class = G_OBJECT_CLASS (klass);
84
85   object_class->finalize = log_store_empathy_finalize;
86
87   g_type_class_add_private (object_class, sizeof (EmpathyLogStoreEmpathyPriv));
88 }
89
90 static void
91 empathy_log_store_empathy_init (EmpathyLogStoreEmpathy *self)
92 {
93   EmpathyLogStoreEmpathyPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
94       EMPATHY_TYPE_LOG_STORE_EMPATHY, EmpathyLogStoreEmpathyPriv);
95
96   self->priv = priv;
97
98   priv->basedir = g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (),
99       ".gnome2", PACKAGE_NAME, "logs", NULL);
100
101   priv->name = g_strdup ("Empathy");
102 }
103
104 static gchar *
105 log_store_empathy_get_dir (EmpathyLogStore *self,
106                            McAccount *account,
107                            const gchar *chat_id,
108                            gboolean chatroom)
109 {
110   const gchar *account_id;
111   gchar *basedir;
112   EmpathyLogStoreEmpathyPriv *priv;
113
114   priv = GET_PRIV (self);
115
116   account_id = mc_account_get_unique_name (account);
117
118   if (chatroom)
119     basedir = g_build_path (G_DIR_SEPARATOR_S, priv->basedir, account_id,
120         LOG_DIR_CHATROOMS, chat_id, NULL);
121   else
122     basedir = g_build_path (G_DIR_SEPARATOR_S, priv->basedir,
123         account_id, chat_id, NULL);
124
125   return basedir;
126 }
127
128 static gchar *
129 log_store_empathy_get_timestamp_filename (void)
130 {
131   time_t t;
132   gchar *time_str;
133   gchar *filename;
134
135   t = empathy_time_get_current ();
136   time_str = empathy_time_to_string_local (t, LOG_TIME_FORMAT);
137   filename = g_strconcat (time_str, LOG_FILENAME_SUFFIX, NULL);
138
139   g_free (time_str);
140
141   return filename;
142 }
143
144 static gchar *
145 log_store_empathy_get_timestamp_from_message (EmpathyMessage *message)
146 {
147   time_t t;
148
149   t = empathy_message_get_timestamp (message);
150
151   /* We keep the timestamps in the messages as UTC. */
152   return empathy_time_to_string_utc (t, LOG_TIME_FORMAT_FULL);
153 }
154
155 static gchar *
156 log_store_empathy_get_filename (EmpathyLogStore *self,
157                                 McAccount *account,
158                                 const gchar *chat_id,
159                                 gboolean chatroom)
160 {
161   gchar *basedir;
162   gchar *timestamp;
163   gchar *filename;
164
165   basedir = log_store_empathy_get_dir (self, account, chat_id, chatroom);
166   timestamp = log_store_empathy_get_timestamp_filename ();
167   filename = g_build_filename (basedir, timestamp, NULL);
168
169   g_free (basedir);
170   g_free (timestamp);
171
172   return filename;
173 }
174
175 static gboolean
176 log_store_empathy_add_message (EmpathyLogStore *self,
177                                const gchar *chat_id,
178                                gboolean chatroom,
179                                EmpathyMessage *message,
180                                GError **error)
181 {
182   FILE *file;
183   McAccount *account;
184   EmpathyContact *sender;
185   const gchar *body_str;
186   const gchar *str;
187   EmpathyAvatar *avatar;
188   gchar *avatar_token = NULL;
189   gchar *filename;
190   gchar *basedir;
191   gchar *body;
192   gchar *timestamp;
193   gchar *contact_name;
194   gchar *contact_id;
195   TpChannelTextMessageType msg_type;
196
197   g_return_val_if_fail (EMPATHY_IS_LOG_STORE (self), FALSE);
198   g_return_val_if_fail (chat_id != NULL, FALSE);
199   g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
200
201   sender = empathy_message_get_sender (message);
202   account = empathy_contact_get_account (sender);
203   body_str = empathy_message_get_body (message);
204   msg_type = empathy_message_get_tptype (message);
205
206   if (EMP_STR_EMPTY (body_str))
207     return FALSE;
208
209   filename = log_store_empathy_get_filename (self, account, chat_id, chatroom);
210   basedir = g_path_get_dirname (filename);
211   if (!g_file_test (basedir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
212     {
213       DEBUG ("Creating directory:'%s'", basedir);
214       g_mkdir_with_parents (basedir, LOG_DIR_CREATE_MODE);
215     }
216   g_free (basedir);
217
218   DEBUG ("Adding message: '%s' to file: '%s'", body_str, filename);
219
220   if (!g_file_test (filename, G_FILE_TEST_EXISTS))
221     {
222       file = g_fopen (filename, "w+");
223       if (file != NULL)
224         g_fprintf (file, LOG_HEADER);
225
226       g_chmod (filename, LOG_FILE_CREATE_MODE);
227     }
228   else
229     {
230       file = g_fopen (filename, "r+");
231       if (file != NULL)
232         fseek (file, - strlen (LOG_FOOTER), SEEK_END);
233     }
234
235   body = g_markup_escape_text (body_str, -1);
236   timestamp = log_store_empathy_get_timestamp_from_message (message);
237
238   str = empathy_contact_get_name (sender);
239   contact_name = g_markup_escape_text (str, -1);
240
241   str = empathy_contact_get_id (sender);
242   contact_id = g_markup_escape_text (str, -1);
243
244   avatar = empathy_contact_get_avatar (sender);
245   if (avatar != NULL)
246     avatar_token = g_markup_escape_text (avatar->token, -1);
247
248   g_fprintf (file,
249        "<message time='%s' cm_id='%d' id='%s' name='%s' token='%s' isuser='%s' type='%s'>"
250        "%s</message>\n" LOG_FOOTER, timestamp,
251        empathy_message_get_id (message),
252        contact_id, contact_name,
253        avatar_token ? avatar_token : "",
254        empathy_contact_is_user (sender) ? "true" : "false",
255        empathy_message_type_to_str (msg_type), body);
256
257   fclose (file);
258   g_free (filename);
259   g_free (contact_id);
260   g_free (contact_name);
261   g_free (timestamp);
262   g_free (body);
263   g_free (avatar_token);
264
265   return TRUE;
266 }
267
268 static gboolean
269 log_store_empathy_exists (EmpathyLogStore *self,
270                           McAccount *account,
271                           const gchar *chat_id,
272                           gboolean chatroom)
273 {
274   gchar *dir;
275   gboolean exists;
276
277   dir = log_store_empathy_get_dir (self, account, chat_id, chatroom);
278   exists = g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR);
279   g_free (dir);
280
281   return exists;
282 }
283
284 static GList *
285 log_store_empathy_get_dates (EmpathyLogStore *self,
286                              McAccount *account,
287                              const gchar *chat_id,
288                              gboolean chatroom)
289 {
290   GList *dates = NULL;
291   gchar *date;
292   gchar *directory;
293   GDir *dir;
294   const gchar *filename;
295   const gchar *p;
296
297   g_return_val_if_fail (EMPATHY_IS_LOG_STORE (self), NULL);
298   g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
299   g_return_val_if_fail (chat_id != NULL, NULL);
300
301   directory = log_store_empathy_get_dir (self, account, chat_id, chatroom);
302   dir = g_dir_open (directory, 0, NULL);
303   if (!dir)
304     {
305       DEBUG ("Could not open directory:'%s'", directory);
306       g_free (directory);
307       return NULL;
308     }
309
310   DEBUG ("Collating a list of dates in:'%s'", directory);
311
312   while ((filename = g_dir_read_name (dir)) != NULL)
313     {
314       if (!g_str_has_suffix (filename, LOG_FILENAME_SUFFIX))
315         continue;
316
317       p = strstr (filename, LOG_FILENAME_SUFFIX);
318       date = g_strndup (filename, p - filename);
319
320       if (!date)
321         continue;
322
323       if (!g_regex_match_simple ("\\d{8}", date, 0, 0))
324         continue;
325
326       dates = g_list_insert_sorted (dates, date, (GCompareFunc) strcmp);
327     }
328
329   g_free (directory);
330   g_dir_close (dir);
331
332   DEBUG ("Parsed %d dates", g_list_length (dates));
333
334   return dates;
335 }
336
337 static gchar *
338 log_store_empathy_get_filename_for_date (EmpathyLogStore *self,
339                                          McAccount *account,
340                                          const gchar *chat_id,
341                                          gboolean chatroom,
342                                          const gchar *date)
343 {
344   gchar *basedir;
345   gchar *timestamp;
346   gchar *filename;
347
348   basedir = log_store_empathy_get_dir (self, account, chat_id, chatroom);
349   timestamp = g_strconcat (date, LOG_FILENAME_SUFFIX, NULL);
350   filename = g_build_filename (basedir, timestamp, NULL);
351
352   g_free (basedir);
353   g_free (timestamp);
354
355   return filename;
356 }
357
358 static EmpathyLogSearchHit *
359 log_store_empathy_search_hit_new (EmpathyLogStore *self,
360                                   const gchar *filename)
361 {
362   EmpathyLogSearchHit *hit;
363   const gchar *account_name;
364   const gchar *end;
365   gchar **strv;
366   guint len;
367
368   if (!g_str_has_suffix (filename, LOG_FILENAME_SUFFIX))
369     return NULL;
370
371   strv = g_strsplit (filename, G_DIR_SEPARATOR_S, -1);
372   len = g_strv_length (strv);
373
374   hit = g_slice_new0 (EmpathyLogSearchHit);
375
376   end = strstr (strv[len-1], LOG_FILENAME_SUFFIX);
377   hit->date = g_strndup (strv[len-1], end - strv[len-1]);
378   hit->chat_id = g_strdup (strv[len-2]);
379   hit->is_chatroom = (strcmp (strv[len-3], LOG_DIR_CHATROOMS) == 0);
380
381   if (hit->is_chatroom)
382     account_name = strv[len-4];
383   else
384     account_name = strv[len-3];
385
386   hit->account = mc_account_lookup (account_name);
387   hit->filename = g_strdup (filename);
388
389   g_strfreev (strv);
390
391   return hit;
392 }
393
394 static GList *
395 log_store_empathy_get_messages_for_file (EmpathyLogStore *self,
396                                          const gchar *filename)
397 {
398   GList *messages = NULL;
399   xmlParserCtxtPtr ctxt;
400   xmlDocPtr doc;
401   xmlNodePtr log_node;
402   xmlNodePtr node;
403   EmpathyLogSearchHit *hit;
404   McAccount *account;
405
406   g_return_val_if_fail (EMPATHY_IS_LOG_STORE (self), NULL);
407   g_return_val_if_fail (filename != NULL, NULL);
408
409   DEBUG ("Attempting to parse filename:'%s'...", filename);
410
411   if (!g_file_test (filename, G_FILE_TEST_EXISTS))
412     {
413       DEBUG ("Filename:'%s' does not exist", filename);
414       return NULL;
415     }
416
417   /* Get the account from the filename */
418   hit = log_store_empathy_search_hit_new (self, filename);
419   account = g_object_ref (hit->account);
420   empathy_log_manager_search_hit_free (hit);
421
422   /* Create parser. */
423   ctxt = xmlNewParserCtxt ();
424
425   /* Parse and validate the file. */
426   doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
427   if (!doc)
428     {
429       g_warning ("Failed to parse file:'%s'", filename);
430       xmlFreeParserCtxt (ctxt);
431       return NULL;
432     }
433
434   /* The root node, presets. */
435   log_node = xmlDocGetRootElement (doc);
436   if (!log_node)
437     {
438       xmlFreeDoc (doc);
439       xmlFreeParserCtxt (ctxt);
440       return NULL;
441     }
442
443   /* Now get the messages. */
444   for (node = log_node->children; node; node = node->next)
445     {
446       EmpathyMessage *message;
447       EmpathyContact *sender;
448       gchar *time;
449       time_t t;
450       gchar *sender_id;
451       gchar *sender_name;
452       gchar *sender_avatar_token;
453       gchar *body;
454       gchar *is_user_str;
455       gboolean is_user = FALSE;
456       gchar *msg_type_str;
457       gchar *cm_id_str;
458       guint cm_id;
459       TpChannelTextMessageType msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
460
461       if (strcmp (node->name, "message") != 0)
462         continue;
463
464       body = xmlNodeGetContent (node);
465       time = xmlGetProp (node, "time");
466       sender_id = xmlGetProp (node, "id");
467       sender_name = xmlGetProp (node, "name");
468       sender_avatar_token = xmlGetProp (node, "token");
469       is_user_str = xmlGetProp (node, "isuser");
470       msg_type_str = xmlGetProp (node, "type");
471       cm_id_str = xmlGetProp (node, "cm_id");
472
473       if (is_user_str)
474         is_user = strcmp (is_user_str, "true") == 0;
475
476       if (msg_type_str)
477         msg_type = empathy_message_type_from_str (msg_type_str);
478
479       if (cm_id_str)
480         cm_id = atoi (cm_id_str);
481
482       t = empathy_time_parse (time);
483
484       sender = empathy_contact_new_static (account, sender_id, sender_name,
485                                            is_user);
486
487       if (!EMP_STR_EMPTY (sender_avatar_token))
488         empathy_contact_load_avatar_cache (sender,
489             sender_avatar_token);
490
491       message = empathy_message_new (body);
492       empathy_message_set_sender (message, sender);
493       empathy_message_set_timestamp (message, t);
494       empathy_message_set_tptype (message, msg_type);
495
496       if (cm_id_str)
497         empathy_message_set_id (message, cm_id);
498
499       messages = g_list_append (messages, message);
500
501       g_object_unref (sender);
502       xmlFree (time);
503       xmlFree (sender_id);
504       xmlFree (sender_name);
505       xmlFree (body);
506       xmlFree (is_user_str);
507       xmlFree (msg_type_str);
508       xmlFree (cm_id_str);
509       xmlFree (sender_avatar_token);
510     }
511
512   DEBUG ("Parsed %d messages", g_list_length (messages));
513
514   xmlFreeDoc (doc);
515   xmlFreeParserCtxt (ctxt);
516
517   return messages;
518 }
519
520 static GList *
521 log_store_empathy_get_all_files (EmpathyLogStore *self,
522                                  const gchar *dir)
523 {
524   GDir *gdir;
525   GList *files = NULL;
526   const gchar *name;
527   const gchar *basedir;
528   EmpathyLogStoreEmpathyPriv *priv;
529
530   priv = GET_PRIV (self);
531
532   basedir = dir ? dir : priv->basedir;
533
534   gdir = g_dir_open (basedir, 0, NULL);
535   if (!gdir)
536     return NULL;
537
538   while ((name = g_dir_read_name (gdir)) != NULL)
539     {
540       gchar *filename;
541
542       filename = g_build_filename (basedir, name, NULL);
543       if (g_str_has_suffix (filename, LOG_FILENAME_SUFFIX))
544         {
545           files = g_list_prepend (files, filename);
546           continue;
547         }
548
549       if (g_file_test (filename, G_FILE_TEST_IS_DIR))
550         {
551           /* Recursively get all log files */
552           files = g_list_concat (files,
553               log_store_empathy_get_all_files (self, filename));
554         }
555
556       g_free (filename);
557     }
558
559   g_dir_close (gdir);
560
561   return files;
562 }
563
564 static GList *
565 log_store_empathy_search_new (EmpathyLogStore *self,
566                               const gchar *text)
567 {
568   GList *files, *l;
569   GList *hits = NULL;
570   gchar *text_casefold;
571
572   g_return_val_if_fail (EMPATHY_IS_LOG_STORE (self), NULL);
573   g_return_val_if_fail (!EMP_STR_EMPTY (text), NULL);
574
575   text_casefold = g_utf8_casefold (text, -1);
576
577   files = log_store_empathy_get_all_files (self, NULL);
578   DEBUG ("Found %d log files in total", g_list_length (files));
579
580   for (l = files; l; l = g_list_next (l))
581     {
582       gchar *filename;
583       GMappedFile *file;
584       gsize length;
585       gchar *contents;
586       gchar *contents_casefold;
587
588       filename = l->data;
589
590       file = g_mapped_file_new (filename, FALSE, NULL);
591       if (!file)
592         continue;
593
594       length = g_mapped_file_get_length (file);
595       contents = g_mapped_file_get_contents (file);
596       contents_casefold = g_utf8_casefold (contents, length);
597
598       g_mapped_file_free (file);
599
600       if (strstr (contents_casefold, text_casefold))
601         {
602           EmpathyLogSearchHit *hit;
603
604           hit = log_store_empathy_search_hit_new (self, filename);
605
606           if (hit)
607             {
608               hits = g_list_prepend (hits, hit);
609               DEBUG ("Found text:'%s' in file:'%s' on date:'%s'",
610                   text, hit->filename, hit->date);
611             }
612         }
613
614       g_free (contents_casefold);
615       g_free (filename);
616     }
617
618   g_list_free (files);
619   g_free (text_casefold);
620
621   return hits;
622 }
623
624 static GList *
625 log_store_empathy_get_chats_for_dir (EmpathyLogStore *self,
626                                      const gchar *dir,
627                                      gboolean is_chatroom)
628 {
629   GDir *gdir;
630   GList *hits = NULL;
631   const gchar *name;
632   GError *error = NULL;
633
634   gdir = g_dir_open (dir, 0, &error);
635   if (!gdir)
636     {
637       DEBUG ("Failed to open directory: %s, error: %s", dir, error->message);
638       g_error_free (error);
639       return NULL;
640     }
641
642   while ((name = g_dir_read_name (gdir)) != NULL)
643     {
644       EmpathyLogSearchHit *hit;
645
646       if (!is_chatroom && strcmp (name, LOG_DIR_CHATROOMS) == 0)
647         {
648           gchar *filename = g_build_filename (dir, name, NULL);
649           hits = g_list_concat (hits, log_store_empathy_get_chats_for_dir (
650                 self, filename, TRUE));
651           g_free (filename);
652           continue;
653         }
654       hit = g_slice_new0 (EmpathyLogSearchHit);
655       hit->chat_id = g_strdup (name);
656       hit->is_chatroom = is_chatroom;
657
658       hits = g_list_prepend (hits, hit);
659     }
660
661   g_dir_close (gdir);
662
663   return hits;
664 }
665
666
667 static GList *
668 log_store_empathy_get_messages_for_date (EmpathyLogStore *self,
669                                          McAccount *account,
670                                          const gchar *chat_id,
671                                          gboolean chatroom,
672                                          const gchar *date)
673 {
674   gchar *filename;
675   GList *messages;
676
677   g_return_val_if_fail (EMPATHY_IS_LOG_STORE (self), NULL);
678   g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
679   g_return_val_if_fail (chat_id != NULL, NULL);
680
681   filename = log_store_empathy_get_filename_for_date (self, account,
682       chat_id, chatroom, date);
683   messages = log_store_empathy_get_messages_for_file (self, filename);
684   g_free (filename);
685
686   return messages;
687 }
688
689 static GList *
690 log_store_empathy_get_chats (EmpathyLogStore *self,
691                               McAccount *account)
692 {
693   gchar *dir;
694   GList *hits;
695   EmpathyLogStoreEmpathyPriv *priv;
696
697   priv = GET_PRIV (self);
698
699   dir = g_build_filename (priv->basedir,
700       mc_account_get_unique_name (account), NULL);
701
702   hits = log_store_empathy_get_chats_for_dir (self, dir, FALSE);
703
704   g_free (dir);
705
706   return hits;
707 }
708
709 static const gchar *
710 log_store_empathy_get_name (EmpathyLogStore *self)
711 {
712   EmpathyLogStoreEmpathyPriv *priv = GET_PRIV (self);
713
714   return priv->name;
715 }
716
717 static GList *
718 log_store_empathy_get_filtered_messages (EmpathyLogStore *self,
719                                          McAccount *account,
720                                          const gchar *chat_id,
721                                          gboolean chatroom,
722                                          guint num_messages,
723                                          EmpathyLogMessageFilter filter,
724                                          gpointer user_data)
725 {
726   GList *dates, *l, *messages = NULL;
727   guint i = 0;
728
729   dates = log_store_empathy_get_dates (self, account, chat_id, chatroom);
730
731   for (l = g_list_last (dates); l && i < num_messages; l = g_list_previous (l))
732     {
733       GList *new_messages, *n, *next;
734
735       /* FIXME: We should really restrict the message parsing to get only
736        * the newest num_messages. */
737       new_messages = log_store_empathy_get_messages_for_date (self, account,
738           chat_id, chatroom, l->data);
739
740       n = new_messages;
741       while (n != NULL)
742         {
743           next = g_list_next (n);
744           if (!filter (n->data, user_data))
745             {
746               g_object_unref (n->data);
747               new_messages = g_list_delete_link (new_messages, n);
748             }
749           else
750             {
751               i++;
752             }
753           n = next;
754         }
755       messages = g_list_concat (messages, new_messages);
756     }
757
758   g_list_foreach (dates, (GFunc) g_free, NULL);
759   g_list_free (dates);
760
761   return messages;
762 }
763
764 static void
765 log_store_iface_init (gpointer g_iface,
766                       gpointer iface_data)
767 {
768   EmpathyLogStoreInterface *iface = (EmpathyLogStoreInterface *) g_iface;
769
770   iface->get_name = log_store_empathy_get_name;
771   iface->exists = log_store_empathy_exists;
772   iface->add_message = log_store_empathy_add_message;
773   iface->get_dates = log_store_empathy_get_dates;
774   iface->get_messages_for_date = log_store_empathy_get_messages_for_date;
775   iface->get_chats = log_store_empathy_get_chats;
776   iface->search_new = log_store_empathy_search_new;
777   iface->ack_message = NULL;
778   iface->get_filtered_messages = log_store_empathy_get_filtered_messages;
779 }