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