From 4aaef6738d1d348d31b28d0502a7e4806b996835 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Thu, 14 Jul 2011 15:25:18 +0200 Subject: [PATCH 1/2] Implement a Tracker based TplLogStore This is a writeonly log store that implements a TrackerMiner, so every logged information ends up in the Tracker store using the NMO/NCO ontologies. It is compiled optionally if Tracker libraries are found. On the first run, it also fetches every previously logged information to ensure data integrity. --- configure.ac | 13 + telepathy-logger/Makefile.am | 15 +- telepathy-logger/log-manager.c | 12 + telepathy-logger/log-store-factory.c | 2 + telepathy-logger/log-store-tracker-internal.h | 62 ++ telepathy-logger/log-store-tracker.c | 1461 +++++++++++++++++++++++++ tests/dbus/Makefile.am | 2 + 7 files changed, 1566 insertions(+), 1 deletions(-) create mode 100644 telepathy-logger/log-store-tracker-internal.h create mode 100644 telepathy-logger/log-store-tracker.c diff --git a/configure.ac b/configure.ac index 2ee6eab..913e9b8 100644 --- a/configure.ac +++ b/configure.ac @@ -154,6 +154,18 @@ PKG_CHECK_MODULES(TPL, telepathy-glib >= $TELEPATHY_GLIB_REQUIRED ]) +# ----------------------------------------------------------- +# Tracker optional support +# ----------------------------------------------------------- +PKG_CHECK_MODULES(TPL_TRACKER, [ tracker-miner-0.11 >= 0.11.0 ], + [have_tracker_miner=yes], + [have_tracker_miner=no]) + +if test x$have_tracker_miner = xyes; then + AC_DEFINE(HAVE_TRACKER_MINER, [], [Have Tracker miner libraries]) +fi + +AM_CONDITIONAL(HAVE_TRACKER_MINER, [test x$have_tracker_miner = xyes]) # ----------------------------------------------------------- # Enable public extensions library @@ -236,5 +248,6 @@ Configure summary: Public extensions library...: ${enable_public_extensions} Introspection support.......: ${found_introspection} Call support................: ${enable_call} + Tracker support.............: ${have_tracker_miner} " diff --git a/telepathy-logger/Makefile.am b/telepathy-logger/Makefile.am index 959bcf4..c82d7d1 100644 --- a/telepathy-logger/Makefile.am +++ b/telepathy-logger/Makefile.am @@ -13,6 +13,7 @@ AM_CPPFLAGS = \ -DG_LOG_DOMAIN=\"tp-logger\" \ -DTPL_DATA_DIR=\"$(PACKAGE_NAME)\" \ $(TPL_CFLAGS) \ + $(TPL_TRACKER_CFLAGS) \ $(DISABLE_DEPRECATED) \ $(WARN_CFLAGS) @@ -108,11 +109,18 @@ libtelepathy_logger_la_SOURCES = \ $(call_sources) \ $(NULL) +if HAVE_TRACKER_MINER +libtelepathy_logger_la_SOURCES += \ + log-store-tracker.c \ + log-store-tracker-internal.h +endif + nodist_libtelepathy_logger_la_SOURCES = \ $(BUILT_SOURCES) libtelepathy_logger_la_LIBADD = \ - $(top_builddir)/extensions/libtpl-extensions-convenience.la + $(top_builddir)/extensions/libtpl-extensions-convenience.la \ + $(TPL_TRACKER_LIBS) check_c_sources = \ $(libtelepathy_logger_la_SOURCES) \ @@ -128,6 +136,11 @@ introspection_sources = $(libtelepathy_logger_la_SOURCES) $(LIBTPL_HEADERS) TelepathyLogger-0.2.gir: libtelepathy-logger.la TelepathyLogger_0_2_gir_SCANNERFLAGS = --identifier-prefix=Tpl --warn-all TelepathyLogger_0_2_gir_INCLUDES = GObject-2.0 TelepathyGLib-0.12 + +if HAVE_TRACKER_MINER +TelepathyLogger_0_2_gir_INCLUDES += TrackerMiner-0.11 +endif + TelepathyLogger_0_2_gir_CFLAGS = $(INCLUDES) TelepathyLogger_0_2_gir_LIBS = libtelepathy-logger.la TelepathyLogger_0_2_gir_FILES = $(filter-out %-internal.h, $(introspection_sources)) diff --git a/telepathy-logger/log-manager.c b/telepathy-logger/log-manager.c index 4f1ed9e..a72c8ce 100644 --- a/telepathy-logger/log-manager.c +++ b/telepathy-logger/log-manager.c @@ -42,6 +42,10 @@ #include #include +#ifdef HAVE_TRACKER_MINER +#include +#endif /* HAVE_TRACKER_MINER */ + #define DEBUG_FLAG TPL_DEBUG_LOG_MANAGER #include #include @@ -197,6 +201,9 @@ add_log_store (TplLogManager *self, "writable", writable, NULL); + if (G_IS_INITABLE (store)) + g_initable_init (G_INITABLE (store), NULL, NULL); + /* set the log store in "testmode" if it supports it and the environment is * currently in test mode */ if (g_object_class_find_property (G_OBJECT_GET_CLASS (store), "testmode")) @@ -296,6 +303,11 @@ tpl_log_manager_init (TplLogManager *self) /* Load the event counting cache */ add_log_store (self, TPL_TYPE_LOG_STORE_SQLITE, "Sqlite", FALSE, TRUE); +#ifdef HAVE_TRACKER_MINER + /* Add tracker log store */ + add_log_store (self, TPL_TYPE_LOG_STORE_TRACKER, "Tracker", FALSE, TRUE); +#endif /* HAVE_TRACKER_MINER */ + DEBUG ("Log Manager initialised"); } diff --git a/telepathy-logger/log-store-factory.c b/telepathy-logger/log-store-factory.c index 894677a..5ff2b1f 100644 --- a/telepathy-logger/log-store-factory.c +++ b/telepathy-logger/log-store-factory.c @@ -49,6 +49,8 @@ _tpl_log_store_factory_add (const gchar *logstore_type, key = g_strdup (logstore_type); + g_print (">>>> %s\n", logstore_type); + if (g_hash_table_lookup (logstores_table, logstore_type) != NULL) { g_warning ("Type %s already mapped. replacing constructor.", diff --git a/telepathy-logger/log-store-tracker-internal.h b/telepathy-logger/log-store-tracker-internal.h new file mode 100644 index 0000000..0208157 --- /dev/null +++ b/telepathy-logger/log-store-tracker-internal.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011 Carlos Garnacho + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Carlos Garnacho + */ + +#ifndef __TPL_LOG_STORE_TRACKER_H__ +#define __TPL_LOG_STORE_TRACKER_H__ + +#include +#include +#include + +G_BEGIN_DECLS +#define TPL_TYPE_LOG_STORE_TRACKER \ + (_tpl_log_store_tracker_get_type ()) +#define TPL_LOG_STORE_TRACKER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPL_TYPE_LOG_STORE_TRACKER, \ + TplLogStoreTracker)) +#define TPL_LOG_STORE_TRACKER_CLASS(vtable) \ + (G_TYPE_CHECK_CLASS_CAST ((vtable), TPL_TYPE_LOG_STORE_TRACKER, \ + TplLogStoreTrackerClass)) +#define TPL_IS_LOG_STORE_TRACKER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPL_TYPE_LOG_STORE_TRACKER)) +#define TPL_IS_LOG_STORE_TRACKER_CLASS(vtable) \ + (G_TYPE_CHECK_CLASS_TYPE ((vtable), TPL_TYPE_LOG_STORE_TRACKER)) +#define TPL_LOG_STORE_TRACKER_GET_CLASS(inst) \ + (G_TYPE_INSTANCE_GET_CLASS ((inst), TPL_TYPE_LOG_STORE_TRACKER, \ + TplLogStoreTrackerClass)) + +typedef struct _TplLogStoreTrackerPriv TplLogStoreTrackerPriv; + +typedef struct TplLogStoreTracker +{ + TrackerMiner parent_instance; + TplLogStoreTrackerPriv *priv; +} TplLogStoreTracker; + +typedef struct +{ + TrackerMinerClass parent_class; +} TplLogStoreTrackerClass; + +GType _tpl_log_store_tracker_get_type (void); + +G_END_DECLS + +#endif /* __TPL_LOG_STORE_TRACKER_H__ */ diff --git a/telepathy-logger/log-store-tracker.c b/telepathy-logger/log-store-tracker.c new file mode 100644 index 0000000..c7ca5d9 --- /dev/null +++ b/telepathy-logger/log-store-tracker.c @@ -0,0 +1,1461 @@ +/* + * Copyright (C) 2011 Carlos Garnacho + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Carlos Garnacho + */ + +#include "config.h" +#include "log-store-tracker-internal.h" + +#include "telepathy-logger/call-event.h" +#include "telepathy-logger/call-event-internal.h" +#include "telepathy-logger/entity-internal.h" +#include "telepathy-logger/event-internal.h" +#include "telepathy-logger/text-event.h" +#include "telepathy-logger/text-event-internal.h" +#include "telepathy-logger/log-manager.h" +#include "telepathy-logger/log-store-internal.h" +#include "telepathy-logger/log-manager-internal.h" +#include "telepathy-logger/util-internal.h" + +#define TPL_LOG_STORE_TRACKER_GRAPH_URN "urn:uuid:287cf12d-debb-cd35-64e3-c4bf36a9246d" +#define TPL_LOG_STORE_ACCOUNT_URN_PREFIX "urn:telepathy-account:" +#define TPL_LOG_STORE_CONTACT_URN_PREFIX "urn:telepathy-contact:" +#define TPL_LOG_STORE_ROOM_URN_PREFIX "urn:telepathy-room:" +#define TPL_LOG_STORE_CHANNEL_URN_PREFIX "urn:telepathy-communication-channel:" + +typedef struct _ContactCrawler ContactCrawler; +typedef struct _EventCrawler EventCrawler; +typedef struct _QueryData QueryData; + +struct _TplLogStoreTrackerPriv +{ + TpAccountManager *account_manager; + TplLogManager *log_manager; + gchar *name; + guint readable : 1; + guint writable : 1; + guint flushing : 1; + guint initializing : 1; + + /* account -> list of entities */ + GHashTable *accounts; + + /* ID -> contact */ + GHashTable *contacts; + + /* Crawler to gather all events to be extracted */ + EventCrawler *event_crawler; + + GPtrArray *contacts_buffer; + GPtrArray *events_buffer; +}; + +struct _ContactCrawler +{ + TplLogStoreTracker *log_store; + + GList *accounts; + GList *cur; + + /* IDs of contacts to query */ + GPtrArray *ids; + guint n_processing_ids; + gint ref_count; +}; + +struct _EventCrawler +{ + TplLogStoreTracker *log_store; + /* Accounts/Contacts to crawl */ + GHashTable *crawl_info; + + GList *accounts; + GList *entities; + GList *dates; +}; + +struct _QueryData +{ + TpContact *contact; + ContactCrawler *contact_crawler; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_READABLE, + PROP_WRITABLE +}; + +static void log_store_iface_init (gpointer g_iface, + gpointer iface_data); + +static gboolean init_sparql_flush (TplLogStoreTracker *self); + +static gboolean event_crawler_process_current (EventCrawler *crawler); +static gboolean event_crawler_iter_next (EventCrawler *crawler); +static void event_crawler_free (EventCrawler *crawler); + +static gboolean contact_crawler_iter_next (ContactCrawler *contact_crawler, + gboolean force); + +static ContactCrawler * contact_crawler_ref (ContactCrawler *crawler); +static void contact_crawler_unref (ContactCrawler *crawler); + + +G_DEFINE_TYPE_WITH_CODE (TplLogStoreTracker, _tpl_log_store_tracker, + TRACKER_TYPE_MINER, + G_IMPLEMENT_INTERFACE (TPL_TYPE_LOG_STORE, + log_store_iface_init)) + +static void +tpl_log_store_tracker_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + TplLogStoreTrackerPriv *priv; + + priv = TPL_LOG_STORE_TRACKER (object)->priv; + + switch (param_id) + { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_READABLE: + g_value_set_boolean (value, priv->readable); + break; + case PROP_WRITABLE: + g_value_set_boolean (value, priv->writable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +tpl_log_store_tracker_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + TplLogStoreTrackerPriv *priv; + + priv = TPL_LOG_STORE_TRACKER (object)->priv; + + switch (param_id) + { + case PROP_NAME: + priv->name = g_value_dup_string (value); + break; + case PROP_READABLE: + priv->readable = g_value_get_boolean (value); + break; + case PROP_WRITABLE: + priv->writable = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +tpl_log_store_tracker_finalize (GObject *object) +{ + TplLogStoreTrackerPriv *priv; + + priv = TPL_LOG_STORE_TRACKER (object)->priv; + + g_hash_table_destroy (priv->accounts); + g_hash_table_destroy (priv->contacts); + + event_crawler_free (priv->event_crawler); + + g_ptr_array_free (priv->contacts_buffer, TRUE); + g_ptr_array_free (priv->events_buffer, TRUE); + + G_OBJECT_CLASS (_tpl_log_store_tracker_parent_class)->finalize (object); +} + +static void +_tpl_log_store_tracker_class_init (TplLogStoreTrackerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = tpl_log_store_tracker_get_property; + object_class->set_property = tpl_log_store_tracker_set_property; + object_class->finalize = tpl_log_store_tracker_finalize; + + g_object_class_override_property (object_class, PROP_READABLE, "readable"); + g_object_class_override_property (object_class, PROP_WRITABLE, "writable"); + + g_type_class_add_private (object_class, sizeof (TplLogStoreTrackerPriv)); +} + +static void +builder_add_im_address (TrackerSparqlBuilder *builder, + const gchar *im_id, + const gchar *nickname) +{ + tracker_sparql_builder_object_blank_open (builder); + tracker_sparql_builder_predicate (builder, "a"); + tracker_sparql_builder_object (builder, "nco:IMAddress"); + + tracker_sparql_builder_predicate (builder, "nco:imID"); + tracker_sparql_builder_object_unvalidated (builder, im_id); + + if (nickname) + { + tracker_sparql_builder_predicate (builder, "nco:imNickname"); + tracker_sparql_builder_object_unvalidated (builder, nickname); + } + + tracker_sparql_builder_object_blank_close (builder); +} + +static void +builder_add_contact_info (TrackerSparqlBuilder *builder, + TpContact *contact, + gchar **nickname) +{ + GList *f, *fields = tp_contact_get_contact_info (contact); + + *nickname = NULL; + + for (f = fields; f; f = f->next) + { + TpContactInfoField *field = f->data; + + if (!field->field_name || + !field->field_value || + !field->field_value[0]) + continue; + + if (strcmp (field->field_name, "email") == 0) + { + gchar *mailto; + + /* Create blank node for email*/ + tracker_sparql_builder_predicate (builder, "nco:hasEmailAddress"); + + tracker_sparql_builder_object_blank_open (builder); + tracker_sparql_builder_predicate (builder, "a"); + tracker_sparql_builder_object (builder, "nco:EmailAddress"); + + mailto = g_strdup_printf ("mailto:%s", field->field_value[0]); + tracker_sparql_builder_predicate (builder, "nco:emailAddress"); + tracker_sparql_builder_object_unvalidated (builder, mailto); + g_free (mailto); + + tracker_sparql_builder_object_blank_close (builder); + continue; + } + else if (strcmp (field->field_name, "fn") == 0) + tracker_sparql_builder_predicate (builder, "nco:fullname"); + else if (strcmp (field->field_name, "nickname") == 0) + { + tracker_sparql_builder_predicate (builder, "nco:nickname"); + *nickname = field->field_value[0]; + } + else if (strcmp (field->field_name, "url") == 0) + tracker_sparql_builder_predicate (builder, "nco:url"); + else + continue; + + tracker_sparql_builder_object_unvalidated (builder, field->field_value[0]); + } + + g_list_free (fields); +} + +static void +update_array_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + TplLogStoreTracker *self = user_data; + GPtrArray *errors; + GError *error = NULL; + + errors = tracker_sparql_connection_update_array_finish (TRACKER_SPARQL_CONNECTION (object), + result, &error); + if (error) + { + g_critical ("Could not insert SPARQL: %s", error->message); + g_error_free (error); + } + + if (errors) + { + guint i; + + for (i = 0; i < errors->len; i++) + { + GError *err; + + err = g_ptr_array_index (errors, i); + + if (err) + g_warning ("Individual SPARQL no. %d insert failed: %s\n", + i, err->message); + } + + g_ptr_array_free (errors, TRUE); + } + + self->priv->flushing = FALSE; + + if (!init_sparql_flush (self)) + { + if (event_crawler_iter_next (self->priv->event_crawler)) + event_crawler_process_current (self->priv->event_crawler); + } +} + +static gboolean +init_sparql_flush (TplLogStoreTracker *self) +{ + TrackerSparqlConnection *connection; + guint flush_n_items; + GPtrArray *array; + + if (self->priv->flushing) + return TRUE; + + connection = tracker_miner_get_connection (TRACKER_MINER (self)); + + if (!connection) + return FALSE; + + if (self->priv->contacts_buffer->len > 0) + array = self->priv->contacts_buffer; + else if (self->priv->events_buffer->len > 0) + { + /* Accounts/Contacts are still being inspected, do not process + * events yet, until every data they rely on is already in the + * store. + */ + if (self->priv->initializing) + return FALSE; + + array = self->priv->events_buffer; + } + else + return FALSE; + + flush_n_items = MIN (array->len, 100); + + tracker_sparql_connection_update_array_async (connection, + (gchar **) array->pdata, + flush_n_items, + G_PRIORITY_DEFAULT, + NULL, + update_array_cb, + self); + g_ptr_array_remove_range (array, 0, flush_n_items); + self->priv->flushing = TRUE; + return TRUE; +} + +static void +process_account (TplLogStoreTracker *self, + TpAccount *account) +{ + TrackerSparqlBuilder *builder; + gchar *sparql, *subject; + + builder = tracker_sparql_builder_new_update (); + tracker_sparql_builder_insert_open (builder, TPL_LOG_STORE_TRACKER_GRAPH_URN); + + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_ACCOUNT_URN_PREFIX "%s", + tp_account_get_normalized_name (account)); + tracker_sparql_builder_subject_iri (builder, subject); + + tracker_sparql_builder_predicate (builder, "a"); + tracker_sparql_builder_object (builder, "nco:IMAccount"); + + /* Account address */ + tracker_sparql_builder_predicate (builder, "nco:imAccountAddress"); + + builder_add_im_address (builder, + tp_account_get_normalized_name (account), + tp_account_get_nickname (account)); + + /* Account type */ + tracker_sparql_builder_predicate (builder, "nco:imAccountType"); + tracker_sparql_builder_object_unvalidated (builder, + tp_account_get_protocol (account)); + + /* Display name */ + tracker_sparql_builder_predicate (builder, "nco:imDisplayName"); + tracker_sparql_builder_object_unvalidated (builder, + tp_account_get_display_name (account)); + + tracker_sparql_builder_insert_close (builder); + + sparql = g_strdup_printf ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> ?pred ?obj" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> ?pred ?obj" + " FILTER (?pred != rdf:type && ?p != nco:hasIMContact)" + " }" + "}" + "%s", + subject, subject, + tracker_sparql_builder_get_result (builder)); + + g_ptr_array_add (self->priv->contacts_buffer, sparql); + + g_object_unref (builder); + g_free (subject); +} + +static void +process_contact (TplLogStoreTracker *self, + TpAccount *account, + TpContact *contact, + const gchar *urn) +{ + TrackerSparqlBuilder *builder; + gchar *subject, *nickname; + + builder = tracker_sparql_builder_new_update (); + tracker_sparql_builder_insert_open (builder, TPL_LOG_STORE_TRACKER_GRAPH_URN); + + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_CONTACT_URN_PREFIX "%s", + tp_contact_get_identifier (contact)); + tracker_sparql_builder_subject_iri (builder, subject); + g_free (subject); + + tracker_sparql_builder_predicate (builder, "a"); + tracker_sparql_builder_object (builder, "nco:PersonContact"); + + builder_add_contact_info (builder, contact, &nickname); + + /* Set also IM Address now that we have all data */ + tracker_sparql_builder_predicate (builder, "nco:hasIMAddress"); + builder_add_im_address (builder, + tp_contact_get_identifier (contact), + nickname); + + /* Link the account to this contact's IM address */ + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_ACCOUNT_URN_PREFIX "%s", + tp_account_get_normalized_name (account)); + tracker_sparql_builder_subject_iri (builder, subject); + g_free (subject); + + tracker_sparql_builder_predicate (builder, "nco:hasIMContact"); + builder_add_im_address (builder, + tp_contact_get_identifier (contact), + nickname); + + tracker_sparql_builder_insert_close (builder); + + if (urn) + { + gchar *sparql; + + /* Item already existed, delete anything + * within our graph so it is updated with + * the new info. + * + * we only preserve rdf:type as it could + * lead to deletion of user data. + */ + sparql = g_strdup_printf ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> ?pred ?obj" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> ?pred ?obj" + " FILTER (?pred != rdf:type)" + " }" + "}" + "%s", + urn, urn, + tracker_sparql_builder_get_result (builder)); + + g_ptr_array_add (self->priv->contacts_buffer, sparql); + } + else + g_ptr_array_add (self->priv->contacts_buffer, + g_strdup (tracker_sparql_builder_get_result (builder))); + + g_object_unref (builder); +} + +static gboolean +process_communication_channel (TplLogStoreTracker *self, + TplEntity *entity) +{ + TrackerSparqlBuilder *builder; + gchar *subject = NULL; + TplEntityType type; + + type = tpl_entity_get_entity_type (entity); + + if (type == TPL_ENTITY_ROOM) + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_ROOM_URN_PREFIX "%s", + tpl_entity_get_identifier (entity)); + else if (type == TPL_ENTITY_CONTACT) + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_CHANNEL_URN_PREFIX "%s", + tpl_entity_get_identifier (entity)); + else + return FALSE; + + builder = tracker_sparql_builder_new_update (); + tracker_sparql_builder_insert_open (builder, TPL_LOG_STORE_TRACKER_GRAPH_URN); + + tracker_sparql_builder_subject_iri (builder, subject); + + tracker_sparql_builder_predicate (builder, "a"); + + if (type == TPL_ENTITY_ROOM) + tracker_sparql_builder_object (builder, "nmo:PermanentChannel"); + else + tracker_sparql_builder_object (builder, "nmo:CommunicationChannel"); + + tracker_sparql_builder_insert_close (builder); + + g_ptr_array_add (self->priv->contacts_buffer, + g_strdup (tracker_sparql_builder_get_result (builder))); + + g_object_unref (builder); + g_free (subject); + + return TRUE; +} + +static void +builder_add_entity (TplLogStoreTracker *self, + TrackerSparqlBuilder *builder, + const gchar *predicate, + TplEntity *entity) +{ + TpContact *contact; + + switch (tpl_entity_get_entity_type (entity)) + { + case TPL_ENTITY_SELF: + tracker_sparql_builder_predicate (builder, predicate); + tracker_sparql_builder_object (builder, "nco:default-contact-me"); + break; + case TPL_ENTITY_CONTACT: + tracker_sparql_builder_predicate (builder, predicate); + + contact = g_hash_table_lookup (self->priv->contacts, + tpl_entity_get_identifier (entity)); + if (contact) + { + gchar *subject; + + /* Contact already exists */ + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_CONTACT_URN_PREFIX "%s", + tpl_entity_get_identifier (entity)); + tracker_sparql_builder_object_iri (builder, subject); + g_free (subject); + } + else + { + /* Unknown contact, probably some contact in a room */ + tracker_sparql_builder_object_blank_open (builder); + tracker_sparql_builder_predicate (builder, "a"); + tracker_sparql_builder_object (builder, "nco:Contact"); + + tracker_sparql_builder_predicate (builder, "nco:nickname"); + tracker_sparql_builder_object_unvalidated (builder, + tpl_entity_get_alias (entity)); + + tracker_sparql_builder_object_blank_close (builder); + } + break; + default: + break; + } +} + +static gboolean +process_event (TplLogStoreTracker *self, + TplEvent *event) +{ + TplEntity *sender, *receiver; + TrackerSparqlBuilder *builder; + gint64 timestamp; + + sender = tpl_event_get_sender (event); + receiver = tpl_event_get_receiver (event); + timestamp = tpl_event_get_timestamp (event); + + builder = tracker_sparql_builder_new_update (); + tracker_sparql_builder_insert_open (builder, TPL_LOG_STORE_TRACKER_GRAPH_URN); + + if (TPL_IS_TEXT_EVENT (event)) + { + const gchar *message; + gchar *conversation_urn; + + /* Add participants to conversation channel / room */ + if (tpl_entity_get_entity_type (receiver) == TPL_ENTITY_ROOM) + { + /* message in permanent conversation room */ + conversation_urn = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_ROOM_URN_PREFIX "%s", + tpl_entity_get_identifier (receiver)); + } + else + { + /* message between 2 contacts, find out which is the peer */ + if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF) + conversation_urn = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_CHANNEL_URN_PREFIX "%s", + tpl_entity_get_identifier (receiver)); + else + conversation_urn = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_CHANNEL_URN_PREFIX "%s", + tpl_entity_get_identifier (sender)); + } + + tracker_sparql_builder_subject_iri (builder, conversation_urn); + tracker_sparql_builder_predicate (builder, "a"); + tracker_sparql_builder_object (builder, "nmo:CommunicationChannel"); + + builder_add_entity (self, builder, "nmo:hasParticipant", sender); + builder_add_entity (self, builder, "nmo:hasParticipant", receiver); + + /* now on to the message */ + tracker_sparql_builder_subject (builder, "_:message"); + tracker_sparql_builder_predicate (builder, "a"); + tracker_sparql_builder_object (builder, "nmo:IMMessage"); + + /* Message content */ + message = tpl_text_event_get_message (TPL_TEXT_EVENT (event)); + tracker_sparql_builder_predicate (builder, "nie:plainTextContent"); + tracker_sparql_builder_object_unvalidated (builder, message); + + /* Communication channel */ + tracker_sparql_builder_predicate (builder, "nmo:communicationChannel"); + tracker_sparql_builder_object_iri (builder, conversation_urn); + + g_free (conversation_urn); + } + else if (TPL_IS_CALL_EVENT (event)) + { + GTimeSpan duration; + + tracker_sparql_builder_subject (builder, "_:call"); + tracker_sparql_builder_predicate (builder, "a"); + tracker_sparql_builder_object (builder, "nmo:Call"); + + /* Call duration, in seconds */ + duration = tpl_call_event_get_duration (TPL_CALL_EVENT (event)); + tracker_sparql_builder_predicate (builder, "nmo:duration"); + tracker_sparql_builder_object_int64 (builder, + duration / G_TIME_SPAN_SECOND); + } + else + { + g_warning ("Unknown event type '%s'", G_OBJECT_TYPE_NAME (event)); + g_object_unref (builder); + return FALSE; + } + + /* Sent date */ + tracker_sparql_builder_predicate (builder, "nmo:sentDate"); + tracker_sparql_builder_object_date (builder, (time_t *) ×tamp); + + /* From, to */ + builder_add_entity (self, builder, "nmo:from", sender); + builder_add_entity (self, builder, "nmo:to", receiver); + + tracker_sparql_builder_insert_close (builder); + + g_ptr_array_add (self->priv->events_buffer, + g_strdup (tracker_sparql_builder_get_result (builder))); + + g_object_unref (builder); + + return TRUE; +} + +static void +event_crawler_get_events_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + EventCrawler *crawler = user_data; + GError *error = NULL; + GList *events, *l; + + if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (object), + result, &events, &error)) + { + g_warning ("Could not get events for date: %s\n", error->message); + g_error_free (error); + } + + if (events) + { + for (l = events; l; l = l->next) + process_event (crawler->log_store, + l->data); + + g_list_foreach (events, (GFunc) g_object_unref, NULL); + g_list_free (events); + } + + if (!crawler->dates->next) + init_sparql_flush (crawler->log_store); + else if (event_crawler_iter_next (crawler)) + event_crawler_process_current (crawler); +} + +static void +event_crawler_get_dates_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + EventCrawler *crawler = user_data; + GError *error = NULL; + + if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (object), result, + &crawler->dates, &error)) + { + g_warning ("Could not get event dates for entity: %s\n", error->message); + g_error_free (error); + + } + + if (!crawler->dates) + event_crawler_iter_next (crawler); + + event_crawler_process_current (crawler); +} + +static gboolean +event_crawler_process_current (EventCrawler *crawler) +{ + TplLogStoreTracker *self; + + self = crawler->log_store; + + if (!crawler->accounts) + return FALSE; + + if (!crawler->entities && + !crawler->dates) + { + crawler->entities = g_hash_table_lookup (crawler->crawl_info, + crawler->accounts->data); + return event_crawler_process_current (crawler); + } + + if (!crawler->dates) + { + tpl_log_manager_get_dates_async (self->priv->log_manager, + crawler->accounts->data, + crawler->entities->data, + TPL_EVENT_MASK_ANY, + event_crawler_get_dates_cb, + crawler); + return TRUE; + } + + tpl_log_manager_get_events_for_date_async (self->priv->log_manager, + crawler->accounts->data, + crawler->entities->data, + TPL_EVENT_MASK_ANY, + crawler->dates->data, + event_crawler_get_events_cb, + crawler); + return TRUE; +} + +static gboolean +event_crawler_iter_next (EventCrawler *crawler) +{ + if (!crawler->dates && + !crawler->entities && + !crawler->accounts) + return FALSE; + + if (crawler->dates) + { + g_date_free (crawler->dates->data); + crawler->dates = g_list_delete_link (crawler->dates, crawler->dates); + } + + if (!crawler->dates) + { + if (crawler->entities) + crawler->entities = g_list_delete_link (crawler->entities, + crawler->entities); + + /* Get to the next account with some entity */ + while (!crawler->entities && + crawler->accounts) + { + crawler->accounts = g_list_delete_link (crawler->accounts, + crawler->accounts); + + if (crawler->accounts) + crawler->entities = g_hash_table_lookup (crawler->crawl_info, + crawler->accounts->data); + } + } + + return (crawler->accounts != NULL); +} + +static EventCrawler * +event_crawler_new (TplLogStoreTracker *log_store) +{ + EventCrawler *crawler; + + crawler = g_slice_new0 (EventCrawler); + crawler->log_store = log_store; + crawler->crawl_info = g_hash_table_new_full (NULL, NULL, + (GDestroyNotify) g_object_unref, + NULL); + return crawler; +} + +static void +event_crawler_add (EventCrawler *crawler, + TpAccount *account, + TplEntity *contact_entity) +{ + GList *entities; + + entities = g_hash_table_lookup (crawler->crawl_info, account); + + if (entities) + entities = g_list_append (entities, g_object_ref (contact_entity)); + else + { + crawler->accounts = g_list_prepend (crawler->accounts, + g_object_ref (account)); + + entities = g_list_prepend (NULL, contact_entity); + g_hash_table_insert (crawler->crawl_info, + g_object_ref (account), + entities); + } +} + +static gboolean +event_crawler_entitities_free (gpointer key, + gpointer value, + gpointer user_data) +{ + GList *entities = value; + + g_list_foreach (entities, (GFunc) g_object_unref, NULL); + g_list_free (entities); + + return TRUE; +} + +void +event_crawler_free (EventCrawler *crawler) +{ + g_hash_table_foreach_remove (crawler->crawl_info, + event_crawler_entitities_free, + NULL); + g_hash_table_destroy (crawler->crawl_info); + + g_list_foreach (crawler->dates, (GFunc) g_date_free, NULL); + g_list_free (crawler->dates); + + /* entities list was owned by the crawl_info HT */ + + g_list_foreach (crawler->accounts, (GFunc) g_object_unref, NULL); + g_list_free (crawler->accounts); + + g_slice_free (EventCrawler, crawler); +} + +static void +event_crawler_start (EventCrawler *crawler) +{ + event_crawler_process_current (crawler); +} + +static void +contact_query_existence_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + QueryData *query_data = user_data; + ContactCrawler *crawler = query_data->contact_crawler; + TrackerSparqlCursor *cursor; + GError *error = NULL; + + cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object), + result, &error); + if (error) + { + g_critical ("Could not query contact: %s", error->message); + g_error_free (error); + } + else + { + if (tracker_sparql_cursor_next (cursor, NULL, NULL)) + process_contact (crawler->log_store, + crawler->cur->data, + query_data->contact, + tracker_sparql_cursor_get_string (cursor, 0, NULL)); + else + { + TplLogStoreTracker *self; + GList *entities; + + /* Unknown contact, find out its corresponding TplEntity + * and have it scheduled for log events extraction */ + self = crawler->log_store; + entities = g_hash_table_lookup (self->priv->accounts, + crawler->cur->data); + while (entities) + { + TplEntity *entity = entities->data; + + entities = entities->next; + + if (strcmp (tpl_entity_get_identifier (entity), + tp_contact_get_identifier (query_data->contact)) == 0) + event_crawler_add (self->priv->event_crawler, + crawler->cur->data, entity); + } + + process_contact (self, + crawler->cur->data, + query_data->contact, + NULL); + } + } + + crawler->n_processing_ids--; + contact_crawler_iter_next (crawler, FALSE); + contact_crawler_unref (crawler); + g_object_unref (cursor); + + g_slice_free (QueryData, query_data); +} + +static void +contact_request_info_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ContactCrawler *crawler = user_data; + gchar *subject, *sparql; + TpContact *contact; + QueryData *query_data; + + contact = TP_CONTACT (object); + tp_contact_request_contact_info_finish (contact, result, NULL); + + /* Query for existence */ + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_CONTACT_URN_PREFIX "%s", + tp_contact_get_identifier (contact)); + sparql = g_strdup_printf ("SELECT <%s> WHERE { <%s> a nco:PersonContact }", + subject, subject); + + query_data = g_slice_new0 (QueryData); + query_data->contact_crawler = crawler; + query_data->contact = contact; + + tracker_sparql_connection_query_async (tracker_miner_get_connection (TRACKER_MINER (crawler->log_store)), + sparql, + NULL, + contact_query_existence_cb, + query_data); + g_free (subject); + g_free (sparql); +} + +static void +contact_crawler_get_entities_contacts_cb (TpConnection *connection, + guint n_contacts, + TpContact * const *contacts, + const gchar * const *requested_ids, + GHashTable *failed_id_errors, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + ContactCrawler *crawler = user_data; + TplLogStoreTracker *self = crawler->log_store; + guint i; + + crawler->n_processing_ids -= g_hash_table_size (failed_id_errors); + + for (i = 0; i < n_contacts; i++) + { + g_hash_table_insert (self->priv->contacts, + g_strdup (tp_contact_get_identifier (contacts[i])), + g_object_ref (contacts[i])); + + tp_contact_request_contact_info_async (contacts[i], + NULL, + contact_request_info_cb, + contact_crawler_ref (crawler)); + } +} + +static void +contact_crawler_get_entities_contacts (ContactCrawler *crawler) +{ + TpConnection *connection; + TpContactFeature features[] = { + TP_CONTACT_FEATURE_ALIAS, + TP_CONTACT_FEATURE_CONTACT_INFO + }; + + connection = tp_account_get_connection (crawler->cur->data); + tp_connection_get_contacts_by_id (connection, + crawler->ids->len, + (gconstpointer) crawler->ids->pdata, + G_N_ELEMENTS (features), features, + contact_crawler_get_entities_contacts_cb, + crawler, NULL, NULL); +} + +static void +contact_crawler_get_entities_contacts_when_ready_cb (TpConnection *connection, + const GError *error, + gpointer user_data) +{ + ContactCrawler *crawler = user_data; + + if (error) + { + g_critical ("Could not query contacts: %s\n", error->message); + contact_crawler_iter_next (crawler, TRUE); + } + else + contact_crawler_get_entities_contacts (crawler); +} + +static void +contact_crawler_get_entities_contacts_when_ready (ContactCrawler *crawler) +{ + TpConnection *connection; + + connection = tp_account_get_connection (crawler->cur->data); + process_account (crawler->log_store, crawler->cur->data); + + if (!connection) + contact_crawler_iter_next (crawler, TRUE); + else + tp_connection_call_when_ready (connection, + contact_crawler_get_entities_contacts_when_ready_cb, + crawler); +} + +static void +contact_crawler_get_entities_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ContactCrawler *crawler = user_data; + TplLogStoreTracker *self = crawler->log_store; + GError *error = NULL; + GList *entities = NULL; + + if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (object), result, + &entities, &error)) + { + g_warning ("Could not get entities for contact: %s\n", error->message); + g_error_free (error); + } + else + { + GPtrArray *ids; + + ids = g_ptr_array_new (); + g_hash_table_insert (self->priv->accounts, + g_object_ref (crawler->cur->data), + entities); + + while (entities) + { + g_ptr_array_add (ids, (gpointer) tpl_entity_get_identifier (entities->data)); + process_communication_channel (self, entities->data); + entities = entities->next; + } + + if (ids->len > 0) + { + crawler->ids = ids; + crawler->n_processing_ids = crawler->ids->len; + + contact_crawler_get_entities_contacts_when_ready (crawler); + return; + } + + g_ptr_array_unref (ids); + } + + /* No contacts or no pending queries, we may proceed in any case */ + contact_crawler_iter_next (crawler, FALSE); +} + +static ContactCrawler * +contact_crawler_new (TplLogStoreTracker *log_store, + GList *accounts) +{ + ContactCrawler *crawler; + + crawler = g_slice_new0 (ContactCrawler); + crawler->accounts = crawler->cur = accounts; + crawler->log_store = log_store; + crawler->ref_count = 1; + + return crawler; +} + +static ContactCrawler * +contact_crawler_ref (ContactCrawler *crawler) +{ + g_atomic_int_inc (&crawler->ref_count); + return crawler; +} + +static void +contact_crawler_unref (ContactCrawler *crawler) +{ + if (g_atomic_int_dec_and_test (&crawler->ref_count)) + { + g_list_free (crawler->accounts); + g_slice_free (ContactCrawler, crawler); + } +} + +static void +contact_crawler_start (ContactCrawler *crawler) +{ + TplLogStoreTracker *self = crawler->log_store; + + tpl_log_manager_get_entities_async (self->priv->log_manager, + crawler->cur->data, + contact_crawler_get_entities_cb, + crawler); +} + +static gboolean +contact_crawler_iter_next (ContactCrawler *crawler, + gboolean force) +{ + TplLogStoreTracker *self; + + if (!force && + crawler->n_processing_ids > 0) + return FALSE; + + if (crawler->ids) + g_ptr_array_unref (crawler->ids); + + self = crawler->log_store; + + if (crawler->cur->next) + { + /* Iterate to the next account */ + crawler->cur = crawler->cur->next; + contact_crawler_start (crawler); + } + else if (self->priv->initializing) + { + self->priv->initializing = FALSE; + contact_crawler_unref (crawler); + + /* Continue on to extracting events */ + event_crawler_start (self->priv->event_crawler); + } + + return TRUE; +} + +static void +proxy_prepared_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + TplLogStoreTracker *self = user_data; + GError *error = NULL; + GList *accounts; + + if (!tp_proxy_prepare_finish (object, result, &error)) + { + g_critical ("Could not prepare proxy: %s", error->message); + g_error_free (error); + return; + } + + self->priv->log_manager = tpl_log_manager_dup_singleton (); + accounts = tp_account_manager_get_valid_accounts (self->priv->account_manager); + + if (accounts) + { + ContactCrawler *crawler; + + crawler = contact_crawler_new (self, accounts); + contact_crawler_start (crawler); + } +} + +static void +contact_unref (TpContact *contact) +{ + if (contact) + g_object_unref (contact); +} + +static void +_tpl_log_store_tracker_init (TplLogStoreTracker *self) +{ + GQuark connection_features [] = { TP_CONNECTION_FEATURE_CORE, 0 }; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TPL_TYPE_LOG_STORE_TRACKER, + TplLogStoreTrackerPriv); + self->priv->account_manager = tp_account_manager_dup (); + self->priv->accounts = g_hash_table_new_full (NULL, NULL, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) g_list_free); + self->priv->contacts = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) contact_unref); + self->priv->events_buffer = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + self->priv->contacts_buffer = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + self->priv->initializing = TRUE; + self->priv->event_crawler = event_crawler_new (self); + + tp_proxy_prepare_async (self->priv->account_manager, + connection_features, + proxy_prepared_cb, + self); +} + +static gboolean +log_store_tracker_add_event (TplLogStore *store, + TplEvent *event, + GError **error) +{ + TplLogStoreTracker *self; + + self = TPL_LOG_STORE_TRACKER (store); + process_event (self, event); + + if (!self->priv->initializing) + init_sparql_flush (self); + + return TRUE; +} + +static void +log_store_tracker_clear (TplLogStore *store) +{ + TplLogStoreTracker *self; + gchar *sparql; + + self = TPL_LOG_STORE_TRACKER (store); + sparql = g_strdup ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " ?u a rdfs:Resource" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " ?u a rdfs:Resource" + " }" + "}"); + + g_ptr_array_add (self->priv->contacts_buffer, sparql); + init_sparql_flush (self); +} + +static void +log_store_tracker_clear_account (TplLogStore *store, + TpAccount *account) +{ + TplLogStoreTracker *self; + gchar *sparql, *subject; + + self = TPL_LOG_STORE_TRACKER (store); + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_ACCOUNT_URN_PREFIX "%s", + tp_account_get_normalized_name (account)); + /* Delete every message with + * all contacts in the account + */ + sparql = g_strdup_printf ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " ?message a rdfs:Resource;" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> a nco:IMAccount ;" + " nco:hasIMContact ?address ." + " ?contact a nco:Contact ;" + " nco:hasIMAddress ?address ." + " ?channel a nmo:CommunicationChannel ;" + " nmo:hasParticipant ?contact ." + " ?message a nmo:IMMessage;" + " nmo:communicationChannel ?channel" + " }" + "}", + subject); + g_ptr_array_add (self->priv->contacts_buffer, sparql); + + /* Delete all communication channels + * between the account and its contacts + */ + sparql = g_strdup_printf ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " ?channel a rdfs:Resource" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> a nco:IMAccount ;" + " nco:hasIMContact ?address ." + " ?contact a nco:Contact ;" + " nco:hasIMAddress ?address ." + " ?channel a nmo:CommunicationChannel ;" + " nmo:hasParticipant ?contact" + " }" + "}", + subject); + g_ptr_array_add (self->priv->contacts_buffer, sparql); + + /* Delete all contacts in the account */ + sparql = g_strdup_printf ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " ?contact a rdfs:Resource" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> a nco:IMAccount ;" + " nco:hasIMContact ?address ." + " ?contact a nco:Contact ;" + " nco:hasIMAddress ?address" + " }" + "}", + subject); + g_ptr_array_add (self->priv->contacts_buffer, sparql); + + /* Delete the account itself */ + sparql = g_strdup_printf ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> a rdfs:Resource" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> a nco:IMAccount" + " }" + "}", + subject, subject); + g_ptr_array_add (self->priv->contacts_buffer, sparql); + + init_sparql_flush (self); + g_free (subject); +} + +static void +log_store_tracker_clear_entity (TplLogStore *store, + TpAccount *account, + TplEntity *entity) +{ + TplLogStoreTracker *self; + gchar *sparql, *subject; + TplEntityType type; + + self = TPL_LOG_STORE_TRACKER (store); + type = tpl_entity_get_entity_type (entity); + + if (type == TPL_ENTITY_ROOM) + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_ROOM_URN_PREFIX "%s", + tpl_entity_get_identifier (entity)); + else if (type == TPL_ENTITY_CONTACT) + subject = tracker_sparql_escape_uri_printf (TPL_LOG_STORE_CHANNEL_URN_PREFIX "%s", + tpl_entity_get_identifier (entity)); + else + return; + + /* Delete every message with the given entity */ + sparql = g_strdup_printf ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " ?message a rdfs:Resource;" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " ?message a nmo:IMMessage;" + " nmo:communicationChannel <%s>" + " }" + "}", + subject); + g_ptr_array_add (self->priv->contacts_buffer, sparql); + + if (type == TPL_ENTITY_CONTACT) + { + /* Delete the contact associated to + * the entity's communication channel + */ + sparql = g_strdup_printf ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " ?contact a rdfs:Resource" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> a nmo:CommunicationChannel ;" + " nmo:hasParticipant ?contact" + " FILTER (?contact != )" + " }" + "}", + subject); + g_ptr_array_add (self->priv->contacts_buffer, sparql); + } + + /* Delete the entity itself */ + sparql = g_strdup_printf ("DELETE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> a rdfs:Resource" + " }" + "} WHERE {" + " GRAPH <" TPL_LOG_STORE_TRACKER_GRAPH_URN "> {" + " <%s> a nmo:CommunicationChannel ;" + " }" + "}", + subject, subject); + g_ptr_array_add (self->priv->contacts_buffer, sparql); + + init_sparql_flush (self); +} + +static void +log_store_iface_init (gpointer g_iface, + gpointer iface_data) +{ + TplLogStoreInterface *iface = (TplLogStoreInterface *) g_iface; + + iface->add_event = log_store_tracker_add_event; + iface->clear = log_store_tracker_clear; + iface->clear_account = log_store_tracker_clear_account; + iface->clear_entity = log_store_tracker_clear_entity; +} diff --git a/tests/dbus/Makefile.am b/tests/dbus/Makefile.am index 1cca85d..422de4f 100644 --- a/tests/dbus/Makefile.am +++ b/tests/dbus/Makefile.am @@ -12,6 +12,7 @@ TESTS = $(noinst_PROGRAMS) LDADD = \ $(top_builddir)/tests/lib/libtp-logger-tests.la \ $(top_builddir)/telepathy-logger/libtelepathy-logger.la \ + $(TPL_TRACKER_LIBS) \ $(TPL_LIBS) \ $(NULL) @@ -27,6 +28,7 @@ check-local: check-coding-style AM_CFLAGS = \ $(ERROR_CFLAGS) \ $(TPL_CFLAGS) \ + $(TPL_TRACKER_CFLAGS) \ -I$(top_srcdir) \ -I$(top_builddir) \ -I$(top_srcdir)/tests -- 1.7.6