]> git.0d.be Git - empathy.git/blob - libempathy/empathy-log-store-empathy.c
tp_chat_check_if_ready: deal with password protected room
[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/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_object_unref (account);
426       g_free (name);
427     }
428   g_list_free (accounts);
429
430   hit->filename = g_strdup (filename);
431
432   g_strfreev (strv);
433
434   return hit;
435 }
436
437 static GList *
438 log_store_empathy_get_messages_for_file (EmpathyLogStore *self,
439                                          TpAccount *account,
440                                          const gchar *filename)
441 {
442   GList *messages = NULL;
443   xmlParserCtxtPtr ctxt;
444   xmlDocPtr doc;
445   xmlNodePtr log_node;
446   xmlNodePtr node;
447
448   g_return_val_if_fail (EMPATHY_IS_LOG_STORE (self), NULL);
449   g_return_val_if_fail (filename != NULL, NULL);
450
451   DEBUG ("Attempting to parse filename:'%s'...", filename);
452
453   if (!g_file_test (filename, G_FILE_TEST_EXISTS))
454     {
455       DEBUG ("Filename:'%s' does not exist", filename);
456       return NULL;
457     }
458
459   /* Create parser. */
460   ctxt = xmlNewParserCtxt ();
461
462   /* Parse and validate the file. */
463   doc = xmlCtxtReadFile (ctxt, filename, NULL, 0);
464   if (!doc)
465     {
466       g_warning ("Failed to parse file:'%s'", filename);
467       xmlFreeParserCtxt (ctxt);
468       return NULL;
469     }
470
471   /* The root node, presets. */
472   log_node = xmlDocGetRootElement (doc);
473   if (!log_node)
474     {
475       xmlFreeDoc (doc);
476       xmlFreeParserCtxt (ctxt);
477       return NULL;
478     }
479
480   /* Now get the messages. */
481   for (node = log_node->children; node; node = node->next)
482     {
483       EmpathyMessage *message;
484       EmpathyContact *sender;
485       gchar *time_;
486       time_t t;
487       gchar *sender_id;
488       gchar *sender_name;
489       gchar *sender_avatar_token;
490       gchar *body;
491       gchar *is_user_str;
492       gboolean is_user = FALSE;
493       gchar *msg_type_str;
494       gchar *cm_id_str;
495       guint cm_id;
496       TpChannelTextMessageType msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
497
498       if (strcmp ((const gchar *) node->name, "message") != 0)
499         continue;
500
501       body = (gchar *) xmlNodeGetContent (node);
502       time_ = (gchar *) xmlGetProp (node, (const xmlChar *) "time");
503       sender_id = (gchar *) xmlGetProp (node, (const xmlChar *) "id");
504       sender_name = (gchar *) xmlGetProp (node, (const xmlChar *) "name");
505       sender_avatar_token = (gchar *) xmlGetProp (node,
506           (const xmlChar *) "token");
507       is_user_str = (gchar *) xmlGetProp (node, (const xmlChar *) "isuser");
508       msg_type_str = (gchar *) xmlGetProp (node, (const xmlChar *) "type");
509       cm_id_str = (gchar *) xmlGetProp (node, (const xmlChar *) "cm_id");
510
511       if (is_user_str)
512         is_user = strcmp (is_user_str, "true") == 0;
513
514       if (msg_type_str)
515         msg_type = empathy_message_type_from_str (msg_type_str);
516
517       if (cm_id_str)
518         cm_id = atoi (cm_id_str);
519
520       t = empathy_time_parse (time_);
521
522       sender = empathy_contact_new_for_log (account, sender_id, sender_name,
523                                             is_user);
524
525       if (!EMP_STR_EMPTY (sender_avatar_token))
526         empathy_contact_load_avatar_cache (sender,
527             sender_avatar_token);
528
529       message = empathy_message_new (body);
530       empathy_message_set_sender (message, sender);
531       empathy_message_set_timestamp (message, t);
532       empathy_message_set_tptype (message, msg_type);
533       empathy_message_set_is_backlog (message, TRUE);
534
535       if (cm_id_str)
536         empathy_message_set_id (message, cm_id);
537
538       messages = g_list_append (messages, message);
539
540       g_object_unref (sender);
541       xmlFree (time_);
542       xmlFree (sender_id);
543       xmlFree (sender_name);
544       xmlFree (body);
545       xmlFree (is_user_str);
546       xmlFree (msg_type_str);
547       xmlFree (cm_id_str);
548       xmlFree (sender_avatar_token);
549     }
550
551   DEBUG ("Parsed %d messages", g_list_length (messages));
552
553   xmlFreeDoc (doc);
554   xmlFreeParserCtxt (ctxt);
555
556   return messages;
557 }
558
559 static GList *
560 log_store_empathy_get_all_files (EmpathyLogStore *self,
561                                  const gchar *dir)
562 {
563   GDir *gdir;
564   GList *files = NULL;
565   const gchar *name;
566   const gchar *basedir;
567   EmpathyLogStoreEmpathyPriv *priv;
568
569   priv = GET_PRIV (self);
570
571   basedir = dir ? dir : priv->basedir;
572
573   gdir = g_dir_open (basedir, 0, NULL);
574   if (!gdir)
575     return NULL;
576
577   while ((name = g_dir_read_name (gdir)) != NULL)
578     {
579       gchar *filename;
580
581       filename = g_build_filename (basedir, name, NULL);
582       if (g_str_has_suffix (filename, LOG_FILENAME_SUFFIX))
583         {
584           files = g_list_prepend (files, filename);
585           continue;
586         }
587
588       if (g_file_test (filename, G_FILE_TEST_IS_DIR))
589         {
590           /* Recursively get all log files */
591           files = g_list_concat (files,
592               log_store_empathy_get_all_files (self, filename));
593         }
594
595       g_free (filename);
596     }
597
598   g_dir_close (gdir);
599
600   return files;
601 }
602
603 static GList *
604 log_store_empathy_search_new (EmpathyLogStore *self,
605                               const gchar *text)
606 {
607   GList *files, *l;
608   GList *hits = NULL;
609   gchar *text_casefold;
610
611   g_return_val_if_fail (EMPATHY_IS_LOG_STORE (self), NULL);
612   g_return_val_if_fail (!EMP_STR_EMPTY (text), NULL);
613
614   text_casefold = g_utf8_casefold (text, -1);
615
616   files = log_store_empathy_get_all_files (self, NULL);
617   DEBUG ("Found %d log files in total", g_list_length (files));
618
619   for (l = files; l; l = g_list_next (l))
620     {
621       gchar *filename;
622       GMappedFile *file;
623       gsize length;
624       gchar *contents;
625       gchar *contents_casefold;
626
627       filename = l->data;
628
629       file = g_mapped_file_new (filename, FALSE, NULL);
630       if (!file)
631         continue;
632
633       length = g_mapped_file_get_length (file);
634       contents = g_mapped_file_get_contents (file);
635       contents_casefold = g_utf8_casefold (contents, length);
636
637       g_mapped_file_unref (file);
638
639       if (strstr (contents_casefold, text_casefold))
640         {
641           EmpathyLogSearchHit *hit;
642
643           hit = log_store_empathy_search_hit_new (self, filename);
644
645           if (hit)
646             {
647               hits = g_list_prepend (hits, hit);
648               DEBUG ("Found text:'%s' in file:'%s' on date:'%s'",
649                   text, hit->filename, hit->date);
650             }
651         }
652
653       g_free (contents_casefold);
654       g_free (filename);
655     }
656
657   g_list_free (files);
658   g_free (text_casefold);
659
660   return hits;
661 }
662
663 static GList *
664 log_store_empathy_get_chats_for_dir (EmpathyLogStore *self,
665                                      const gchar *dir,
666                                      gboolean is_chatroom)
667 {
668   GDir *gdir;
669   GList *hits = NULL;
670   const gchar *name;
671   GError *error = NULL;
672
673   gdir = g_dir_open (dir, 0, &error);
674   if (!gdir)
675     {
676       DEBUG ("Failed to open directory: %s, error: %s", dir, error->message);
677       g_error_free (error);
678       return NULL;
679     }
680
681   while ((name = g_dir_read_name (gdir)) != NULL)
682     {
683       EmpathyLogSearchHit *hit;
684
685       if (!is_chatroom && strcmp (name, LOG_DIR_CHATROOMS) == 0)
686         {
687           gchar *filename = g_build_filename (dir, name, NULL);
688           hits = g_list_concat (hits, log_store_empathy_get_chats_for_dir (
689                 self, filename, TRUE));
690           g_free (filename);
691           continue;
692         }
693       hit = g_slice_new0 (EmpathyLogSearchHit);
694       hit->chat_id = g_strdup (name);
695       hit->is_chatroom = is_chatroom;
696
697       hits = g_list_prepend (hits, hit);
698     }
699
700   g_dir_close (gdir);
701
702   return hits;
703 }
704
705
706 static GList *
707 log_store_empathy_get_messages_for_date (EmpathyLogStore *self,
708                                          TpAccount *account,
709                                          const gchar *chat_id,
710                                          gboolean chatroom,
711                                          const gchar *date)
712 {
713   gchar *filename;
714   GList *messages;
715
716   g_return_val_if_fail (EMPATHY_IS_LOG_STORE (self), NULL);
717   g_return_val_if_fail (chat_id != NULL, NULL);
718   g_return_val_if_fail (account != NULL, NULL);
719
720   filename = log_store_empathy_get_filename_for_date (self, account,
721       chat_id, chatroom, date);
722   messages = log_store_empathy_get_messages_for_file (self, account,
723     filename);
724   g_free (filename);
725
726   return messages;
727 }
728
729 static GList *
730 log_store_empathy_get_chats (EmpathyLogStore *self,
731                               TpAccount *account)
732 {
733   gchar *dir;
734   GList *hits;
735   EmpathyLogStoreEmpathyPriv *priv;
736
737   priv = GET_PRIV (self);
738
739   dir = log_store_empathy_get_dir (self, account, NULL, FALSE);
740
741   hits = log_store_empathy_get_chats_for_dir (self, dir, FALSE);
742
743   g_free (dir);
744
745   return hits;
746 }
747
748 static const gchar *
749 log_store_empathy_get_name (EmpathyLogStore *self)
750 {
751   EmpathyLogStoreEmpathyPriv *priv = GET_PRIV (self);
752
753   return priv->name;
754 }
755
756 static GList *
757 log_store_empathy_get_filtered_messages (EmpathyLogStore *self,
758                                          TpAccount *account,
759                                          const gchar *chat_id,
760                                          gboolean chatroom,
761                                          guint num_messages,
762                                          EmpathyLogMessageFilter filter,
763                                          gpointer user_data)
764 {
765   GList *dates, *l, *messages = NULL;
766   guint i = 0;
767
768   dates = log_store_empathy_get_dates (self, account, chat_id, chatroom);
769
770   for (l = g_list_last (dates); l && i < num_messages; l = g_list_previous (l))
771     {
772       GList *new_messages, *n, *next;
773
774       /* FIXME: We should really restrict the message parsing to get only
775        * the newest num_messages. */
776       new_messages = log_store_empathy_get_messages_for_date (self, account,
777           chat_id, chatroom, l->data);
778
779       n = new_messages;
780       while (n != NULL)
781         {
782           next = g_list_next (n);
783           if (!filter (n->data, user_data))
784             {
785               g_object_unref (n->data);
786               new_messages = g_list_delete_link (new_messages, n);
787             }
788           else
789             {
790               i++;
791             }
792           n = next;
793         }
794       messages = g_list_concat (messages, new_messages);
795     }
796
797   g_list_foreach (dates, (GFunc) g_free, NULL);
798   g_list_free (dates);
799
800   return messages;
801 }
802
803 static void
804 log_store_iface_init (gpointer g_iface,
805                       gpointer iface_data)
806 {
807   EmpathyLogStoreInterface *iface = (EmpathyLogStoreInterface *) g_iface;
808
809   iface->get_name = log_store_empathy_get_name;
810   iface->exists = log_store_empathy_exists;
811   iface->add_message = log_store_empathy_add_message;
812   iface->get_dates = log_store_empathy_get_dates;
813   iface->get_messages_for_date = log_store_empathy_get_messages_for_date;
814   iface->get_chats = log_store_empathy_get_chats;
815   iface->search_new = log_store_empathy_search_new;
816   iface->ack_message = NULL;
817   iface->get_filtered_messages = log_store_empathy_get_filtered_messages;
818 }