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