diff options
author | Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> | 2010-01-13 14:58:08 +0000 |
---|---|---|
committer | Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> | 2010-01-13 14:58:08 +0000 |
commit | c5a57433eb4c2ade0fc110d98093eea2bb42de3e (patch) | |
tree | cecda8ac7bdc2bab944c8e16cbcab5e610dfc89c /telepathy-logger | |
parent | 4ed7270537dce069ee0f73d24a575043828f1622 (diff) | |
download | telepathy-logger-c5a57433eb4c2ade0fc110d98093eea2bb42de3e.tar.gz |
Async API infrastructure
* moved code from libtelepathy-logger/ to telepathy-logger/
* used /usr/bin/indent filter with GNU style for source formatting
* added log-manager's async APIs infrastructure using GIO
* addes some async method using the async infrastructure
* added gconf infrastructure in the telepathy-logger/conf module
Diffstat (limited to 'telepathy-logger')
28 files changed, 5725 insertions, 0 deletions
diff --git a/telepathy-logger/Makefile.am b/telepathy-logger/Makefile.am new file mode 100644 index 0000000..34ce6dd --- /dev/null +++ b/telepathy-logger/Makefile.am @@ -0,0 +1,57 @@ +include $(top_srcdir)/tools/shave.mk +include $(top_srcdir)/tools/flymake.mk + +AM_CPPFLAGS = \ + $(ERROR_CFLAGS) \ + -DG_LOG_DOMAIN=\"telepathy-logger\" \ + $(LIBTPL_CFLAGS) \ + $(DISABLE_DEPRECATED) \ + $(WARN_CFLAGS) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libtelepathy-logger.pc + +lib_LTLIBRARIES = libtelepathy-logger.la + +LIBTPLdir = $(includedir)/telepathy-logger +LIBTPL_HEADERS = \ + observer.h \ + channel.h \ + contact.h \ + log-entry.h \ + log-entry-text.h \ + log-manager.h \ + log-manager-priv.h \ + log-store-empathy.h \ + log-store.h \ + channel-text.h \ + datetime.h \ + utils.h + + +libtelepathy_logger_la_SOURCES = \ + observer.c \ + channel.c \ + channel-text.c \ + log-entry.c \ + log-entry-text.c \ + contact.c \ + log-manager.c \ + log-store.c \ + log-store-empathy.c \ + utils.c \ + datetime.c + +schemadir = @GCONF_SCHEMA_FILE_DIR@ +schema_DATA = telepathy-logger.schemas + +install-data-local: + GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) \ + --makefile-install-rule $(srcdir)/$(schema_DATA) + +check_c_sources = \ + $(libtelepathy_logger_la_SOURCES) \ +include $(top_srcdir)/tools/check-coding-style.mk +check-local: check-coding-style + +CLEANFILES = $(BUILT_SOURCES) diff --git a/telepathy-logger/channel-text.c b/telepathy-logger/channel-text.c new file mode 100644 index 0000000..ca8e679 --- /dev/null +++ b/telepathy-logger/channel-text.c @@ -0,0 +1,708 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +/* + * This object acts as a Text Channel context, handling a automaton to + * set up all the needed information before connect to Text iface + * signals. + */ + +#include "channel-text.h" + +#include <telepathy-glib/contact.h> +#include <telepathy-glib/enums.h> + +#include <telepathy-logger/contact.h> +#include <telepathy-logger/channel.h> +#include <telepathy-logger/observer.h> +#include <telepathy-logger/log-entry.h> +#include <telepathy-logger/log-entry-text.h> +#include <telepathy-logger/log-manager-priv.h> + +#define TP_CONTACT_FEATURES_LEN 2 +#define TP_CONTACT_MYSELF 0 +#define TP_CONTACT_REMOTE 1 + +typedef void (*TplPendingProc) (TplTextChannel * self); + +static TpContactFeature features[TP_CONTACT_FEATURES_LEN] = { + TP_CONTACT_FEATURE_ALIAS, + TP_CONTACT_FEATURE_PRESENCE +}; + +/* Signal's Callbacks */ + +static void +_channel_on_closed_cb (TpChannel * proxy, + gpointer user_data, GObject * weak_object) +{ + TplTextChannel *tpl_text = TPL_TEXT_CHANNEL (user_data); + TplChannel *tpl_chan = tpl_text_channel_get_tpl_channel (tpl_text); + gchar *chan_path; + + chan_path = g_strdup (tpl_channel_get_channel_path (tpl_chan)); + + if (!tpl_channel_unregister_from_observer (tpl_chan)) + g_warning ("Channel %s couldn't be unregistered correctly (BUG?)\n", + chan_path); + + g_free (chan_path); +} + +static void +_channel_on_lost_message_cb (TpChannel * proxy, + gpointer user_data, GObject * weak_object) +{ + g_debug ("lost message signal catched. nothing logged\n"); + // TODO log that the system lost a message +} + +static void +_channel_on_send_error_cb (TpChannel * proxy, + guint arg_Error, + guint arg_Timestamp, + guint arg_Type, + const gchar * arg_Text, + gpointer user_data, GObject * weak_object) +{ + g_error ("unlogged event: " + "TP was unable to send the message: %s.\n", arg_Text); + // TODO log that the system was unable to send the message +} + + +static void +_channel_on_sent_signal_cb (TpChannel * proxy, + guint arg_Timestamp, + guint arg_Type, + const gchar * arg_Text, + gpointer user_data, GObject * weak_object) +{ + GError *error = NULL; + TplTextChannel *tpl_text = TPL_TEXT_CHANNEL (user_data); + TpContact *remote, *me; + TplContact *tpl_contact_sender; + TplContact *tpl_contact_receiver; + TplLogEntryText *tlog; + TplLogEntry *log; + TplLogManager *logmanager; + gchar *chat_id; + + g_assert (TPL_IS_TEXT_CHANNEL (tpl_text)); + g_return_if_fail (TPL_IS_TEXT_CHANNEL (tpl_text)); + + /* Initialize data for TplContact */ + me = tpl_text_channel_get_my_contact (tpl_text); + remote = tpl_text_channel_get_remote_contact (tpl_text); + + if (!tpl_text_channel_is_chatroom (tpl_text) && remote == NULL) + { + g_error ("Sending message: Remote TplContact NULL on 1-1 Chat\n"); + } + + tpl_contact_sender = tpl_contact_from_tp_contact (me); + tpl_contact_set_contact_type (tpl_contact_sender, TPL_CONTACT_USER); + tpl_contact_receiver = tpl_contact_from_tp_contact (remote); + tpl_contact_set_contact_type (tpl_contact_receiver, TPL_CONTACT_USER); + + g_message ("%s (%s): %s\n", + tpl_contact_get_identifier (tpl_contact_sender), + tpl_contact_get_alias (tpl_contact_sender), arg_Text); + + /* Initialize TplLogEntryText */ + log = tpl_log_entry_new (); + tlog = tpl_log_entry_text_new (); + + tpl_log_entry_text_set_tpl_text_channel (tlog, tpl_text); + tpl_log_entry_text_set_sender (tlog, tpl_contact_sender); + tpl_log_entry_text_set_receiver (tlog, tpl_contact_receiver); + tpl_log_entry_text_set_message (tlog, arg_Text); + tpl_log_entry_text_set_message_type (tlog, arg_Type); + tpl_log_entry_text_set_signal_type (tlog, TPL_LOG_ENTRY_TEXT_SIGNAL_SENT); + tpl_log_entry_text_set_message_id (tlog, 123); + + tpl_log_entry_set_entry (log, tlog); + tpl_log_entry_set_timestamp (log, (time_t) arg_Timestamp); + + /* Initialized LogStore and send the log entry */ + + if (!tpl_text_channel_is_chatroom (tpl_text)) + chat_id = g_strdup (tpl_contact_get_identifier (tpl_contact_receiver)); + else + chat_id = g_strdup (tpl_text_channel_get_chatroom_id (tpl_text)); + + tpl_log_entry_text_set_chat_id (tlog, chat_id); + + logmanager = tpl_log_manager_dup_singleton (); + + tpl_log_manager_add_message (logmanager, chat_id, + tpl_text_channel_is_chatroom (tpl_text), + log, &error); + + if (error != NULL) + { + g_error ("LogStore: %s", error->message); + g_clear_error (&error); + g_error_free (error); + } + + g_object_unref (tpl_contact_receiver); + g_object_unref (tpl_contact_sender); + g_object_unref (logmanager); + g_object_unref (log); + g_free (chat_id); +} + +static void +_channel_on_received_signal_with_contact_cb (TpConnection * connection, + guint n_contacts, + TpContact * const *contacts, + guint n_failed, + const TpHandle * failed, + const GError * error, + gpointer user_data, + GObject * weak_object) +{ + TplLogEntry *log = TPL_LOG_ENTRY (user_data); + TplLogEntryText *tlog = TPL_LOG_ENTRY_TEXT (tpl_log_entry_get_entry (log)); + TplTextChannel *tpl_text = tpl_log_entry_text_get_tpl_text_channel (tlog); + GError *e = NULL; + TplLogManager *logmanager; + TplContact *tpl_contact_sender; + TpContact *remote; + gchar *chat_id; + + g_return_if_fail (TPL_IS_LOG_ENTRY (log)); + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (tlog)); + + if (error != NULL) + { + g_error ("Unrecoverable error retrieving remote contact " + "information: %s\n", error->message); + g_error ("Not able to log the received message: %s\n", + tpl_log_entry_text_get_message (tlog)); + return; + } + + if (n_failed > 0) + { + g_error ("%d invalid handle(s) passed to " + "tp_connection_get_contacts_by_handle()\n", n_failed); + g_error ("Not able to log the received message: %s\n", + tpl_log_entry_text_get_message (tlog)); + return; + } + + remote = contacts[0]; + tpl_text_channel_set_remote_contact (tpl_text, remote); + tpl_contact_sender = tpl_contact_from_tp_contact (remote); + + tpl_contact_set_contact_type (tpl_contact_sender, TPL_CONTACT_USER); + tpl_log_entry_text_set_sender (tlog, tpl_contact_sender); + + g_message ("%s (%s): %s\n", + tpl_contact_get_identifier (tpl_contact_sender), + tpl_contact_get_alias (tpl_contact_sender), + tpl_log_entry_text_get_message (tlog)); + + /* Initialize LogStore and store the message */ + + if (!tpl_text_channel_is_chatroom (tpl_text)) + chat_id = g_strdup (tpl_contact_get_identifier (tpl_contact_sender)); + else + chat_id = g_strdup (tpl_text_channel_get_chatroom_id (tpl_text)); + + tpl_log_entry_text_set_chat_id (tlog, chat_id); + + logmanager = tpl_log_manager_dup_singleton (); + tpl_log_manager_add_message (logmanager, + tpl_log_entry_text_get_chat_id (tlog), + tpl_text_channel_is_chatroom (tpl_text), + log, &e); + if (e != NULL) + { + g_error ("LogStore: %s", e->message); + g_clear_error (&e); + g_error_free (e); + } + + g_object_unref (tpl_contact_sender); + g_object_unref (logmanager); + g_free (chat_id); +} + +static void +_channel_on_received_signal_cb (TpChannel * proxy, + guint arg_ID, + guint arg_Timestamp, + guint arg_Sender, + guint arg_Type, + guint arg_Flags, + const gchar * arg_Text, + gpointer user_data, GObject * weak_object) +{ + TpHandle remote_handle = (TpHandle) arg_Sender; + TplTextChannel *tpl_text = TPL_TEXT_CHANNEL (user_data); + TplChannel *tpl_chan = tpl_text_channel_get_tpl_channel (tpl_text); + TpContact *me; + TplContact *tpl_contact_receiver; + TplLogEntry *log; + TplLogEntryText *tlog; + + g_message ("ID: %d\n", arg_ID); + + // TODO use the Message iface to check the delivery + // notification and handle it correctly + if (arg_Flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT) + { + g_debug ("Non text content flag set." + "Probably a delivery notification for a sent message." + "Ignoring\n"); + return; + } + + /* Initialize TplLogEntryText (part 1) */ + log = tpl_log_entry_new (); + tlog = tpl_log_entry_text_new (); + tpl_log_entry_set_entry (log, tlog); + + tpl_log_entry_text_set_tpl_text_channel (tlog, tpl_text); + tpl_log_entry_text_set_message (tlog, arg_Text); + tpl_log_entry_text_set_message_type (tlog, arg_Type); + tpl_log_entry_text_set_signal_type (tlog, + TPL_LOG_ENTRY_TEXT_SIGNAL_RECEIVED); + tpl_log_entry_text_set_message_id (tlog, 123); //TODO set a real Id + + me = tpl_text_channel_get_my_contact (tpl_text); + tpl_contact_receiver = tpl_contact_from_tp_contact (me); + tpl_contact_set_contact_type (tpl_contact_receiver, TPL_CONTACT_USER); + tpl_log_entry_text_set_receiver (tlog, tpl_contact_receiver); + + tpl_log_entry_set_timestamp (log, (time_t) arg_Timestamp); + + tp_connection_get_contacts_by_handle (tpl_channel_get_connection (tpl_chan), + 1, &remote_handle, + TP_CONTACT_FEATURES_LEN, features, + _channel_on_received_signal_with_contact_cb, + log, g_object_unref, NULL); + + g_object_unref (tpl_contact_receiver); +} + +/* End of Signal's Callbacks */ + + +/* Context related operations */ + +static void +context_continue (TplTextChannel * ctx) +{ + if (g_queue_is_empty (ctx->chain)) + { + // TODO do some sanity checks + } + else + { + TplPendingProc next = g_queue_pop_head (ctx->chain); + next (ctx); + } +} + +/* Context TplPendingProc and related CB */ + +/* Connect signals to TplTextChannel instance */ +static void +_tpl_text_channel_pendingproc_connect_signals (TplTextChannel * self) +{ + GError *error = NULL; + TpChannel *channel = NULL; + + channel = tpl_channel_get_channel (tpl_text_channel_get_tpl_channel (self)); + + tp_cli_channel_type_text_connect_to_received (channel, + _channel_on_received_signal_cb, + self, NULL, NULL, &error); + if (error != NULL) + { + g_error ("received signal connect: %s\n", error->message); + g_clear_error (&error); + g_error_free (error); + error = NULL; + } + + tp_cli_channel_type_text_connect_to_sent (channel, + _channel_on_sent_signal_cb, self, + NULL, NULL, &error); + if (error != NULL) + { + g_error ("sent signal connect: %s\n", error->message); + g_clear_error (&error); + g_error_free (error); + error = NULL; + } + + tp_cli_channel_type_text_connect_to_send_error (channel, + _channel_on_send_error_cb, + self, NULL, NULL, &error); + if (error != NULL) + { + g_error ("send error signal connect: %s\n", error->message); + g_clear_error (&error); + g_error_free (error); + error = NULL; + } + + tp_cli_channel_type_text_connect_to_lost_message (channel, + _channel_on_lost_message_cb, + self, NULL, NULL, &error); + if (error != NULL) + { + g_error ("lost message signal connect: %s\n", error->message); + g_clear_error (&error); + g_error_free (error); + error = NULL; + } + + tp_cli_channel_connect_to_closed (channel, _channel_on_closed_cb, + self, NULL, NULL, &error); + if (error != NULL) + { + g_error ("channel closed signal connect: %s\n", error->message); + g_clear_error (&error); + g_error_free (error); + error = NULL; + } + + // TODO connect to TpContacts' notify::presence-type + context_continue (self); +} + +static void +_tpl_text_channel_get_chatroom_cb (TpConnection * proxy, + const gchar ** out_Identifiers, + const GError * error, + gpointer user_data, GObject * weak_object) +{ + TplTextChannel *tpl_text = TPL_TEXT_CHANNEL (user_data); + + if (error != NULL) + { + g_error ("retrieving chatroom identifier: %s\n", error->message); + } + + tpl_text_channel_set_chatroom_id (tpl_text, *out_Identifiers); + + context_continue (tpl_text); +} + +static void +_tpl_text_channel_pendingproc_get_chatroom_id (TplTextChannel * ctx) +{ + TplChannel *tpl_chan = tpl_text_channel_get_tpl_channel (ctx); + TpConnection *connection = tpl_channel_get_connection (tpl_chan); + TpHandle room_handle; + GArray *handles; + + handles = g_array_new (FALSE, FALSE, sizeof (TpHandle)); + room_handle = tp_channel_get_handle (tpl_channel_get_channel (tpl_chan), + NULL); + g_array_append_val (handles, room_handle); + + tpl_text_channel_set_chatroom (ctx, TRUE); + tp_cli_connection_call_inspect_handles (connection, + -1, TP_HANDLE_TYPE_ROOM, handles, + _tpl_text_channel_get_chatroom_cb, + ctx, NULL, NULL); + + g_array_unref (handles); +} + + +/* retrieve contacts (me and remote buddy/chatroom) and set TplTextChannel + * members */ + + +// used by _get_my_contact and _get_remote_contact +static void +_tpl_text_channel_get_contact_cb (TpConnection * connection, + guint n_contacts, + TpContact * const *contacts, + guint n_failed, + const TpHandle * failed, + const GError * error, + gpointer user_data, GObject * weak_object) +{ + TplTextChannel *tpl_text = TPL_TEXT_CHANNEL (user_data); + + g_assert_cmpuint (n_failed, ==, 0); + g_assert_cmpuint (n_contacts, ==, 1); + g_assert_cmpuint (tpl_text->selector, <=, TP_CONTACT_REMOTE); + + if (n_failed > 0) + { + g_error ("error resolving self handle for connection %s.\n" + "Aborting channel %s observation\n", + tpl_channel_get_connection_path + (tpl_text_channel_get_tpl_channel (tpl_text)), + tpl_channel_get_channel_path (tpl_text_channel_get_tpl_channel + (tpl_text))); + tpl_channel_unregister_from_observer (tpl_text_channel_get_tpl_channel + (tpl_text)); + return; + } + + switch (tpl_text->selector) + { + case TP_CONTACT_MYSELF: + tpl_text_channel_set_my_contact (tpl_text, *contacts); + break; + case TP_CONTACT_REMOTE: + tpl_text_channel_set_remote_contact (tpl_text, *contacts); + break; + default: + g_error ("retrieving TpContacts: passing invalid value " + "for selector: %d\n" + "Aborting channel %s observation\n", + tpl_text->selector, + tpl_channel_get_channel_path (tpl_text_channel_get_tpl_channel + (tpl_text))); + tpl_channel_unregister_from_observer (tpl_text_channel_get_tpl_channel + (tpl_text)); + return; + } + + context_continue (tpl_text); +} + + +static void +_tpl_text_channel_pendingproc_get_remote_contact (TplTextChannel * ctx) +{ + TplChannel *tpl_chan = tpl_text_channel_get_tpl_channel (ctx); + TpHandleType remote_handle_type; + TpHandle remote_handle; + + remote_handle = tp_channel_get_handle (tpl_channel_get_channel (tpl_chan), + &remote_handle_type); + + ctx->selector = TP_CONTACT_REMOTE; + tp_connection_get_contacts_by_handle (tpl_channel_get_connection (tpl_chan), + 1, &remote_handle, + TP_CONTACT_FEATURES_LEN, features, + _tpl_text_channel_get_contact_cb, + ctx, NULL, NULL); +} + +static void +_tpl_text_channel_pendingproc_get_my_contact (TplTextChannel * ctx) +{ + TplChannel *tpl_chan = tpl_text_channel_get_tpl_channel (ctx); + TpHandle my_handle = + tp_connection_get_self_handle (tpl_channel_get_connection (tpl_chan)); + + ctx->selector = TP_CONTACT_MYSELF; + tp_connection_get_contacts_by_handle (tpl_channel_get_connection (tpl_chan), + 1, &my_handle, + TP_CONTACT_FEATURES_LEN, features, + _tpl_text_channel_get_contact_cb, + ctx, NULL, NULL); +} + +/* end of async Callbacks */ + + +G_DEFINE_TYPE (TplTextChannel, tpl_text_channel, G_TYPE_OBJECT) + static void tpl_text_channel_dispose (GObject * obj) +{ + TplTextChannel *self = TPL_TEXT_CHANNEL (obj); + + g_debug ("TplTextChannel: disposing\n"); + + tpl_object_unref_if_not_null (self->tpl_channel); + self->tpl_channel = NULL; + tpl_object_unref_if_not_null (self->my_contact); + self->my_contact = NULL; + tpl_object_unref_if_not_null (self->remote_contact); + self->remote_contact = NULL; + g_queue_free (self->chain); + self->chain = NULL; + + G_OBJECT_CLASS (tpl_text_channel_parent_class)->dispose (obj); + + g_debug ("TplTextChannel: disposed\n"); +} + +static void +tpl_text_channel_finalize (GObject * obj) +{ + TplTextChannel *self = TPL_TEXT_CHANNEL (obj); + + g_debug ("TplTextChannel: finalizing\n"); + + g_free ((gchar *) self->chatroom_id); + G_OBJECT_CLASS (tpl_text_channel_parent_class)->finalize (obj); + + g_debug ("TplTextChannel: finalized\n"); +} + +static void +tpl_text_channel_class_init (TplTextChannelClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = tpl_text_channel_dispose; + object_class->finalize = tpl_text_channel_finalize; +} + + +static void +tpl_text_channel_init (TplTextChannel * self) +{ + /* Init TplTextChannel's members to zero/NULL */ +#undef TPL_SET_NULL + tpl_text_channel_set_tpl_channel (self, NULL); + tpl_text_channel_set_my_contact (self, NULL); + tpl_text_channel_set_remote_contact (self, NULL); + tpl_text_channel_set_chatroom_id (self, NULL); + tpl_text_channel_set_chatroom (self, FALSE); +} + +TplTextChannel * +tpl_text_channel_new (TplChannel * tpl_channel) +{ + TplTextChannel *ret = g_object_new (TPL_TYPE_TEXT_CHANNEL, NULL); + tpl_text_channel_set_tpl_channel (ret, tpl_channel); + + // here some post instance-initialization, the object needs + // to set some type's members and probably access (futurely) some + // props + TpHandleType remote_handle_type; + tp_channel_get_handle (tpl_channel_get_channel (tpl_channel), + &remote_handle_type); + + ret->chain = g_queue_new (); + g_queue_push_tail (ret->chain, + _tpl_text_channel_pendingproc_get_my_contact); + + switch (remote_handle_type) + { + case TP_HANDLE_TYPE_CONTACT: + g_queue_push_tail (ret->chain, + _tpl_text_channel_pendingproc_get_remote_contact); + break; + case TP_HANDLE_TYPE_ROOM: + g_queue_push_tail (ret->chain, + _tpl_text_channel_pendingproc_get_chatroom_id); + break; + + /* follows unhandled TpHandleType */ + case TP_HANDLE_TYPE_NONE: + g_warning ("remote handle: TP_HANDLE_TYPE_NONE: " + "un-handled. It's probably OK.\n"); + break; + case TP_HANDLE_TYPE_LIST: + g_warning ("remote handle: TP_HANDLE_TYPE_LIST: \n" + "un-handled. It's probably OK.\n"); + break; + case TP_HANDLE_TYPE_GROUP: + g_warning ("remote handle: TP_HANDLE_TYPE_GROUP: " + "un-handled. It's probably OK.\n"); + break; + default: + g_error ("remote handle type unknown %d.\n", remote_handle_type); + break; + } + + g_queue_push_tail (ret->chain, + _tpl_text_channel_pendingproc_connect_signals); + + // start the chain consuming + context_continue (ret); + return ret; +} + + +TplChannel * +tpl_text_channel_get_tpl_channel (TplTextChannel * self) +{ + return self->tpl_channel; +} + +TpContact * +tpl_text_channel_get_remote_contact (TplTextChannel * self) +{ + return self->remote_contact; +} + +TpContact * +tpl_text_channel_get_my_contact (TplTextChannel * self) +{ + return self->my_contact; +} + +gboolean +tpl_text_channel_is_chatroom (TplTextChannel * self) +{ + return self->chatroom; +} + +const gchar * +tpl_text_channel_get_chatroom_id (TplTextChannel * self) +{ + return self->chatroom_id; +} + +void +tpl_text_channel_set_tpl_channel (TplTextChannel * self, TplChannel * data) +{ + tpl_object_unref_if_not_null (self->tpl_channel); + self->tpl_channel = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_text_channel_set_remote_contact (TplTextChannel * self, TpContact * data) +{ + tpl_object_unref_if_not_null (self->remote_contact); + self->remote_contact = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_text_channel_set_my_contact (TplTextChannel * self, TpContact * data) +{ + tpl_object_unref_if_not_null (self->my_contact); + self->my_contact = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_text_channel_set_chatroom (TplTextChannel * self, gboolean data) +{ + self->chatroom = data; +} + +void +tpl_text_channel_set_chatroom_id (TplTextChannel * self, const gchar * data) +{ + g_free ((gchar *) self->chatroom_id); + self->chatroom_id = g_strdup (data); +} diff --git a/telepathy-logger/channel-text.h b/telepathy-logger/channel-text.h new file mode 100644 index 0000000..069f077 --- /dev/null +++ b/telepathy-logger/channel-text.h @@ -0,0 +1,90 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#ifndef __TPL_TEXT_CHANNEL_H__ +#define __TPL_TEXT_CHANNEL_H__ + +/* + * http://telepathy.freedesktop.org/doc/telepathy-glib/telepathy-glib-channel-text.html#tp-cli-channel-type-text-connect-to-received + */ + +#include <glib-object.h> +#include <telepathy-glib/account.h> +#include <telepathy-glib/channel.h> +#include <telepathy-glib/connection.h> +#include <telepathy-glib/contact.h> +#include <telepathy-glib/svc-client.h> + +#include <telepathy-logger/channel.h> +#include <telepathy-logger/observer.h> +#include <telepathy-logger/utils.h> + +G_BEGIN_DECLS +#define TPL_TYPE_TEXT_CHANNEL (tpl_text_channel_get_type ()) +#define TPL_TEXT_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPL_TYPE_TEXT_CHANNEL, TplTextChannel)) +#define TPL_TEXT_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TPL_TYPE_TEXT_CHANNEL, TplTextChannelClass)) +#define TPL_IS_TEXT_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPL_TYPE_TEXT_CHANNEL)) +#define TPL_IS_TEXT_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TPL_TYPE_TEXT_CHANNEL)) +#define TPL_TEXT_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TPL_TYPE_TEXT_CHANNEL, TplTextChannelClass)) + typedef struct +{ + GObject parent; + + /* private */ + TplChannel *tpl_channel; + gboolean chatroom; + TpContact *my_contact; + TpContact *remote_contact; // only set if chatroom==FALSE + const gchar *chatroom_id; // only set if chatroom==TRUE + + GQueue *chain; // queue of TplPendingProc + + // only used as metadata in CB data passing + guint selector; +} TplTextChannel; + +typedef struct +{ + GObjectClass parent_class; +} TplTextChannelClass; + +GType tpl_text_channel_get_type (void); + +TplTextChannel *tpl_text_channel_new (TplChannel * tpl_channel); + +TplChannel *tpl_text_channel_get_tpl_channel (TplTextChannel * self); +TpContact *tpl_text_channel_get_remote_contact (TplTextChannel * self); +TpContact *tpl_text_channel_get_my_contact (TplTextChannel * self); +gboolean tpl_text_channel_is_chatroom (TplTextChannel * self); +const gchar *tpl_text_channel_get_chatroom_id (TplTextChannel * self); + +void tpl_text_channel_set_tpl_channel (TplTextChannel * self, + TplChannel * tpl_chan); +void tpl_text_channel_set_remote_contact (TplTextChannel * self, + TpContact * data); +void tpl_text_channel_set_my_contact (TplTextChannel * self, + TpContact * data); +void tpl_text_channel_set_chatroom (TplTextChannel * self, gboolean data); +void tpl_text_channel_set_chatroom_id (TplTextChannel * self, + const gchar * data); + +G_END_DECLS +#endif // __TPL_TEXT_CHANNEL_H__ diff --git a/telepathy-logger/channel.c b/telepathy-logger/channel.c new file mode 100644 index 0000000..7a75449 --- /dev/null +++ b/telepathy-logger/channel.c @@ -0,0 +1,334 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#include "channel.h" + +#include <glib.h> + +#include <telepathy-logger/channel-text.h> +#include <telepathy-logger/observer.h> + +G_DEFINE_TYPE (TplChannel, tpl_channel, G_TYPE_OBJECT) + static void tpl_channel_dispose (GObject * obj) +{ + TplChannel *self = TPL_CHANNEL (obj); + + g_debug ("TplChannel dispose start\n"); + + tpl_object_unref_if_not_null (self->channel); + self->channel = NULL; + + if (self->channel_properties != NULL) + g_hash_table_unref (self->channel_properties); + self->channel_properties = NULL; + + tpl_object_unref_if_not_null (self->account); + self->account = NULL; + + tpl_object_unref_if_not_null (self->connection); + self->connection = NULL; + + tpl_object_unref_if_not_null (self->observer); + self->observer = NULL; + + G_OBJECT_CLASS (tpl_channel_parent_class)->dispose (obj); + g_debug ("TplChannel dispose end\n"); +} + +static void +tpl_channel_finalize (GObject * obj) +{ + TplChannel *self = TPL_CHANNEL (obj); + g_free ((gchar *) self->channel_path); + g_free ((gchar *) self->channel_type); + g_free ((gchar *) self->account_path); + g_free ((gchar *) self->connection_path); + + G_OBJECT_CLASS (tpl_channel_parent_class)->finalize (obj); + + g_debug ("TplChannel instance finalized\n"); +} + +static void +tpl_channel_class_init (TplChannelClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = tpl_channel_dispose; + object_class->finalize = tpl_channel_finalize; +} + + +static void +tpl_channel_init (TplChannel * self) +{ + /* Init TplChannel's members to zero/NULL */ +/* TODO remove the comment +#define TPL_SET_NULL(x) tpl_channel_set_##x(self, NULL) + TPL_SET_NULL(channel); + TPL_SET_NULL(channel_path); + TPL_SET_NULL(channel_type); + TPL_SET_NULL(channel_properties); + TPL_SET_NULL(account); + TPL_SET_NULL(account_path); + TPL_SET_NULL(connection); + TPL_SET_NULL(connection_path); + TPL_SET_NULL(observer); +#undef TPL_SET_NULL +*/ +} + + +TplChannel * +tpl_channel_new (TpSvcClientObserver * observer) +{ + g_return_val_if_fail (TP_IS_SVC_CLIENT_OBSERVER (observer), NULL); + + TplChannel *ret = g_object_new (TPL_TYPE_CHANNEL, NULL); + tpl_channel_set_observer (ret, observer); + return ret; +} + +TpSvcClientObserver * +tpl_channel_get_observer (TplChannel * self) +{ + g_return_val_if_fail (TPL_IS_CHANNEL (self), NULL); + return self->observer; +} + +TpAccount * +tpl_channel_get_account (TplChannel * self) +{ + g_return_val_if_fail (TPL_IS_CHANNEL (self), NULL); + return self->account; +} + +const gchar * +tpl_channel_get_account_path (TplChannel * self) +{ + g_return_val_if_fail (TPL_IS_CHANNEL (self), NULL); + return self->account_path; +} + +TpConnection * +tpl_channel_get_connection (TplChannel * self) +{ + g_return_val_if_fail (TPL_IS_CHANNEL (self), NULL); + return self->connection; +} + +const gchar * +tpl_channel_get_connection_path (TplChannel * self) +{ + g_return_val_if_fail (TPL_IS_CHANNEL (self), NULL); + return self->connection_path; +} + +TpChannel * +tpl_channel_get_channel (TplChannel * self) +{ + g_return_val_if_fail (TPL_IS_CHANNEL (self), NULL); + return self->channel; +} + +const gchar * +tpl_channel_get_channel_path (TplChannel * self) +{ + g_return_val_if_fail (TPL_IS_CHANNEL (self), NULL); + return self->channel_path; +} + +const gchar * +tpl_channel_get_channel_type (TplChannel * self) +{ + g_return_val_if_fail (TPL_IS_CHANNEL (self), NULL); + return self->channel_type; +} + +GHashTable * +tpl_channel_get_channel_properties (TplChannel * self) +{ + g_return_val_if_fail (TPL_IS_CHANNEL (self), NULL); + return self->channel_properties; +} + + + +void +tpl_channel_set_observer (TplChannel * self, TpSvcClientObserver * data) +{ + g_return_if_fail (TPL_IS_CHANNEL (self)); + g_return_if_fail (TP_IS_SVC_CLIENT_OBSERVER (data) || data == NULL); + + tpl_object_unref_if_not_null (self->observer); + self->observer = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_channel_set_account (TplChannel * self, TpAccount * data) +{ + g_return_if_fail (TPL_IS_CHANNEL (self)); + g_return_if_fail (TP_IS_ACCOUNT (data) || data == NULL); + + tpl_object_unref_if_not_null (self->account); + self->account = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_channel_set_account_path (TplChannel * self, const gchar * data) +{ + g_return_if_fail (TPL_IS_CHANNEL (self)); + // TODO check validity of data + + g_free ((gchar *) self->account_path); + self->account_path = g_strdup (data); +} + +void +tpl_channel_set_connection (TplChannel * self, TpConnection * data) +{ + g_return_if_fail (TPL_IS_CHANNEL (self)); + g_return_if_fail (TP_IS_CONNECTION (data) || data == NULL); + + tpl_object_unref_if_not_null (self->connection); + self->connection = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_channel_set_connection_path (TplChannel * self, const gchar * data) +{ + g_return_if_fail (TPL_IS_CHANNEL (self)); + // TODO check validity of data + + g_free ((gchar *) self->connection_path); + self->connection_path = g_strdup (data); +} + +void +tpl_channel_set_channel (TplChannel * self, TpChannel * data) +{ + g_return_if_fail (TPL_IS_CHANNEL (self)); + g_return_if_fail (TP_IS_CHANNEL (data) || data == NULL); + + tpl_object_unref_if_not_null (self->channel); + self->channel = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_channel_set_channel_path (TplChannel * self, const gchar * data) +{ + g_return_if_fail (TPL_IS_CHANNEL (self)); + // TODO check validity of data + + g_free ((gchar *) self->channel_path); + self->channel_path = g_strdup (data); +} + +void +tpl_channel_set_channel_type (TplChannel * self, const gchar * data) +{ + g_return_if_fail (TPL_IS_CHANNEL (self)); + // TODO check validity of data + + g_free ((gchar *) self->channel_type); + self->channel_type = g_strdup (data); +} + +void +tpl_channel_set_channel_properties (TplChannel * self, GHashTable * data) +{ + g_return_if_fail (TPL_IS_CHANNEL (self)); + // TODO check validity of data + + if (self->channel_properties != NULL) + g_hash_table_unref (self->channel_properties); + self->channel_properties = data; + if (data != NULL) + g_hash_table_ref (data); +} + + +gboolean +tpl_channel_register_to_observer (TplChannel * self) +{ + TplObserver *obs = TPL_OBSERVER (tpl_channel_get_observer (self)); + GHashTable *glob_map = tpl_observer_get_channel_map (obs); + gchar *key; + + g_return_val_if_fail (TPL_IS_CHANNEL (self), FALSE); + g_return_val_if_fail (glob_map != NULL, FALSE); + + key = g_strdup (tpl_channel_get_channel_path (self)); + + if (g_hash_table_lookup (glob_map, key) != NULL) + { + g_error ("Channel path found, replacing %s\n", key); + g_hash_table_remove (glob_map, key); + } + else + { + g_debug ("Channel path not found, registering %s\n", key); + } + + // Instantiate and delegate channel handling to the right object + if (0 == g_strcmp0 (TP_IFACE_CHAN_TEXT, + tpl_channel_get_channel_type (self))) + { + // when removed, automatically frees the Key and unrefs + // its Value + TplTextChannel *chan_text = tpl_text_channel_new (self); + g_hash_table_insert (glob_map, key, chan_text); + } + else + { + g_warning ("%s: channel type not handled by this logger", + tpl_channel_get_channel_type (self)); + } + + g_object_unref (self); + + return TRUE; +} + +gboolean +tpl_channel_unregister_from_observer (TplChannel * self) +{ + TplObserver *obs = TPL_OBSERVER (tpl_channel_get_observer (self)); + GHashTable *glob_map = tpl_observer_get_channel_map (obs); + const gchar *key; + + g_assert (TPL_IS_CHANNEL (self)); + g_assert (glob_map != NULL); + g_return_val_if_fail (TPL_IS_CHANNEL (self), FALSE); + g_return_val_if_fail (glob_map != NULL, FALSE); + + key = tpl_channel_get_channel_path (self); + g_debug ("Unregistering channel path %s\n", key); + + // this will destroy the associated value object: at this point + // the hash table reference should be the only one for the + // value's object + return g_hash_table_remove (glob_map, key); +} diff --git a/telepathy-logger/channel.h b/telepathy-logger/channel.h new file mode 100644 index 0000000..9b47ade --- /dev/null +++ b/telepathy-logger/channel.h @@ -0,0 +1,99 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#ifndef __TPL_CHANNEL_H__ +#define __TPL_CHANNEL_H__ + +#include <glib.h> +#include <glib-object.h> +#include <telepathy-glib/account.h> +#include <telepathy-glib/connection.h> +#include <telepathy-glib/channel.h> +#include <telepathy-glib/svc-client.h> + +#include <telepathy-logger/observer.h> +#include <telepathy-logger/utils.h> + +G_BEGIN_DECLS +#define TPL_TYPE_CHANNEL (tpl_channel_get_type ()) +#define TPL_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPL_TYPE_CHANNEL, TplChannel)) +#define TPL_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TPL_TYPE_CHANNEL, TplChannelClass)) +#define TPL_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPL_TYPE_CHANNEL)) +#define TPL_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TPL_TYPE_CHANNEL)) +#define TPL_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TPL_TYPE_CHANNEL, TplChannelClass)) +// TODO test the following macros +//#define TPL_CHANNEL_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), TPL_TYPE_CHANNEL, TplChannelClass)) + typedef struct +{ + GObject parent; + + /* private */ + TpChannel *channel; + const gchar *channel_path; + const gchar *channel_type; + GHashTable *channel_properties; + + TpAccount *account; + const gchar *account_path; + TpConnection *connection; + const gchar *connection_path; + + TpSvcClientObserver *observer; +} TplChannel; + +typedef struct +{ + GObjectClass parent_class; +} TplChannelClass; + + +GType tpl_channel_get_type (void); + +TplChannel *tpl_channel_new (TpSvcClientObserver * observer); +void tpl_channel_free (TplChannel * tpl_chan); + +TpSvcClientObserver *tpl_channel_get_observer (TplChannel * self); +TpAccount *tpl_channel_get_account (TplChannel * self); +const gchar *tpl_channel_get_account_path (TplChannel * self); +TpConnection *tpl_channel_get_connection (TplChannel * self); +const gchar *tpl_channel_get_connection_path (TplChannel * self); +TpChannel *tpl_channel_get_channel (TplChannel * self); +const gchar *tpl_channel_get_channel_path (TplChannel * self); +const gchar *tpl_channel_get_channel_type (TplChannel * self); +GHashTable *tpl_channel_get_channel_properties (TplChannel * self); + + +void tpl_channel_set_observer (TplChannel * self, TpSvcClientObserver * data); +void tpl_channel_set_account (TplChannel * self, TpAccount * data); +void tpl_channel_set_account_path (TplChannel * self, const gchar * data); +void tpl_channel_set_connection (TplChannel * self, TpConnection * data); +void tpl_channel_set_connection_path (TplChannel * self, const gchar * data); +void tpl_channel_set_channel (TplChannel * self, TpChannel * data); +void tpl_channel_set_channel_path (TplChannel * self, const gchar * data); +void tpl_channel_set_channel_type (TplChannel * self, const gchar * data); +void tpl_channel_set_channel_properties (TplChannel * self, + GHashTable * data); + +gboolean tpl_channel_register_to_observer (TplChannel * self); +gboolean tpl_channel_unregister_from_observer (TplChannel * self); + +G_END_DECLS +#endif // __TPL_CHANNEL_H__ diff --git a/telepathy-logger/conf.c b/telepathy-logger/conf.c new file mode 100644 index 0000000..a38dc0d --- /dev/null +++ b/telepathy-logger/conf.c @@ -0,0 +1,93 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + /* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#include "conf.h" + +#include <glib.h> + +#define DEBUG(...) + +G_DEFINE_TYPE (TplConf, tpl_conf, G_TYPE_OBJECT) + static void tpl_conf_finalize (GObject * obj) +{ + + G_OBJECT_CLASS (tpl_conf_parent_class)->finalize (obj); +} + +static void +tpl_conf_dispose (GObject * obj) +{ + TplConf *self = TPL_CONF (obj); + + tpl_object_unref_if_not_null (tpl_conf_get_entry (self)); + self->entry.generic = NULL; + + G_OBJECT_CLASS (tpl_conf_parent_class)->dispose (obj); +} + + +static void +tpl_conf_class_init (TplConfClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = tpl_conf_finalize; + object_class->dispose = tpl_conf_dispose; +} + +static void +tpl_conf_init (TplConf * self) +{ + self->client = gconf_client_get_default (); +} + + +TplConf * +tpl_conf_new (void) +{ + return g_object_new (TPL_TYPE_CONF, NULL); +} + +#define GCONF_KEY_DISABLING_GLOBAL "/apps/telepathy-logger/disabling/global"; +#define GCONF_KEY_DISABLING_ACCOUNT_LIST "/apps/telepathy-logger/disabling/accounts/blocklist" +gboolean * +tpl_conf_is_enabled_globally (TplConf * self) +{ + GConfValue *val = + gconf_client_get (self->client, GCONF_KEY_DISABLING_GLOBAL, &error); + return !gconf_value_get_bool (val); +} + +void +tpl_conf_enable_globally (TplConf * self) +{ + GConfValue *val = gconf_value_new (GCONF_VALUE_BOOL); + gconf_value_set_bool (val, FALSE); // not disabling + gconf_client_set (self->client, GCONF_KEY_DISABLING_GLOBAL, val); +} + + +void +tpl_conf_enable_globally (TplConf * self) +{ + GConfValue *val = gconf_value_new (GCONF_VALUE_BOOL); + gconf_value_set_bool (val, TRUE); // disabling + gconf_client_set (self->client, GCONF_KEY_DISABLING_GLOBAL, val); +} diff --git a/telepathy-logger/contact.c b/telepathy-logger/contact.c new file mode 100644 index 0000000..e79e34d --- /dev/null +++ b/telepathy-logger/contact.c @@ -0,0 +1,216 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#include "contact.h" + +#include <telepathy-glib/account.h> + +#include <telepathy-logger/utils.h> + +G_DEFINE_TYPE (TplContact, tpl_contact, G_TYPE_OBJECT) + static void tpl_contact_finalize (GObject * obj); + static void tpl_contact_dispose (GObject * obj); + + static void tpl_contact_class_init (TplContactClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = tpl_contact_finalize; + object_class->dispose = tpl_contact_dispose; +} + +static void +tpl_contact_init (TplContact * self) +{ +} + +static void +tpl_contact_finalize (GObject * obj) +{ + TplContact *self = TPL_CONTACT (obj); + + g_free (self->alias); + self->alias = NULL; + g_free (self->identifier); + self->identifier = NULL; + g_free (self->presence_status); + self->presence_status = NULL; + g_free (self->presence_message); + self->presence_message = NULL; + + G_OBJECT_CLASS (tpl_contact_parent_class)->finalize (obj); +} + +static void +tpl_contact_dispose (GObject * obj) +{ + TplContact *self = TPL_CONTACT (obj); + + tpl_object_unref_if_not_null (self->contact); + self->contact = NULL; + + G_OBJECT_CLASS (tpl_contact_parent_class)->dispose (obj); +} + + + +TplContact * +tpl_contact_from_tp_contact (TpContact * contact) +{ + TplContact *ret; + + ret = tpl_contact_new (); + tpl_contact_set_contact (ret, contact); + tpl_contact_set_identifier (ret, + (gchar *) tp_contact_get_identifier (contact)); + tpl_contact_set_alias (ret, (gchar *) tp_contact_get_alias (contact)); + tpl_contact_set_presence_status (ret, + (gchar *) + tp_contact_get_presence_status (contact)); + tpl_contact_set_presence_message (ret, + (gchar *) + tp_contact_get_presence_message + (contact)); + + return ret; +} + +TplContact * +tpl_contact_new (void) +{ + return g_object_new (TPL_TYPE_CONTACT, NULL); +} + +TpContact * +tpl_contact_get_contact (TplContact * self) +{ + g_return_val_if_fail (TPL_IS_CONTACT (self), NULL); + return self->contact; +} + +gchar * +tpl_contact_get_alias (TplContact * self) +{ + g_return_val_if_fail (TPL_IS_CONTACT (self), NULL); + return self->alias; +} + +gchar * +tpl_contact_get_identifier (TplContact * self) +{ + g_return_val_if_fail (TPL_IS_CONTACT (self), NULL); + return self->identifier; +} + +gchar * +tpl_contact_get_presence_status (TplContact * self) +{ + g_return_val_if_fail (TPL_IS_CONTACT (self), NULL); + return self->presence_status; +} + +gchar * +tpl_contact_get_presence_message (TplContact * self) +{ + g_return_val_if_fail (TPL_IS_CONTACT (self), NULL); + return self->presence_message; +} + +TplContactType +tpl_contact_get_contact_type (TplContact * self) +{ + g_return_val_if_fail (TPL_IS_CONTACT (self), TPL_CONTACT_UNKNOWN); + return self->contact_type; +} + +TpAccount * +tpl_contact_get_account (TplContact * self) +{ + g_return_val_if_fail (TPL_IS_CONTACT (self), NULL); + return self->account; +} + + +void +tpl_contact_set_contact (TplContact * self, TpContact * data) +{ + g_return_if_fail (TPL_IS_CONTACT (self)); + g_return_if_fail (TP_IS_CONTACT (data) || data == NULL); + + tpl_object_unref_if_not_null (self->contact); + self->contact = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_contact_set_account (TplContact * self, TpAccount * data) +{ + g_return_if_fail (TPL_IS_CONTACT (self)); + g_return_if_fail (TP_IS_ACCOUNT (data) || data == NULL); + + tpl_object_unref_if_not_null (self->account); + self->account = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_contact_set_alias (TplContact * self, gchar * data) +{ + g_return_if_fail (TPL_IS_CONTACT (self)); + + g_free (self->alias); + self->alias = g_strdup (data); +} + +void +tpl_contact_set_identifier (TplContact * self, gchar * data) +{ + g_return_if_fail (TPL_IS_CONTACT (self)); + + g_free (self->identifier); + self->identifier = g_strdup (data); +} + +void +tpl_contact_set_presence_status (TplContact * self, gchar * data) +{ + g_return_if_fail (TPL_IS_CONTACT (self)); + + g_free (self->presence_status); + self->presence_status = g_strdup (data); +} + +void +tpl_contact_set_presence_message (TplContact * self, gchar * data) +{ + g_return_if_fail (TPL_IS_CONTACT (self)); + + g_free (self->presence_message); + self->presence_message = g_strdup (data); +} + + +void +tpl_contact_set_contact_type (TplContact * self, TplContactType data) +{ + g_return_if_fail (TPL_IS_CONTACT (self)); + + self->contact_type = data; +} diff --git a/telepathy-logger/contact.h b/telepathy-logger/contact.h new file mode 100644 index 0000000..6fca0ae --- /dev/null +++ b/telepathy-logger/contact.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#ifndef __TPL_CONTACT_H__ +#define __TPL_CONTACT_H__ + +#include <glib-object.h> +#include <telepathy-glib/contact.h> +#include <telepathy-glib/account.h> + +G_BEGIN_DECLS +#define TPL_TYPE_CONTACT (tpl_contact_get_type ()) +#define TPL_CONTACT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPL_TYPE_CONTACT, TplContact)) +#define TPL_CONTACT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TPL_TYPE_CONTACT, TplContactClass)) +#define TPL_IS_CONTACT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPL_TYPE_CONTACT)) +#define TPL_IS_CONTACT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TPL_TYPE_CONTACT)) +#define TPL_CONTACT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TPL_TYPE_CONTACT, TplContactClass)) + typedef enum +{ + TPL_CONTACT_UNKNOWN, + TPL_CONTACT_USER, + TPL_CONTACT_GROUP +} TplContactType; + +typedef struct +{ + GObject parent; + + /* Private */ + TpContact *contact; + TplContactType contact_type; + gchar *alias; + gchar *identifier; + gchar *presence_status; + gchar *presence_message; + + TpAccount *account; +} TplContact; + + +typedef struct +{ + GObjectClass parent_class; +} TplContactClass; + + +GType tpl_contact_get_type (void); + +TplContact *tpl_contact_from_tp_contact (TpContact * contact); + +TplContact *tpl_contact_new (void); + +TpContact *tpl_contact_get_contact (TplContact * self); + +gchar *tpl_contact_get_alias (TplContact * self); + +gchar *tpl_contact_get_identifier (TplContact * self); + +gchar *tpl_contact_get_presence_status (TplContact * self); + +gchar *tpl_contact_get_presence_message (TplContact * self); + +TplContactType tpl_contact_get_contact_type (TplContact * self); + +TpAccount *tpl_contact_get_account (TplContact * self); + +void tpl_contact_set_contact (TplContact * self, TpContact * data); + +void tpl_contact_set_account (TplContact * self, TpAccount * data); + +void tpl_contact_set_alias (TplContact * self, gchar * data); + +void tpl_contact_set_identifier (TplContact * self, gchar * data); + +void tpl_contact_set_presence_status (TplContact * self, gchar * data); + +void tpl_contact_set_presence_message (TplContact * self, gchar * data); + +void tpl_contact_set_contact_type (TplContact * self, TplContactType data); + +G_END_DECLS +#endif // __TPL_CONTACT_H__ diff --git a/telepathy-logger/datetime.c b/telepathy-logger/datetime.c new file mode 100644 index 0000000..41f32b3 --- /dev/null +++ b/telepathy-logger/datetime.c @@ -0,0 +1,186 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Richard Hult <richard@imendio.com> + */ + +#include "datetime.h" + +#include <glib/gi18n.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + + +/* Note: TplTime is always in UTC. */ + +time_t +tpl_time_get_current (void) +{ + return time (NULL); +} + +time_t +tpl_time_get_local_time (struct tm * tm) +{ + const gchar *tz; + time_t t; + + tz = g_getenv ("TZ"); + g_setenv ("TZ", "", TRUE); + + tzset (); + + t = mktime (tm); + + if (tz) + { + g_setenv ("TZ", tz, TRUE); + } + else + { + g_unsetenv ("TZ"); + } + + tzset (); + + return t; +} + +/* The format is: "20021209T23:51:30" and is in UTC. 0 is returned on + * failure. The alternative format "20021209" is also accepted. + */ +time_t +tpl_time_parse (const gchar * str) +{ + struct tm tm; + gint year, month; + gint n_parsed; + + memset (&tm, 0, sizeof (struct tm)); + + n_parsed = sscanf (str, "%4d%2d%2dT%2d:%2d:%2d", + &year, &month, &tm.tm_mday, &tm.tm_hour, + &tm.tm_min, &tm.tm_sec); + if (n_parsed != 3 && n_parsed != 6) + { + return 0; + } + + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_isdst = -1; + + return tpl_time_get_local_time (&tm); +} + +/* Converts the UTC timestamp to a string, also in UTC. Returns NULL on failure. */ +gchar * +tpl_time_to_string_utc (time_t t, const gchar * format) +{ + gchar stamp[128]; + struct tm *tm; + + g_return_val_if_fail (format != NULL, NULL); + + tm = gmtime (&t); + if (strftime (stamp, sizeof (stamp), format, tm) == 0) + { + return NULL; + } + + return g_strdup (stamp); +} + +/* Converts the UTC timestamp to a string, in local time. Returns NULL on failure. */ +gchar * +tpl_time_to_string_local (time_t t, const gchar * format) +{ + gchar stamp[128]; + struct tm *tm; + + g_return_val_if_fail (format != NULL, NULL); + + tm = localtime (&t); + if (strftime (stamp, sizeof (stamp), format, tm) == 0) + { + return NULL; + } + + return g_strdup (stamp); +} + +gchar * +tpl_time_to_string_relative (time_t then) +{ + time_t now; + gint seconds; + + now = time (NULL); + seconds = now - then; + + if (seconds > 0) + { + if (seconds < 60) + { + return g_strdup_printf (ngettext ("%d second ago", + "%d seconds ago", seconds), + seconds); + } + else if (seconds < (60 * 60)) + { + seconds /= 60; + return g_strdup_printf (ngettext ("%d minute ago", + "%d minutes ago", seconds), + seconds); + } + else if (seconds < (60 * 60 * 24)) + { + seconds /= 60 * 60; + return g_strdup_printf (ngettext ("%d hour ago", + "%d hours ago", seconds), + seconds); + } + else if (seconds < (60 * 60 * 24 * 7)) + { + seconds /= 60 * 60 * 24; + return g_strdup_printf (ngettext ("%d day ago", + "%d days ago", seconds), seconds); + } + else if (seconds < (60 * 60 * 24 * 30)) + { + seconds /= 60 * 60 * 24 * 7; + return g_strdup_printf (ngettext ("%d week ago", + "%d weeks ago", seconds), + seconds); + } + else + { + seconds /= 60 * 60 * 24 * 30; + return g_strdup_printf (ngettext ("%d month ago", + "%d months ago", seconds), + seconds); + } + } + else + { + return g_strdup (_("in the future")); + } +} diff --git a/telepathy-logger/datetime.h b/telepathy-logger/datetime.h new file mode 100644 index 0000000..2e4ae2a --- /dev/null +++ b/telepathy-logger/datetime.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004 Imendio AB + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef __TPL_TIME_H__ +#define __TPL_TIME_H__ + +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#include <time.h> + +#include <glib.h> + +G_BEGIN_DECLS +#define TPL_TIME_FORMAT_DISPLAY_SHORT "%H:%M" +#define TPL_TIME_FORMAT_DISPLAY_LONG "%a %d %b %Y" + time_t tpl_time_get_current (void); +time_t tpl_time_get_local_time (struct tm *tm); +time_t tpl_time_parse (const gchar * str); +gchar *tpl_time_to_string_utc (time_t t, const gchar * format); +gchar *tpl_time_to_string_local (time_t t, const gchar * format); +gchar *tpl_time_to_string_relative (time_t t); + +G_END_DECLS +#endif /* __TPL_TIME_H__ */ diff --git a/telepathy-logger/debug.h b/telepathy-logger/debug.h new file mode 100644 index 0000000..cb90ec5 --- /dev/null +++ b/telepathy-logger/debug.h @@ -0,0 +1,2 @@ + +#define DEBUG(...) diff --git a/telepathy-logger/libtelepathy-logger.pc.in b/telepathy-logger/libtelepathy-logger.pc.in new file mode 100644 index 0000000..c11a59f --- /dev/null +++ b/telepathy-logger/libtelepathy-logger.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/telepathy-logger + +Name: something +Description: Telepathy Logger library +Requires: telepathy-glib libxml-2.0 +Version: @VERSION@ +Libs: -L${libdir} -llibtelepathy-logger +Cflags: -I${includedir} diff --git a/telepathy-logger/log-entry-text.c b/telepathy-logger/log-entry-text.c new file mode 100644 index 0000000..0332f9d --- /dev/null +++ b/telepathy-logger/log-entry-text.c @@ -0,0 +1,292 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#include "log-entry-text.h" + +#include <telepathy-logger/channel.h> +#include <telepathy-logger/contact.h> +#include <telepathy-logger/utils.h> + +G_DEFINE_TYPE (TplLogEntryText, tpl_log_entry_text, G_TYPE_OBJECT) + static void tpl_log_entry_text_finalize (GObject * obj); + static void tpl_log_entry_text_dispose (GObject * obj); + + static void tpl_log_entry_text_class_init (TplLogEntryTextClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = tpl_log_entry_text_finalize; + object_class->dispose = tpl_log_entry_text_dispose; +} + +static void +tpl_log_entry_text_init (TplLogEntryText * self) +{ +#define TPL_SET_NULL(x) tpl_log_entry_text_set_##x(self, NULL) + TPL_SET_NULL (tpl_text_channel); + TPL_SET_NULL (sender); + TPL_SET_NULL (receiver); + TPL_SET_NULL (message); + TPL_SET_NULL (chat_id); +#undef TPL_SET_NULL +} + +static void +tpl_log_entry_text_dispose (GObject * obj) +{ + TplLogEntryText *self = TPL_LOG_ENTRY_TEXT (obj); + g_debug ("TplLogEntryText: disposing\n"); + + tpl_object_unref_if_not_null (self->tpl_text); + self->tpl_text = NULL; + tpl_object_unref_if_not_null (self->sender); + self->sender = NULL; + tpl_object_unref_if_not_null (self->receiver); + self->receiver = NULL; + + G_OBJECT_CLASS (tpl_log_entry_text_parent_class)->finalize (obj); + + g_debug ("TplLogEntryText: disposed\n"); +} + +static void +tpl_log_entry_text_finalize (GObject * obj) +{ + TplLogEntryText *self = TPL_LOG_ENTRY_TEXT (obj); + + g_debug ("TplLogEntryText: finalizing\n"); + + g_free ((gchar *) self->message); + self->message = NULL; + g_free ((gchar *) self->chat_id); + self->chat_id = NULL; + + G_OBJECT_CLASS (tpl_log_entry_text_parent_class)->dispose (obj); + + g_debug ("TplLogEntryText: finalized\n"); +} + + +TplLogEntryText * +tpl_log_entry_text_new (void) +{ + return g_object_new (TPL_TYPE_LOG_ENTRY_TEXT, NULL); +} + + + +TpChannelTextMessageType +tpl_log_entry_text_message_type_from_str (const gchar * type_str) +{ + if (g_strcmp0 (type_str, "normal") == 0) + { + return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; + } + else if (g_strcmp0 (type_str, "action") == 0) + { + return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION; + } + else if (g_strcmp0 (type_str, "notice") == 0) + { + return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE; + } + else if (g_strcmp0 (type_str, "auto-reply") == 0) + { + return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY; + } + + return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; +} + + +const gchar * +tpl_log_entry_text_message_type_to_str (TpChannelTextMessageType msg_type) +{ + switch (msg_type) + { + case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION: + return "action"; + case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE: + return "notice"; + case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY: + return "auto-reply"; + default: + return "normal"; + } +} + + +TplChannel * +tpl_log_entry_text_get_tpl_channel (TplLogEntryText * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY_TEXT (self), NULL); + + return + tpl_text_channel_get_tpl_channel (tpl_log_entry_text_get_tpl_text_channel + (self)); +} + +TplTextChannel * +tpl_log_entry_text_get_tpl_text_channel (TplLogEntryText * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY_TEXT (self), NULL); + return self->tpl_text; +} + +TplContact * +tpl_log_entry_text_get_sender (TplLogEntryText * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY_TEXT (self), NULL); + return self->sender; +} + +TplContact * +tpl_log_entry_text_get_receiver (TplLogEntryText * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY_TEXT (self), NULL); + return self->receiver; +} + +const gchar * +tpl_log_entry_text_get_message (TplLogEntryText * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY_TEXT (self), NULL); + return self->message; +} + +TpChannelTextMessageType +tpl_log_entry_text_get_message_type (TplLogEntryText * self) +{ + return self->message_type; +} + +TplLogEntryTextSignalType +tpl_log_entry_text_get_signal_type (TplLogEntryText * self) +{ + return self->signal_type; +} + +TplLogEntryTextDirection +tpl_log_entry_text_get_direction (TplLogEntryText * self) +{ + return self->direction; +} + +guint +tpl_log_entry_text_get_message_id (TplLogEntryText * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY_TEXT (self), 0); + return self->message_id; +} + +const gchar * +tpl_log_entry_text_get_chat_id (TplLogEntryText * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY_TEXT (self), NULL); + return self->chat_id; +} + + +void +tpl_log_entry_text_set_tpl_text_channel (TplLogEntryText * self, + TplTextChannel * data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (self)); + g_return_if_fail (TPL_IS_TEXT_CHANNEL (data) || data == NULL); + + tpl_object_unref_if_not_null (self->tpl_text); + self->tpl_text = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_log_entry_text_set_sender (TplLogEntryText * self, TplContact * data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (self)); + g_return_if_fail (TPL_IS_CONTACT (data) || data == NULL); + + tpl_object_unref_if_not_null (self->sender); + self->sender = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_log_entry_text_set_receiver (TplLogEntryText * self, TplContact * data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (self)); + g_return_if_fail (TPL_IS_CONTACT (data) || data == NULL); + + tpl_object_unref_if_not_null (self->receiver); + self->receiver = data; + tpl_object_ref_if_not_null (data); +} + +void +tpl_log_entry_text_set_message (TplLogEntryText * self, const gchar * data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (self)); + + g_free ((gchar *) self->message); + self->message = g_strdup (data); +} + +void +tpl_log_entry_text_set_message_type (TplLogEntryText * self, + TpChannelTextMessageType data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (self)); + + self->message_type = data; +} + +void +tpl_log_entry_text_set_signal_type (TplLogEntryText * self, + TplLogEntryTextSignalType data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (self)); + + self->signal_type = data; +} + +void +tpl_log_entry_text_set_direction (TplLogEntryText * self, + TplLogEntryTextDirection data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (self)); + + self->direction = data; +} + +void +tpl_log_entry_text_set_message_id (TplLogEntryText * self, guint data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (self)); + + self->message_id = data; +} + +void +tpl_log_entry_text_set_chat_id (TplLogEntryText * self, const gchar * data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY_TEXT (self)); + + g_free ((gchar *) self->chat_id); + self->chat_id = g_strdup (data); +} diff --git a/telepathy-logger/log-entry-text.h b/telepathy-logger/log-entry-text.h new file mode 100644 index 0000000..7d6c32d --- /dev/null +++ b/telepathy-logger/log-entry-text.h @@ -0,0 +1,144 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#ifndef __TPL_LOG_ENTRY_TEXT_H__ +#define __TPL_LOG_ENTRY_TEXT_H__ + +#include <glib-object.h> +#include <telepathy-glib/enums.h> + +#include <telepathy-logger/channel-text.h> +#include <telepathy-logger/contact.h> + +G_BEGIN_DECLS +#define TPL_TYPE_LOG_ENTRY_TEXT (tpl_log_entry_text_get_type ()) +#define TPL_LOG_ENTRY_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPL_TYPE_LOG_ENTRY_TEXT, TplLogEntryText)) +#define TPL_LOG_ENTRY_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TPL_TYPE_LOG_ENTRY_TEXT, TplLogEntryTextClass)) +#define TPL_IS_LOG_ENTRY_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPL_TYPE_LOG_ENTRY_TEXT)) +#define TPL_IS_LOG_ENTRY_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TPL_TYPE_LOG_ENTRY_TEXT)) +#define TPL_LOG_ENTRY_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TPL_TYPE_LOG_ENTRY_TEXT, TplLogEntryTextClass)) +/* Valid for org.freedesktop.Telepathy.Channel.Type.Text */ + typedef enum +{ + TPL_LOG_ENTRY_TEXT_SIGNAL_SENT, + TPL_LOG_ENTRY_TEXT_SIGNAL_RECEIVED, + TPL_LOG_ENTRY_TEXT_SIGNAL_SEND_ERROR, + TPL_LOG_ENTRY_TEXT_SIGNAL_LOST_MESSAGE, + TPL_LOG_ENTRY_TEXT_SIGNAL_CHAT_STATUS_CHANGED, + TPL_LOG_ENTRY_TEXT_SIGNAL_CHANNEL_CLOSED +} TplLogEntryTextSignalType; + +/* wether the log entry is referring to something outgoing on incoming */ +typedef enum +{ + TPL_LOG_ENTRY_TEXT_CHANNEL_IN, + TPL_LOG_ENTRY_TEXT_CHANNEL_OUT +} TplLogEntryTextDirection; + +typedef struct +{ + GObject parent; + + /* Private */ + + // tpl_channel has informations about channel/account/connection + TplTextChannel *tpl_text; + // what kind of signal produced this log entry + TplLogEntryTextSignalType signal_type; + TpChannelTextMessageType message_type; + // is the this entry produced by something incoming or outgoing + TplLogEntryTextDirection direction; + + // message and receiver may be NULL depending on the signal. ie. + // status changed signals set only the sender + TplContact *sender; + TplContact *receiver; + const gchar *message; + guint message_id; + const gchar *chat_id; + gboolean chatroom; +} TplLogEntryText; + +typedef struct +{ + GObjectClass parent_class; +} TplLogEntryTextClass; + +GType tpl_log_entry_text_get_type (void); + +TplLogEntryText *tpl_log_entry_text_new (void); + +TpChannelTextMessageType tpl_log_entry_text_message_type_from_str (const gchar + * + type_str); + +const gchar *tpl_log_entry_text_message_type_to_str (TpChannelTextMessageType + msg_type); + +TplChannel *tpl_log_entry_text_get_tpl_channel (TplLogEntryText * self); + +TplTextChannel *tpl_log_entry_text_get_tpl_text_channel (TplLogEntryText * + self); + +TplContact *tpl_log_entry_text_get_sender (TplLogEntryText * self); + +TplContact *tpl_log_entry_text_get_receiver (TplLogEntryText * self); + +const gchar *tpl_log_entry_text_get_message (TplLogEntryText * self); + +TpChannelTextMessageType +tpl_log_entry_text_get_message_type (TplLogEntryText * self); + +TplLogEntryTextSignalType +tpl_log_entry_text_get_signal_type (TplLogEntryText * self); + +TplLogEntryTextDirection +tpl_log_entry_text_get_direction (TplLogEntryText * self); + +guint tpl_log_entry_text_get_message_id (TplLogEntryText * self); + +const gchar *tpl_log_entry_text_get_chat_id (TplLogEntryText * self); + +gboolean tpl_log_entry_text_is_chatroom (TplLogEntryText * self); + +void +tpl_log_entry_text_set_tpl_text_channel (TplLogEntryText * self, + TplTextChannel * data); + +void tpl_log_entry_text_set_sender (TplLogEntryText * self, + TplContact * data); +void tpl_log_entry_text_set_receiver (TplLogEntryText * self, + TplContact * data); +void tpl_log_entry_text_set_message (TplLogEntryText * self, + const gchar * data); +void tpl_log_entry_text_set_message_type (TplLogEntryText * self, + TpChannelTextMessageType data); +void tpl_log_entry_text_set_signal_type (TplLogEntryText * self, + TplLogEntryTextSignalType data); +void tpl_log_entry_text_set_direction (TplLogEntryText * self, + TplLogEntryTextDirection data); +void tpl_log_entry_text_set_message_id (TplLogEntryText * self, guint data); +void tpl_log_entry_text_set_chat_id (TplLogEntryText * self, + const gchar * data); +void tpl_log_entry_text_set_chatroom (TplLogEntryText * self, gboolean data); + +G_END_DECLS +#endif // __TPL_LOG_ENTRY_TEXT_H__ diff --git a/telepathy-logger/log-entry.c b/telepathy-logger/log-entry.c new file mode 100644 index 0000000..cddddb0 --- /dev/null +++ b/telepathy-logger/log-entry.c @@ -0,0 +1,131 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#include "log-entry.h" + +#include <glib.h> + +#include <telepathy-logger/debug.h> + +G_DEFINE_TYPE (TplLogEntry, tpl_log_entry, G_TYPE_OBJECT) + static void tpl_log_entry_finalize (GObject * obj) +{ + G_OBJECT_CLASS (tpl_log_entry_parent_class)->finalize (obj); +} + +static void +tpl_log_entry_dispose (GObject * obj) +{ + TplLogEntry *self = TPL_LOG_ENTRY (obj); + + DEBUG ("TplLogEntry: disposing\n"); + + tpl_object_unref_if_not_null (tpl_log_entry_get_entry (self)); + self->entry.generic = NULL; + + G_OBJECT_CLASS (tpl_log_entry_parent_class)->dispose (obj); + + DEBUG ("TplLogEntry: disposed\n"); +} + + +static void +tpl_log_entry_class_init (TplLogEntryClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = tpl_log_entry_finalize; + object_class->dispose = tpl_log_entry_dispose; +} + +static void +tpl_log_entry_init (TplLogEntry * self) +{ +} + + +void +tpl_log_entry_set_entry (TplLogEntry * self, void *entry) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY (self)); + g_return_if_fail (self->entry.generic == NULL); + + if (TPL_IS_LOG_ENTRY_TEXT (entry)) + { + self->type = TPL_LOG_ENTRY_TEXT; + self->entry.text = entry; + } + else + { + g_error ("TplLogEntry does handle only Text channels\n"); + } +} + +TplLogEntryType +tpl_log_entry_get_entry_type (TplLogEntry * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY (self), TPL_LOG_ENTRY_ERROR); + + return self->type; +} + +void * +tpl_log_entry_get_entry (TplLogEntry * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY (self), NULL); + + switch (self->type) + { + case TPL_LOG_ENTRY_TEXT: + if (!TPL_IS_LOG_ENTRY_TEXT (self->entry.text)) + { + g_error ("TplLogEntry->entry->text not a TplLogEntryText instance"); + return NULL; + } + return self->entry.text; + break; + default: + g_warning ("TplLogEntry type not handled\n"); + return NULL; + break; + } +} + +TplLogEntry * +tpl_log_entry_new (void) +{ + return g_object_new (TPL_TYPE_LOG_ENTRY, NULL); +} + + +time_t +tpl_log_entry_get_timestamp (TplLogEntry * self) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY (self), -1); + return self->timestamp; +} + +void +tpl_log_entry_set_timestamp (TplLogEntry * self, time_t data) +{ + g_return_if_fail (TPL_IS_LOG_ENTRY (self)); + + self->timestamp = data; +} diff --git a/telepathy-logger/log-entry.h b/telepathy-logger/log-entry.h new file mode 100644 index 0000000..dbb073e --- /dev/null +++ b/telepathy-logger/log-entry.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#ifndef __TPL_LOG_ENTRY_H__ +#define __TPL_LOG_ENTRY_H__ + +#include <glib-object.h> + +#include <telepathy-logger/log-entry-text.h> + +G_BEGIN_DECLS +#define TPL_TYPE_LOG_ENTRY (tpl_log_entry_get_type ()) +#define TPL_LOG_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPL_TYPE_LOG_ENTRY, TplLogEntry)) +#define TPL_LOG_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TPL_TYPE_LOG_ENTRY, TplLogEntryClass)) +#define TPL_IS_LOG_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPL_TYPE_LOG_ENTRY)) +#define TPL_IS_LOG_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TPL_TYPE_LOG_ENTRY)) +#define TPL_LOG_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TPL_TYPE_LOG_ENTRY, TplLogEntryClass)) + typedef enum +{ + TPL_LOG_ENTRY_ERROR, + TPL_LOG_ENTRY_TEXT +} TplLogEntryType; + +typedef struct +{ + GObject parent; + + /* Private */ + TplLogEntryType type; + union + { + TplLogEntryText *text; + void *generic; + } entry; + time_t timestamp; +} TplLogEntry; + +typedef struct +{ + GObjectClass parent_class; +} TplLogEntryClass; + +GType tpl_log_entry_get_type (void); + +TplLogEntry *tpl_log_entry_new (void); + +TplLogEntryType tpl_log_entry_get_entry_type (TplLogEntry * data); +void *tpl_log_entry_get_entry (TplLogEntry * data); +time_t tpl_log_entry_get_timestamp (TplLogEntry * self); + +// sets entry type and its object +void tpl_log_entry_set_entry (TplLogEntry * self, void *entry); +void tpl_log_entry_set_timestamp (TplLogEntry * self, time_t data); + +G_END_DECLS +#endif // __TPL_LOG_ENTRY_H__ diff --git a/telepathy-logger/log-manager-priv.h b/telepathy-logger/log-manager-priv.h new file mode 100644 index 0000000..2bfcaea --- /dev/null +++ b/telepathy-logger/log-manager-priv.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __TPL_LOG_MANAGER_PRIV_H__ +#define __TPL_LOG_MANAGER_PRIV_H__ + +#include <telepathy-logger/log-manager.h> + +gboolean tpl_log_manager_add_message (TplLogManager * manager, + const gchar * chat_id, + gboolean chatroom, + TplLogEntry * message, GError ** error); + +#endif /* __TPL_LOG_MANAGER_PRIV_H__ */ diff --git a/telepathy-logger/log-manager.c b/telepathy-logger/log-manager.c new file mode 100644 index 0000000..f9f1a75 --- /dev/null +++ b/telepathy-logger/log-manager.c @@ -0,0 +1,833 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + * Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#include "log-manager.h" // RO +#include "log-manager-priv.h" // W + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <gio/gio.h> +#include <glib/gstdio.h> +#include <telepathy-glib/interfaces.h> +#include <telepathy-glib/util.h> + +#include <telepathy-logger/log-entry.h> +#include <telepathy-logger/log-store.h> +#include <telepathy-logger/log-store-empathy.h> +#include <telepathy-logger/datetime.h> +#include <telepathy-logger/utils.h> + +//#define DEBUG_FLAG EMPATHY_DEBUG_OTHER +//#include <empathy-debug.h> + +#define DEBUG g_debug + +#define GET_PRIV(obj) TPL_GET_PRIV (obj, TplLogManager) +typedef struct +{ + GList *stores; +} TplLogManagerPriv; + +G_DEFINE_TYPE (TplLogManager, tpl_log_manager, G_TYPE_OBJECT); + +static TplLogManager *manager_singleton = NULL; + +static void +log_manager_finalize (GObject * object) +{ + TplLogManagerPriv *priv; + + priv = GET_PRIV (object); + + g_list_foreach (priv->stores, (GFunc) g_object_unref, NULL); + g_list_free (priv->stores); +} + +/* + * - Singleton LogManager constructor - + * Initialises LogStores with LogStoreEmpathy instance + */ +static GObject * +log_manager_constructor (GType type, + guint n_props, GObjectConstructParam * props) +{ + GObject *retval; + TplLogManagerPriv *priv; + + if (manager_singleton) + { + retval = g_object_ref (manager_singleton); + } + else + { + retval = G_OBJECT_CLASS (tpl_log_manager_parent_class)->constructor + (type, n_props, props); + + manager_singleton = TPL_LOG_MANAGER (retval); + g_object_add_weak_pointer (retval, (gpointer *) & manager_singleton); + + priv = GET_PRIV (manager_singleton); + + priv->stores = g_list_append (priv->stores, + g_object_new (TPL_TYPE_LOG_STORE_EMPATHY, + NULL)); + } + + return retval; +} + +static void +tpl_log_manager_class_init (TplLogManagerClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = log_manager_constructor; + object_class->finalize = log_manager_finalize; + + g_type_class_add_private (object_class, sizeof (TplLogManagerPriv)); +} + +static void +tpl_log_manager_init (TplLogManager * manager) +{ + TplLogManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, + TPL_TYPE_LOG_MANAGER, + TplLogManagerPriv); + + manager->priv = priv; +} + +TplLogManager * +tpl_log_manager_dup_singleton (void) +{ + return g_object_new (TPL_TYPE_LOG_MANAGER, NULL); +} + + +gboolean +tpl_log_manager_add_message (TplLogManager * manager, + const gchar * chat_id, + gboolean chatroom, + TplLogEntry * message, GError ** error) +{ + TplLogManagerPriv *priv; + GList *l; + gboolean out = FALSE; + gboolean found = FALSE; + + /* TODO: When multiple log stores appear with add_message implementations + * make this customisable. */ + const gchar *add_store = "TpLogger"; + + g_return_val_if_fail (TPL_IS_LOG_MANAGER (manager), FALSE); + g_return_val_if_fail (!TPL_STR_EMPTY (chat_id), FALSE); + g_return_val_if_fail (TPL_IS_LOG_ENTRY (message), FALSE); + + priv = GET_PRIV (manager); + + for (l = priv->stores; l; l = g_list_next (l)) + { + if (!tp_strdiff + (tpl_log_store_get_name (TPL_LOG_STORE (l->data)), add_store)) + { + out = tpl_log_store_add_message (TPL_LOG_STORE (l->data), + chat_id, chatroom, message, error); + found = TRUE; + break; + } + } + + if (!found) + DEBUG ("Failed to find chosen log store to write to."); + + return out; +} + + + +gboolean +tpl_log_manager_exists (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, gboolean chatroom) +{ + GList *l; + TplLogManagerPriv *priv; + + g_return_val_if_fail (TPL_IS_LOG_MANAGER (manager), FALSE); + g_return_val_if_fail (chat_id != NULL, FALSE); + + priv = GET_PRIV (manager); + + for (l = priv->stores; l; l = g_list_next (l)) + { + if (tpl_log_store_exists (TPL_LOG_STORE (l->data), + account, chat_id, chatroom)) + return TRUE; + } + + return FALSE; +} + +// returns a list of gchar dates +GList * +tpl_log_manager_get_dates (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, gboolean chatroom) +{ + GList *l, *out = NULL; + TplLogManagerPriv *priv; + + g_return_val_if_fail (TPL_IS_LOG_MANAGER (manager), NULL); + g_return_val_if_fail (chat_id != NULL, NULL); + + priv = GET_PRIV (manager); + + for (l = priv->stores; l; l = g_list_next (l)) + { + TplLogStore *store = TPL_LOG_STORE (l->data); + GList *new; + + /* Insert dates of each store in the out list. Keep the out list sorted + * and avoid to insert dups. */ + new = tpl_log_store_get_dates (store, account, chat_id, chatroom); + while (new) + { + if (g_list_find_custom (out, new->data, (GCompareFunc) strcmp)) + g_free (new->data); + else + out = + g_list_insert_sorted (out, new->data, (GCompareFunc) strcmp); + + new = g_list_delete_link (new, new); + } + } + + return out; +} + +GList * +tpl_log_manager_get_messages_for_date (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, const gchar * date) +{ + GList *l, *out = NULL; + TplLogManagerPriv *priv; + + g_return_val_if_fail (TPL_IS_LOG_MANAGER (manager), NULL); + g_return_val_if_fail (chat_id != NULL, NULL); + + priv = GET_PRIV (manager); + + for (l = priv->stores; l; l = g_list_next (l)) + { + TplLogStore *store = TPL_LOG_STORE (l->data); + + out = + g_list_concat (out, + tpl_log_store_get_messages_for_date (store, account, + chat_id, chatroom, + date)); + } + + return out; +} + +static gint +log_manager_message_date_cmp (gconstpointer a, gconstpointer b) +{ + TplLogEntry *one = (TplLogEntry *) a; + TplLogEntry *two = (TplLogEntry *) b; + time_t one_time, two_time; + + one_time = tpl_log_entry_get_timestamp (one); + two_time = tpl_log_entry_get_timestamp (two); + + /* Return -1 of message1 is older than message2 */ + return one_time < two_time ? -1 : one_time - two_time; +} + +GList * +tpl_log_manager_get_filtered_messages (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + guint num_messages, + TplLogMessageFilter filter, + gpointer user_data) +{ + TplLogManagerPriv *priv; + GList *out = NULL; + GList *l; + guint i = 0; + + g_return_val_if_fail (TPL_IS_LOG_MANAGER (manager), NULL); + g_return_val_if_fail (chat_id != NULL, NULL); + + priv = GET_PRIV (manager); + + /* Get num_messages from each log store and keep only the + * newest ones in the out list. Keep that list sorted: Older first. */ + for (l = priv->stores; l; l = g_list_next (l)) + { + TplLogStore *store = TPL_LOG_STORE (l->data); + GList *new; + + new = tpl_log_store_get_filtered_messages (store, account, chat_id, + chatroom, num_messages, + filter, user_data); + while (new) + { + if (i < num_messages) + { + /* We have less message than needed so far. Keep this message */ + out = g_list_insert_sorted (out, new->data, + (GCompareFunc) + log_manager_message_date_cmp); + i++; + } + else if (log_manager_message_date_cmp (new->data, out->data) > 0) + { + /* This message is newer than the oldest message we have in out + * list. Remove the head of out list and insert this message */ + g_object_unref (out->data); + out = g_list_delete_link (out, out); + out = g_list_insert_sorted (out, new->data, + (GCompareFunc) + log_manager_message_date_cmp); + } + else + { + /* This message is older than the oldest message we have in out + * list. Drop it. */ + g_object_unref (new->data); + } + + new = g_list_delete_link (new, new); + } + } + + return out; +} + +GList * +tpl_log_manager_get_chats (TplLogManager * manager, TpAccount * account) +{ + GList *l, *out = NULL; + TplLogManagerPriv *priv; + + g_return_val_if_fail (TPL_IS_LOG_MANAGER (manager), NULL); + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + priv = GET_PRIV (manager); + + for (l = priv->stores; l; l = g_list_next (l)) + { + TplLogStore *store = TPL_LOG_STORE (l->data); + + out = g_list_concat (out, tpl_log_store_get_chats (store, account)); + } + + return out; +} + +GList * +tpl_log_manager_search_in_identifier_chats_new (TplLogManager * manager, + TpAccount * account, + gchar const *identifier, + const gchar * text) +{ + GList *l, *out = NULL; + TplLogManagerPriv *priv; + + g_return_val_if_fail (TPL_IS_LOG_MANAGER (manager), NULL); + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (identifier), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (text), NULL); + + priv = GET_PRIV (manager); + + for (l = priv->stores; l; l = g_list_next (l)) + { + TplLogStore *store = TPL_LOG_STORE (l->data); + + out = g_list_concat (out, + tpl_log_store_search_in_identifier_chats_new + (store, account, identifier, text)); + } + + return out; +} + +GList * +tpl_log_manager_search_new (TplLogManager * manager, const gchar * text) +{ + GList *l, *out = NULL; + TplLogManagerPriv *priv; + + g_return_val_if_fail (TPL_IS_LOG_MANAGER (manager), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (text), NULL); + + priv = GET_PRIV (manager); + + for (l = priv->stores; l; l = g_list_next (l)) + { + TplLogStore *store = TPL_LOG_STORE (l->data); + + out = g_list_concat (out, tpl_log_store_search_new (store, text)); + } + + return out; +} + +void +tpl_log_manager_search_hit_free (TplLogSearchHit * hit) +{ + if (hit->account != NULL) + g_object_unref (hit->account); + + g_free (hit->date); + g_free (hit->filename); + g_free (hit->chat_id); + + g_slice_free (TplLogSearchHit, hit); +} + +void +tpl_log_manager_search_free (GList * hits) +{ + GList *l; + + for (l = hits; l; l = g_list_next (l)) + { + tpl_log_manager_search_hit_free (l->data); + } + + g_list_free (hits); +} + +/* Format is just date, 20061201. */ +gchar * +tpl_log_manager_get_date_readable (const gchar * date) +{ + time_t t; + + t = tpl_time_parse (date); + + return tpl_time_to_string_local (t, "%a %d %b %Y"); +} + +/* Async */ + +TplLogManagerAsyncData * +tpl_log_manager_async_data_new (void) +{ + return g_slice_new (TplLogManagerAsyncData); +} + +void +tpl_log_manager_async_data_free (TplLogManagerAsyncData * data) +{ + g_return_if_fail (TPL_IS_LOG_MANAGER (data->manager)); + g_return_if_fail (data->request && data->request_free); + + g_object_unref (data->manager); + data->request_free (data->request); + g_free (data); +} + + + +TplLogManagerChatInfo * +tpl_log_manager_chat_info_new (void) +{ + return g_slice_new0 (TplLogManagerChatInfo); +} + +void +tpl_log_manager_chat_info_free (TplLogManagerChatInfo * data) +{ + tpl_object_unref_if_not_null (data->account); + if (data->chat_id) + g_free (data->chat_id); + if (data->date) + g_free (data->date); + g_free (data); +} + + + +static gpointer +_tpl_log_manager_async_operation_finish (TplLogManager * manager, + GAsyncResult * result, + TplLogManagerAsyncData * async_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + g_print ("FINISH CALLED\n"); + return g_simple_async_result_get_op_res_gpointer (simple); +} + +static void +_tpl_log_manager_async_operation_cb (GObject * source_object, + GAsyncResult * result, + gpointer user_data) +{ + TplLogManager *manager = TPL_LOG_MANAGER (source_object); + TplLogManagerAsyncData *async_data = (TplLogManagerAsyncData *) user_data; + gpointer retval; + + g_message ("GSimple CB called: calling _finish to retrieve the result\n"); + retval = + _tpl_log_manager_async_operation_finish (manager, result, async_data); + + if (async_data->cb) + { + async_data->cb (async_data->manager, retval, NULL, + async_data->user_data); + } + + //tpl_log_manager_async_data_free(async_data); +} + +/* wrapper around GIO's GSimpleAsync* */ +static void +_tpl_log_manager_call_async_operation (TplLogManager * manager, + GSimpleAsyncThreadFunc + operation_thread_func, + TplLogManagerAsyncData * async_data, + TplLogManagerAsyncCallback callback) +{ + GSimpleAsyncResult *simple; + + g_message ("called async\n"); + simple = g_simple_async_result_new (G_OBJECT (manager), + _tpl_log_manager_async_operation_cb, + async_data, + _tpl_log_manager_async_operation_finish); + + g_simple_async_result_run_in_thread (simple, operation_thread_func, 0, + NULL); +} + +/* end of Async common function */ + +gboolean +tpl_log_manager_add_message_async (TplLogManager * manager, + const gchar * chat_id, + gboolean chatroom, + TplLogEntry * message, GError ** error) + + +/* Start of get_dates async implementation */ +static void +_get_dates_async_thread (GSimpleAsyncResult * simple, GObject * object, + GCancellable * cancellable) +{ + TplLogManagerAsyncData *async_data; + TplLogManagerChatInfo *chat_info; + GList *lst = NULL; + + async_data = g_async_result_get_user_data (G_ASYNC_RESULT (simple)); + chat_info = async_data->request; + + // use the sync method + lst = tpl_log_manager_get_dates (async_data->manager, + chat_info->account, chat_info->chat_id, + chat_info->is_chatroom); + + g_simple_async_result_set_op_res_gpointer (simple, lst, NULL); // TODO add destructor + g_message ("THREAD!: GASYNC RESULT: SET GLIST PTR\n"); +} + + +void +tpl_log_manager_get_dates_async (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean is_chatroom, + TplLogManagerAsyncCallback callback, + gpointer user_data, GDestroyNotify destroy) +{ + TplLogManagerChatInfo *chat_info = tpl_log_manager_chat_info_new (); + TplLogManagerAsyncData *async_data = tpl_log_manager_async_data_new (); + + tpl_call_with_err_if_fail (TPL_IS_LOG_MANAGER (manager), manager, + TPL_LOG_MANAGER, FAILED, + "manager argument passed is not a TplManager instance", + callback, user_data); + tpl_call_with_err_if_fail (TP_IS_ACCOUNT (account), manager, + TPL_LOG_MANAGER, FAILED, + "account argument is not a TpAccount instance", + callback, user_data); + tpl_call_with_err_if_fail (!TPL_STR_EMPTY (chat_id), manager, + TPL_LOG_MANAGER, FAILED, + "chat_id argument passed cannot be empty string or NULL ptr", + callback, user_data); + + + + // TODO add check against manager, chat_info + // TODO add check against date!=NULL and call cb in case of error + + chat_info->account = account; + g_object_ref (account); + chat_info->chat_id = g_strdup (chat_id); + chat_info->is_chatroom = is_chatroom; + + async_data->manager = manager; + g_object_ref (manager); + async_data->request = (gpointer) chat_info; + async_data->request_free = + (TplLogManagerFreeFunc) tpl_log_manager_chat_info_free; + async_data->cb = callback; + async_data->user_data = user_data; + + _tpl_log_manager_call_async_operation (manager, _get_dates_async_thread, + async_data, callback); +} + +/* End of get_dates async implementation */ + +/* Start of get_messages_for_date async implementation */ +static void +_get_messages_for_date_async_thread (GSimpleAsyncResult * simple, + GObject * object, + GCancellable * cancellable) +{ + TplLogManagerAsyncData *async_data; + TplLogManagerChatInfo *chat_info; + GList *lst; + + async_data = g_async_result_get_user_data (G_ASYNC_RESULT (simple)); + chat_info = async_data->request; + + // use the sync method + lst = tpl_log_manager_get_messages_for_date (async_data->manager, + chat_info->account, + chat_info->chat_id, + chat_info->is_chatroom, + chat_info->date); + + g_simple_async_result_set_op_res_gpointer (simple, lst, NULL); // TODO add destructor + g_message ("THREAD!: GASYNC RESULT: SET GLIST PTR\n"); +} + + +void +tpl_log_manager_get_messages_for_date_async (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean is_chatroom, + const gchar * date, + TplLogManagerAsyncCallback + callback, gpointer user_data, + GDestroyNotify destroy) +{ + TplLogManagerChatInfo *chat_info = tpl_log_manager_chat_info_new (); + TplLogManagerAsyncData *async_data = tpl_log_manager_async_data_new (); + + // TODO add check against manager, chat_info + // TODO add check against date!=NULL and call cb in case of error + tpl_call_with_err_if_fail (TPL_IS_LOG_MANAGER (manager), manager, + TPL_LOG_MANAGER, FAILED, + "manager argument passed is not a TplManager instance", + callback, user_data); + tpl_call_with_err_if_fail (TP_IS_ACCOUNT (account), manager, + TPL_LOG_MANAGER, FAILED, + "account argument is not a TpAccount instance", + callback, user_data); + tpl_call_with_err_if_fail (!TPL_STR_EMPTY (chat_id), manager, + TPL_LOG_MANAGER, FAILED, + "chat_id argument passed cannot be empty string or NULL ptr", + callback, user_data); + tpl_call_with_err_if_fail (!TPL_STR_EMPTY (date), manager, + TPL_LOG_MANAGER, FAILED, + "date argument passed cannot be empty string or NULL ptr", + callback, user_data); + + + + chat_info->account = account; + g_object_ref (account); + chat_info->chat_id = g_strdup (chat_id); + chat_info->is_chatroom = is_chatroom; + chat_info->date = g_strdup(date); + + async_data->manager = manager; + g_object_ref (manager); + async_data->request = (gpointer) chat_info; + async_data->request_free = + (TplLogManagerFreeFunc) tpl_log_manager_chat_info_free; + async_data->cb = callback; + async_data->user_data = user_data; + + _tpl_log_manager_call_async_operation (manager, + _get_messages_for_date_async_thread, + async_data, callback); +} + +/* End of get_messages_for_date async implementation */ + +/* Start of get_filtered_messages async implementation */ +static void +_get_filtered_messages_thread (GSimpleAsyncResult * simple, GObject * object, + GCancellable * cancellable) +{ + TplLogManagerAsyncData *async_data; + TplLogManagerChatInfo *chat_info; + GList *lst; + + async_data = g_async_result_get_user_data (G_ASYNC_RESULT (simple)); + chat_info = async_data->request; + + // use the sync method + lst = tpl_log_manager_get_filtered_messages (async_data->manager, + chat_info->account, + chat_info->chat_id, + chat_info->is_chatroom, + chat_info->num_messages, + chat_info->filter, + chat_info->user_data); + + g_simple_async_result_set_op_res_gpointer (simple, lst, NULL); // TODO add destructor + g_message ("THREAD!: GASYNC RESULT: SET GLIST PTR\n"); +} + + +void +tpl_log_manager_get_filtered_messages_async (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean is_chatroom, + guint num_messages, + TplLogMessageFilter filter, + gpointer filter_user_data, + TplLogManagerAsyncCallback + callback, gpointer user_data, + GDestroyNotify destroy) +{ + TplLogManagerChatInfo *chat_info = tpl_log_manager_chat_info_new (); + TplLogManagerAsyncData *async_data = tpl_log_manager_async_data_new (); + + tpl_call_with_err_if_fail (TPL_IS_LOG_MANAGER (manager), manager, + TPL_LOG_MANAGER, FAILED, + "manager argument passed is not a TplManager instance", + callback, user_data); + tpl_call_with_err_if_fail (TP_IS_ACCOUNT (account), manager, + TPL_LOG_MANAGER, FAILED, + "account argument is not a TpAccount instance", + callback, user_data); + tpl_call_with_err_if_fail (!TPL_STR_EMPTY (chat_id), manager, + TPL_LOG_MANAGER, FAILED, + "chat_id argument passed cannot be empty string or NULL ptr", + callback, user_data); + tpl_call_with_err_if_fail ((num_messages > 0), manager, + TPL_LOG_MANAGER, FAILED, + "num_message argument passed needs to be greater than 0", + callback, user_data); + tpl_call_with_err_if_fail (filter != NULL, manager, + TPL_LOG_MANAGER, FAILED, + "filter function should be not NULL", + callback, user_data); + + chat_info->account = account; + g_object_ref (account); + chat_info->chat_id = g_strdup (chat_id); + chat_info->is_chatroom = is_chatroom; + chat_info->num_messages = num_messages; + chat_info->filter = filter; + chat_info->user_data = filter_user_data; + + async_data->manager = manager; + g_object_ref (manager); + async_data->request = (gpointer) chat_info; + async_data->request_free = + (TplLogManagerFreeFunc) tpl_log_manager_chat_info_free; + async_data->cb = callback; + async_data->user_data = user_data; + + _tpl_log_manager_call_async_operation (manager, + _get_filtered_messages_thread, + async_data, callback); +} + +/* End of get_filtered_messages async implementation */ + + +/* Start of get_chats async implementation */ +static void +_get_chats_thread (GSimpleAsyncResult * simple, GObject * object, + GCancellable * cancellable) +{ + TplLogManagerAsyncData *async_data; + TplLogManagerChatInfo *chat_info; + GList *lst; + + async_data = g_async_result_get_user_data (G_ASYNC_RESULT (simple)); + chat_info = async_data->request; + + // use the sync method + lst = tpl_log_manager_get_chats (async_data->manager, chat_info->account); + + g_simple_async_result_set_op_res_gpointer (simple, lst, NULL); // TODO add destructor +} + + +void +tpl_log_manager_get_chats_async (TplLogManager * manager, + TpAccount * account, + TplLogManagerAsyncCallback callback, + gpointer user_data, GDestroyNotify destroy) +{ + TplLogManagerChatInfo *chat_info = tpl_log_manager_chat_info_new (); + TplLogManagerAsyncData *async_data = tpl_log_manager_async_data_new (); + + tpl_call_with_err_if_fail (TPL_IS_LOG_MANAGER (manager), manager, + TPL_LOG_MANAGER, FAILED, + "manager argument is not a TplManager instance", + callback, user_data); + tpl_call_with_err_if_fail (TP_IS_ACCOUNT (account), manager, + TPL_LOG_MANAGER, FAILED, + "account argument is not a TpAccount instance", + callback, user_data); + + chat_info->account = account; + g_object_ref (account); + + async_data->manager = manager; + g_object_ref (manager); + async_data->request = (gpointer) chat_info; + async_data->request_free = + (TplLogManagerFreeFunc) tpl_log_manager_chat_info_free; + async_data->cb = callback; + async_data->user_data = user_data; + + _tpl_log_manager_call_async_operation (manager, + _get_chats_thread, + async_data, callback); +} + +/* End of get_filtered_messages async implementation */ diff --git a/telepathy-logger/log-manager.h b/telepathy-logger/log-manager.h new file mode 100644 index 0000000..8bcdf7f --- /dev/null +++ b/telepathy-logger/log-manager.h @@ -0,0 +1,198 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __TPL_LOG_MANAGER_H__ +#define __TPL_LOG_MANAGER_H__ + +#include <gio/gio.h> +#include <glib-object.h> +#include <telepathy-glib/account.h> + +#include <telepathy-logger/log-entry.h> + +G_BEGIN_DECLS +#define TPL_TYPE_LOG_MANAGER (tpl_log_manager_get_type ()) +#define TPL_LOG_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TPL_TYPE_LOG_MANAGER, TplLogManager)) +#define TPL_LOG_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), TPL_TYPE_LOG_MANAGER, TplLogManagerClass)) +#define TPL_IS_LOG_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TPL_TYPE_LOG_MANAGER)) +#define TPL_IS_LOG_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TPL_TYPE_LOG_MANAGER)) +#define TPL_LOG_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TPL_TYPE_LOG_MANAGER, TplLogManagerClass)) + +#define TPL_LOG_MANAGER_ERROR g_quark_from_static_string ("tpl-log-manager-error-quark") + typedef enum +{ + TPL_LOG_MANAGER_ERROR_FAILED +} TplLogManagerError; + + + +typedef struct +{ + GObject parent; + + gpointer priv; +} TplLogManager; + +typedef struct +{ + GObjectClass parent_class; +} TplLogManagerClass; + +typedef struct +{ + TpAccount *account; + gchar *chat_id; + gboolean is_chatroom; + gchar *filename; + gchar *date; +} TplLogSearchHit; + +typedef gboolean (*TplLogMessageFilter) (TplLogEntry * message, + gpointer user_data); + +GType tpl_log_manager_get_type (void); + +TplLogManager *tpl_log_manager_dup_singleton (void); + +gboolean tpl_log_manager_exists (TplLogManager * manager, + TpAccount * account, const gchar * chat_id, + gboolean chatroom); + +GList *tpl_log_manager_get_dates (TplLogManager * manager, + TpAccount * account, const gchar * chat_id, + gboolean chatroom); + + +GList *tpl_log_manager_get_messages_for_date (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + const gchar * date); + +GList *tpl_log_manager_get_filtered_messages (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + guint num_messages, + TplLogMessageFilter filter, + gpointer user_data); + +GList *tpl_log_manager_get_chats (TplLogManager * manager, + TpAccount * account); + +GList *tpl_log_manager_search_in_identifier_chats_new (TplLogManager * + manager, + TpAccount * account, + gchar const + *identifier, + const gchar * text); + +GList *tpl_log_manager_search_new (TplLogManager * manager, + const gchar * text); + +void tpl_log_manager_search_free (GList * hits); + +gchar *tpl_log_manager_get_date_readable (const gchar * date); + +void tpl_log_manager_search_hit_free (TplLogSearchHit * hit); + + +/* Async */ +typedef void (*TplLogManagerAsyncCallback) (TplLogManager * manager, + gpointer result, GError * error, + gpointer user_data); + +typedef void (*TplLogManagerFreeFunc) (gpointer * data); + +typedef struct +{ + TplLogManager *manager; + gpointer request; + TplLogManagerFreeFunc request_free; + TplLogManagerAsyncCallback cb; + gpointer user_data; +} TplLogManagerAsyncData; + +TplLogManagerAsyncData *tpl_log_manager_async_data_new (void); + +void tpl_log_manager_async_data_free (TplLogManagerAsyncData * data); + + +typedef struct +{ + TpAccount *account; + gchar *chat_id; + gboolean is_chatroom; + gchar *date; + guint num_messages; + TplLogMessageFilter filter; + gpointer user_data; +} TplLogManagerChatInfo; + +TplLogManagerChatInfo *tpl_log_manager_chat_info_new (void); + +void tpl_log_manager_chat_info_free (TplLogManagerChatInfo * data); + +void tpl_log_manager_get_dates_async (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean is_chatroom, + TplLogManagerAsyncCallback callback, + gpointer user_data, + GDestroyNotify destroy); + + +void tpl_log_manager_get_messages_for_date_async (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean is_chatroom, + const gchar * date, + TplLogManagerAsyncCallback + callback, + gpointer user_data, + GDestroyNotify destroy); + +void tpl_log_manager_get_filtered_messages_async (TplLogManager * manager, + TpAccount * account, + const gchar * chat_id, + gboolean is_chatroom, + guint num_messages, + TplLogMessageFilter filter, + gpointer filter_user_data, + TplLogManagerAsyncCallback + callback, + gpointer user_data, + GDestroyNotify destroy); + +void +tpl_log_manager_get_chats_async (TplLogManager * manager, + TpAccount * account, + TplLogManagerAsyncCallback callback, + gpointer user_data, GDestroyNotify destroy); + + +/* End of Async */ + + +G_END_DECLS +#endif /* __TPL_LOG_MANAGER_H__ */ diff --git a/telepathy-logger/log-store-empathy.c b/telepathy-logger/log-store-empathy.c new file mode 100644 index 0000000..eb35aec --- /dev/null +++ b/telepathy-logger/log-store-empathy.c @@ -0,0 +1,1097 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + * Jonny Lamb <jonny.lamb@collabora.co.uk> + */ + +#include "log-store-empathy.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <glib/gstdio.h> + +#include <glib-object.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <telepathy-glib/account.h> +#include <telepathy-glib/account-manager.h> +#include <telepathy-glib/defs.h> +#include <telepathy-glib/util.h> + +#include <telepathy-logger/contact.h> +#include <telepathy-logger/log-manager.h> +#include <telepathy-logger/log-store.h> +#include <telepathy-logger/log-entry-text.h> +#include <telepathy-logger/datetime.h> + +#define DEBUG g_debug + +#define LOG_DIR_CREATE_MODE (S_IRUSR | S_IWUSR | S_IXUSR) +#define LOG_FILE_CREATE_MODE (S_IRUSR | S_IWUSR) +#define LOG_DIR_CHATROOMS "chatrooms" +#define LOG_FILENAME_SUFFIX ".log" +#define LOG_TIME_FORMAT_FULL "%Y%m%dT%H:%M:%S" +#define LOG_TIME_FORMAT "%Y%m%d" +#define LOG_HEADER \ + "<?xml version='1.0' encoding='utf-8'?>\n" \ + "<?xml-stylesheet type=\"text/xsl\" href=\"empathy-log.xsl\"?>\n" \ + "<log>\n" + +#define LOG_FOOTER \ + "</log>\n" + + +#define GET_PRIV(obj) TPL_GET_PRIV (obj, TplLogStoreEmpathy) +typedef struct +{ + gchar *basedir; + gchar *name; + TpAccountManager *account_manager; +} TplLogStoreEmpathyPriv; + +static void log_store_iface_init (gpointer g_iface, gpointer iface_data); + +G_DEFINE_TYPE_WITH_CODE (TplLogStoreEmpathy, tpl_log_store_empathy, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TPL_TYPE_LOG_STORE, + log_store_iface_init)); + +static void +log_store_empathy_dispose (GObject * object) +{ + TplLogStoreEmpathy *self = TPL_LOG_STORE_EMPATHY (object); + TplLogStoreEmpathyPriv *priv = GET_PRIV (self); + + // FIXME See TP-bug #25569, when dispose a non prepared TP_AM, it + // might segfault. + // To avoid it, a *klduge*, a reference in the TplObserver to + // the TplLogManager is kept, so that until TplObserver is instanced, + // there will always be a TpLogManager reference and it won't be + // diposed, not executing the follwoing unref (which caused the bug) + if (priv->account_manager != NULL) + { + g_object_unref (priv->account_manager); + priv->account_manager = NULL; + } +} + + +static void +log_store_empathy_finalize (GObject * object) +{ + TplLogStoreEmpathy *self = TPL_LOG_STORE_EMPATHY (object); + TplLogStoreEmpathyPriv *priv = GET_PRIV (self); + + g_free (priv->basedir); + g_free (priv->name); +} + +static void +tpl_log_store_empathy_class_init (TplLogStoreEmpathyClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = log_store_empathy_finalize; + object_class->dispose = log_store_empathy_dispose; + + g_type_class_add_private (object_class, sizeof (TplLogStoreEmpathyPriv)); +} + +static void +tpl_log_store_empathy_init (TplLogStoreEmpathy * self) +{ + TplLogStoreEmpathyPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TPL_TYPE_LOG_STORE_EMPATHY, + TplLogStoreEmpathyPriv); + + self->priv = priv; + + priv->basedir = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), + "TpLogger", "logs", NULL); + + // TODO update to a more meaningful name when more than one LogStore + // implementation will exist, like TpXMLLogger and TPSQLiteLogger + priv->name = g_strdup ("TpLogger"); + priv->account_manager = tp_account_manager_dup (); +} + +static gchar * +log_store_account_to_dirname (TpAccount * account) +{ + const gchar *name; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + name = tp_proxy_get_object_path (account); + if (g_str_has_prefix (name, TP_ACCOUNT_OBJECT_PATH_BASE)) + name += strlen (TP_ACCOUNT_OBJECT_PATH_BASE); + + return g_strdelimit (g_strdup (name), "/", '_'); +} + + +static gchar * +log_store_empathy_get_dir (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, gboolean chatroom) +{ + gchar *basedir; + gchar *escaped; + TplLogStoreEmpathyPriv *priv; + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + // chat_id may be NULL, in order to obtain the account part of the + // path. + + priv = GET_PRIV (self); + + escaped = log_store_account_to_dirname (account); + + if (chatroom) + basedir = g_build_path (G_DIR_SEPARATOR_S, priv->basedir, escaped, + LOG_DIR_CHATROOMS, chat_id, NULL); + else + basedir = g_build_path (G_DIR_SEPARATOR_S, priv->basedir, + escaped, chat_id, NULL); + + g_free (escaped); + + return basedir; +} + +static gchar * +log_store_empathy_get_timestamp_filename (void) +{ + time_t t; + gchar *time_str; + gchar *filename; + + t = tpl_time_get_current (); + time_str = tpl_time_to_string_local (t, LOG_TIME_FORMAT); + filename = g_strconcat (time_str, LOG_FILENAME_SUFFIX, NULL); + + g_free (time_str); + + return filename; +} + +static gchar * +log_store_empathy_get_timestamp_from_message (TplLogEntry * message) +{ + time_t t; + + t = tpl_log_entry_get_timestamp (message); + + /* We keep the timestamps in the messages as UTC. */ + return tpl_time_to_string_utc (t, LOG_TIME_FORMAT_FULL); +} + +static gchar * +log_store_empathy_get_filename (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, gboolean chatroom) +{ + gchar *basedir; + gchar *timestamp; + gchar *filename; + + basedir = log_store_empathy_get_dir (self, account, chat_id, chatroom); + timestamp = log_store_empathy_get_timestamp_filename (); + filename = g_build_filename (basedir, timestamp, NULL); + + g_free (basedir); + g_free (timestamp); + + return filename; +} + +static gboolean +_log_store_empathy_write_to_store (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + const gchar * entry, GError ** error) +{ + FILE *file; + gchar *filename; + gchar *basedir; + + filename = + log_store_empathy_get_filename (self, account, chat_id, chatroom); + basedir = g_path_get_dirname (filename); + if (!g_file_test (basedir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) + { + DEBUG ("Creating directory:'%s'", basedir); + g_mkdir_with_parents (basedir, LOG_DIR_CREATE_MODE); + } + g_free (basedir); + + DEBUG ("Adding log to file: '%s': %s", filename, entry); + + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) + { + file = g_fopen (filename, "w+"); + if (file != NULL) + g_fprintf (file, LOG_HEADER); + + g_chmod (filename, LOG_FILE_CREATE_MODE); + } + else + { + file = g_fopen (filename, "r+"); + if (file != NULL) + fseek (file, -strlen (LOG_FOOTER), SEEK_END); + } + + g_fprintf (file, entry); + + fclose (file); + g_free (filename); + return TRUE; +} + +/* currently unused */ +static gboolean +_log_store_empathy_add_message_text_status_changed (TplLogStore * self, + const gchar * chat_id, + gboolean chatroom, + TplLogEntry * message, + GError ** error) +{ + TpAccount *account; + TplContact *sender; + const gchar *str; + gchar *timestamp; + gchar *contact_name; + gchar *contact_id; + gchar *contact_status; + gchar *contact_presence; + gchar *entry; + gboolean ret = FALSE; + TplLogEntryText *tmessage; + + tmessage = tpl_log_entry_get_entry (message); + sender = tpl_log_entry_text_get_sender (tmessage); + account = + tpl_channel_get_account (tpl_log_entry_text_get_tpl_channel (tmessage)); + + timestamp = log_store_empathy_get_timestamp_from_message (message); + + str = tpl_contact_get_alias (sender); + contact_name = g_markup_escape_text (str, -1); + + str = tpl_contact_get_identifier (sender); + contact_id = g_markup_escape_text (str, -1); + + str = tpl_contact_get_presence_status (sender); + contact_presence = g_markup_escape_text (str, -1); + + str = tpl_contact_get_presence_message (sender); + contact_status = g_markup_escape_text (str, -1); + + entry = + g_strdup_printf ("<statusUpdate time='%s' id='%s' name='%s' isuser='%s'" + "presence='%s' status='%s'/>\n" LOG_FOOTER, timestamp, + contact_id, contact_name, + tpl_contact_get_contact_type (sender) == + TPL_CONTACT_USER ? "true" : "false", contact_presence, + contact_status); + + + ret = _log_store_empathy_write_to_store (self, + account, chat_id, chatroom, entry, + error); + + g_free (contact_id); + g_free (contact_name); + g_free (contact_presence); + g_free (contact_status); + g_free (timestamp); + g_free (entry); + + return ret; +} + + +static gboolean +_log_store_empathy_add_message_text_chat (TplLogStore * self, + const gchar * chat_id, + gboolean chatroom, + TplLogEntry * message, + GError ** error) +{ + gboolean ret; + TpAccount *account; + TplContact *sender; + const gchar *body_str; + const gchar *str; + //EmpathyAvatar *avatar; + //gchar *avatar_token = NULL; + gchar *body; + gchar *timestamp; + gchar *contact_name; + gchar *contact_id; + gchar *entry; + TpChannelTextMessageType msg_type; + TplLogEntryText *tmessage; + + tmessage = tpl_log_entry_get_entry (message); + + sender = tpl_log_entry_text_get_sender (tmessage); + account = + tpl_channel_get_account (tpl_log_entry_text_get_tpl_channel (tmessage)); + body_str = tpl_log_entry_text_get_message (tmessage); + msg_type = tpl_log_entry_text_get_message_type (tmessage); + + if (TPL_STR_EMPTY (body_str)) + return FALSE; + + body = g_markup_escape_text (body_str, -1); + timestamp = log_store_empathy_get_timestamp_from_message (message); + + str = tpl_contact_get_alias (sender); + contact_name = g_markup_escape_text (str, -1); + + str = tpl_contact_get_identifier (sender); + + contact_id = g_markup_escape_text (str, -1); +/* + avatar = empathy_contact_get_avatar (sender); + if (avatar != NULL) + avatar_token = g_markup_escape_text (avatar->token, -1); +*/ + entry = g_strdup_printf ("<message time='%s' cm_id='%d' id='%s' name='%s' " + "token='%s' isuser='%s' type='%s'>" + "%s</message>\n" LOG_FOOTER, timestamp, + tpl_log_entry_text_get_message_id (tmessage), + contact_id, contact_name, + //avatar_token ? avatar_token : "", // instead force to "" as + //follow + "", + tpl_contact_get_contact_type (sender) == + TPL_CONTACT_USER ? "true" : "false", + tpl_log_entry_text_message_type_to_str (msg_type), + body); + + ret = _log_store_empathy_write_to_store (self, + account, chat_id, chatroom, entry, + error); + + g_free (contact_id); + g_free (contact_name); + g_free (timestamp); + g_free (body); + g_free (entry); + //g_free (avatar_token); + + return ret; +} + + +static gboolean +_log_store_empathy_add_message_text (TplLogStore * self, + const gchar * chat_id, + gboolean chatroom, + TplLogEntry * message, GError ** error) +{ + TplLogEntryTextSignalType signal_type; + TplLogEntryText *tmessage; + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), FALSE); + g_return_val_if_fail (chat_id != NULL, FALSE); + g_return_val_if_fail (TPL_IS_LOG_ENTRY (message), FALSE); + + tmessage = tpl_log_entry_get_entry (message); + signal_type = tpl_log_entry_text_get_signal_type (tmessage); + + switch (signal_type) + { + case TPL_LOG_ENTRY_TEXT_SIGNAL_SENT: + case TPL_LOG_ENTRY_TEXT_SIGNAL_RECEIVED: + return _log_store_empathy_add_message_text_chat (self, + chat_id, chatroom, + message, error); + break; + case TPL_LOG_ENTRY_TEXT_SIGNAL_CHAT_STATUS_CHANGED: + return _log_store_empathy_add_message_text_status_changed (self, + chat_id, + chatroom, + message, + error); + break; + case TPL_LOG_ENTRY_TEXT_SIGNAL_SEND_ERROR: + g_warning ("SEND_ERROR log entry not currently handled"); + return FALSE; + case TPL_LOG_ENTRY_TEXT_SIGNAL_LOST_MESSAGE: + g_warning ("LOST_MESSAGE log entry not currently handled"); + return FALSE; + default: + g_warning ("LogEntry's signal type unknown"); + return FALSE; + } +} + + +/* First of two phases selection: understand the type LogEntry */ +static gboolean +log_store_empathy_add_message (TplLogStore * self, + const gchar * chat_id, + gboolean chatroom, + TplLogEntry * message, GError ** error) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY (message), FALSE); + + switch (tpl_log_entry_get_entry_type (message)) + { + case TPL_LOG_ENTRY_TEXT: + return _log_store_empathy_add_message_text (self, chat_id, chatroom, + message, error); + default: + return FALSE; + } +} + + +static gboolean +log_store_empathy_exists (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, gboolean chatroom) +{ + gchar *dir; + gboolean exists; + + g_return_val_if_fail (TPL_IS_LOG_ENTRY (self), FALSE); + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (!TPL_STR_EMPTY (chat_id), FALSE); + + dir = log_store_empathy_get_dir (self, account, chat_id, chatroom); + exists = g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR); + g_free (dir); + + return exists; +} + +static GList * +log_store_empathy_get_dates (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, gboolean chatroom) +{ + GList *dates = NULL; + gchar *date; + gchar *directory; + GDir *dir; + const gchar *filename; + const gchar *p; + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (chat_id), NULL); + + directory = log_store_empathy_get_dir (self, account, chat_id, chatroom); + dir = g_dir_open (directory, 0, NULL); + if (!dir) + { + DEBUG ("Could not open directory:'%s'", directory); + g_free (directory); + return NULL; + } + + DEBUG ("Collating a list of dates in:'%s'", directory); + + while ((filename = g_dir_read_name (dir)) != NULL) + { + if (!g_str_has_suffix (filename, LOG_FILENAME_SUFFIX)) + continue; + + p = strstr (filename, LOG_FILENAME_SUFFIX); + date = g_strndup (filename, p - filename); + + if (!date) + continue; + + if (!g_regex_match_simple ("\\d{8}", date, 0, 0)) + continue; + + dates = g_list_insert_sorted (dates, date, (GCompareFunc) strcmp); + } + + g_free (directory); + g_dir_close (dir); + + DEBUG ("Parsed %d dates", g_list_length (dates)); + + return dates; +} + +static gchar * +log_store_empathy_get_filename_for_date (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + const gchar * date) +{ + gchar *basedir; + gchar *timestamp; + gchar *filename; + + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (chat_id), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (date), NULL); + + basedir = log_store_empathy_get_dir (self, account, chat_id, chatroom); + timestamp = g_strconcat (date, LOG_FILENAME_SUFFIX, NULL); + filename = g_build_filename (basedir, timestamp, NULL); + + g_free (basedir); + g_free (timestamp); + + return filename; +} + +static TplLogSearchHit * +log_store_empathy_search_hit_new (TplLogStore * self, const gchar * filename) +{ + TplLogStoreEmpathyPriv *priv = GET_PRIV (self); + TplLogSearchHit *hit; + gchar *account_name; + const gchar *end; + gchar **strv; + guint len; + GList *accounts, *l; + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (filename), NULL); + g_return_val_if_fail (g_str_has_suffix (filename, LOG_FILENAME_SUFFIX), + NULL); + + strv = g_strsplit (filename, G_DIR_SEPARATOR_S, -1); + len = g_strv_length (strv); + + hit = g_slice_new0 (TplLogSearchHit); + + end = strstr (strv[len - 1], LOG_FILENAME_SUFFIX); + hit->date = g_strndup (strv[len - 1], end - strv[len - 1]); + hit->chat_id = g_strdup (strv[len - 2]); + hit->is_chatroom = (strcmp (strv[len - 3], LOG_DIR_CHATROOMS) == 0); + + if (hit->is_chatroom) + account_name = strv[len - 4]; + else + account_name = strv[len - 3]; + + /* FIXME: This assumes the account manager is prepared, but the + * synchronous API forces this. See bug #599189. */ + accounts = tp_account_manager_get_valid_accounts (priv->account_manager); + + for (l = accounts; l != NULL; l = g_list_next (l)) + { + TpAccount *account = TP_ACCOUNT (l->data); + gchar *name; + + name = log_store_account_to_dirname (account); + if (!tp_strdiff (name, account_name)) + { + g_assert (hit->account == NULL); + hit->account = account; + g_object_ref (account); + } + g_free (name); + } + g_list_free (accounts); + + hit->filename = g_strdup (filename); + + g_strfreev (strv); + + return hit; +} + +static GList * +log_store_empathy_get_messages_for_file (TplLogStore * self, + TpAccount * account, + const gchar * filename) +{ + GList *messages = NULL; + xmlParserCtxtPtr ctxt; + xmlDocPtr doc; + xmlNodePtr log_node; + xmlNodePtr node; + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (filename), NULL); + + DEBUG ("Attempting to parse filename:'%s'...", filename); + + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) + { + DEBUG ("Filename:'%s' does not exist", filename); + return NULL; + } + + /* Create parser. */ + ctxt = xmlNewParserCtxt (); + + /* Parse and validate the file. */ + doc = xmlCtxtReadFile (ctxt, filename, NULL, 0); + if (!doc) + { + g_warning ("Failed to parse file:'%s'", filename); + xmlFreeParserCtxt (ctxt); + return NULL; + } + + /* The root node, presets. */ + log_node = xmlDocGetRootElement (doc); + if (!log_node) + { + xmlFreeDoc (doc); + xmlFreeParserCtxt (ctxt); + return NULL; + } + + /* Now get the messages. */ + for (node = log_node->children; node; node = node->next) + { + TplLogEntry *message; + TplLogEntryText *tmessage; + TplContact *sender; + gchar *time_; + time_t t; + gchar *sender_id; + gchar *sender_name; + //gchar *sender_avatar_token; + gchar *body; + gchar *is_user_str; + gboolean is_user = FALSE; + gchar *msg_type_str; + gchar *cm_id_str; + guint cm_id; + TpChannelTextMessageType msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; + + if (strcmp ((const gchar *) node->name, "message") != 0) + continue; + + body = (gchar *) xmlNodeGetContent (node); + time_ = (gchar *) xmlGetProp (node, (const xmlChar *) "time"); + sender_id = (gchar *) xmlGetProp (node, (const xmlChar *) "id"); + sender_name = (gchar *) xmlGetProp (node, (const xmlChar *) "name"); + /* + sender_avatar_token = (gchar *) xmlGetProp (node, + (const xmlChar *) "token"); + */ + is_user_str = (gchar *) xmlGetProp (node, (const xmlChar *) "isuser"); + msg_type_str = (gchar *) xmlGetProp (node, (const xmlChar *) "type"); + cm_id_str = (gchar *) xmlGetProp (node, (const xmlChar *) "cm_id"); + + if (is_user_str) + is_user = strcmp (is_user_str, "true") == 0; + + if (msg_type_str) + msg_type = tpl_log_entry_text_message_type_from_str (msg_type_str); + + if (cm_id_str) + cm_id = atoi (cm_id_str); + + t = tpl_time_parse (time_); + + sender = tpl_contact_new (); + tpl_contact_set_account (sender, account); + tpl_contact_set_identifier (sender, sender_id); + tpl_contact_set_alias (sender, sender_name); + + /* TODO remove avatar code + if (!TPL_STR_EMPTY (sender_avatar_token)) + empathy_contact_load_avatar_cache (sender, + sender_avatar_token); + */ + + tmessage = tpl_log_entry_text_new (); + tpl_log_entry_text_set_message (tmessage, body); + tpl_log_entry_text_set_sender (tmessage, sender); + tpl_log_entry_text_set_message_type (tmessage, msg_type); + //TODO uderstand if useful + //tpl_log_entry_text_set_is_backlog (message, TRUE); + + message = tpl_log_entry_new (); + tpl_log_entry_set_timestamp (message, t); + tpl_log_entry_set_entry (message, tmessage); + + if (cm_id_str) + tpl_log_entry_text_set_message_id (tmessage, cm_id); + + messages = g_list_append (messages, message); + + g_object_unref (sender); + xmlFree (time_); + xmlFree (sender_id); + xmlFree (sender_name); + xmlFree (body); + xmlFree (is_user_str); + xmlFree (msg_type_str); + xmlFree (cm_id_str); + //xmlFree (sender_avatar_token); + } + + DEBUG ("Parsed %d messages", g_list_length (messages)); + + xmlFreeDoc (doc); + xmlFreeParserCtxt (ctxt); + + return messages; +} + +static GList * +log_store_empathy_get_all_files (TplLogStore * self, const gchar * dir) +{ + GDir *gdir; + GList *files = NULL; + const gchar *name; + const gchar *basedir; + TplLogStoreEmpathyPriv *priv; + + priv = GET_PRIV (self); + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + // dir can be NULL, and basedir will be searched instead + + basedir = dir ? dir : priv->basedir; + + gdir = g_dir_open (basedir, 0, NULL); + if (!gdir) + return NULL; + + while ((name = g_dir_read_name (gdir)) != NULL) + { + gchar *filename; + + filename = g_build_filename (basedir, name, NULL); + if (g_str_has_suffix (filename, LOG_FILENAME_SUFFIX)) + { + files = g_list_prepend (files, filename); + continue; + } + + if (g_file_test (filename, G_FILE_TEST_IS_DIR)) + { + /* Recursively get all log files */ + files = g_list_concat (files, + log_store_empathy_get_all_files (self, + filename)); + } + + g_free (filename); + } + + g_dir_close (gdir); + + return files; +} + + +static GList * +_log_store_empathy_search_in_files (TplLogStore * self, + const gchar * text, GList * files) +{ + GList *l; + GList *hits = NULL; + gchar *text_casefold; + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (text), NULL); + + text_casefold = g_utf8_casefold (text, -1); + + for (l = files; l; l = g_list_next (l)) + { + gchar *filename; + GMappedFile *file; + gsize length; + gchar *contents; + gchar *contents_casefold; + + filename = l->data; + + file = g_mapped_file_new (filename, FALSE, NULL); + if (!file) + continue; + + length = g_mapped_file_get_length (file); + contents = g_mapped_file_get_contents (file); + contents_casefold = g_utf8_casefold (contents, length); + + g_mapped_file_unref (file); + + if (strstr (contents_casefold, text_casefold)) + { + TplLogSearchHit *hit; + + hit = log_store_empathy_search_hit_new (self, filename); + + if (hit) + { + hits = g_list_prepend (hits, hit); + DEBUG ("Found text:'%s' in file:'%s' on date:'%s'", + text, hit->filename, hit->date); + } + } + + g_free (contents_casefold); + g_free (filename); + } + + g_list_free (files); + g_free (text_casefold); + + return hits; +} + + +static GList * +log_store_empathy_search_in_identifier_chats_new (TplLogStore * self, + TpAccount * account, + gchar const *identifier, + const gchar * text) +{ + GList *files; + gchar *dir, *account_dir; + TplLogStoreEmpathyPriv *priv = GET_PRIV (self); + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (identifier), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (text), NULL); + + account_dir = log_store_account_to_dirname (account); + dir = + g_build_path (G_DIR_SEPARATOR_S, priv->basedir, account_dir, + G_DIR_SEPARATOR_S, identifier, NULL); + + files = log_store_empathy_get_all_files (self, dir); + DEBUG ("Found %d log files in total", g_list_length (files)); + + return _log_store_empathy_search_in_files (self, text, files); +} + + + +static GList * +log_store_empathy_search_new (TplLogStore * self, const gchar * text) +{ + GList *files; + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (text), NULL); + + files = log_store_empathy_get_all_files (self, NULL); + DEBUG ("Found %d log files in total", g_list_length (files)); + + return _log_store_empathy_search_in_files (self, text, files); +} + +/* +static gboolean +log_store_empathy_is_logfile (gchar const *filename) { + gchar **path; + guint len; + g_return_val_if_fail(!TPL_STR_EMPTY(filename), FALSE); + + + path = g_strsplit (filename, G_DIR_SEPARATOR_S, -1); + len = g_strv_length (path); + + return g_regex_match_simple ( + TPL_LOG_STORE_EMPATHY_LOGFILE_REGEX, path[len-1], 0, 0); +} +*/ + +static GList * +log_store_empathy_get_chats_for_dir (TplLogStore * self, + const gchar * dir, gboolean is_chatroom) +{ + GDir *gdir; + GList *hits = NULL; + const gchar *name; + GError *error = NULL; + + gdir = g_dir_open (dir, 0, &error); + if (!gdir) + { + DEBUG ("Failed to open directory: %s, error: %s", dir, error->message); + g_error_free (error); + return NULL; + } + + while ((name = g_dir_read_name (gdir)) != NULL) + { + TplLogSearchHit *hit; + + if (!is_chatroom && strcmp (name, LOG_DIR_CHATROOMS) == 0) + { + gchar *filename = g_build_filename (dir, name, NULL); + hits = + g_list_concat (hits, + log_store_empathy_get_chats_for_dir (self, + filename, + TRUE)); + g_free (filename); + continue; + } + hit = g_slice_new0 (TplLogSearchHit); + hit->chat_id = g_strdup (name); + hit->is_chatroom = is_chatroom; + + hits = g_list_prepend (hits, hit); + } + + g_dir_close (gdir); + + return hits; +} + + +static GList * +log_store_empathy_get_messages_for_date (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + const gchar * date) +{ + gchar *filename; + GList *messages; + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (!TPL_STR_EMPTY (chat_id), NULL); + + filename = log_store_empathy_get_filename_for_date (self, account, + chat_id, chatroom, + date); + messages = + log_store_empathy_get_messages_for_file (self, account, filename); + g_free (filename); + + return messages; +} + +static GList * +log_store_empathy_get_chats (TplLogStore * self, TpAccount * account) +{ + gchar *dir; + GList *hits; + TplLogStoreEmpathyPriv *priv; + + priv = GET_PRIV (self); + + dir = log_store_empathy_get_dir (self, account, NULL, FALSE); + + hits = log_store_empathy_get_chats_for_dir (self, dir, FALSE); + + g_free (dir); + + for (guint i = 0; i < g_list_length (hits); ++i) + { + TplLogSearchHit *hit; + hit = g_list_nth_data (hits, i); + } + + + return hits; +} + +static const gchar * +log_store_empathy_get_name (TplLogStore * self) +{ + TplLogStoreEmpathyPriv *priv = GET_PRIV (self); + + return priv->name; +} + +static GList * +log_store_empathy_get_filtered_messages (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + guint num_messages, + TplLogMessageFilter filter, + gpointer user_data) +{ + GList *dates, *l, *messages = NULL; + guint i = 0; + + dates = log_store_empathy_get_dates (self, account, chat_id, chatroom); + + for (l = g_list_last (dates); l && i < num_messages; + l = g_list_previous (l)) + { + GList *new_messages, *n, *next; + + /* FIXME: We should really restrict the message parsing to get only + * the newest num_messages. */ + new_messages = log_store_empathy_get_messages_for_date (self, account, + chat_id, + chatroom, + l->data); + + n = new_messages; + while (n != NULL) + { + next = g_list_next (n); + if (!filter (n->data, user_data)) + { + g_object_unref (n->data); + new_messages = g_list_delete_link (new_messages, n); + } + else + { + i++; + } + n = next; + } + messages = g_list_concat (messages, new_messages); + } + + g_list_foreach (dates, (GFunc) g_free, NULL); + g_list_free (dates); + + return messages; +} + +static void +log_store_iface_init (gpointer g_iface, gpointer iface_data) +{ + TplLogStoreInterface *iface = (TplLogStoreInterface *) g_iface; + + iface->get_name = log_store_empathy_get_name; + iface->exists = log_store_empathy_exists; + iface->add_message = log_store_empathy_add_message; + iface->get_dates = log_store_empathy_get_dates; + iface->get_messages_for_date = log_store_empathy_get_messages_for_date; + iface->get_chats = log_store_empathy_get_chats; + iface->search_in_identifier_chats_new = + log_store_empathy_search_in_identifier_chats_new; + iface->search_new = log_store_empathy_search_new; + iface->ack_message = NULL; + iface->get_filtered_messages = log_store_empathy_get_filtered_messages; +} diff --git a/telepathy-logger/log-store-empathy.h b/telepathy-logger/log-store-empathy.h new file mode 100644 index 0000000..d099371 --- /dev/null +++ b/telepathy-logger/log-store-empathy.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + * Jonny Lamb <jonny.lamb@collabora.co.uk> + */ + +#ifndef __TPL_LOG_STORE_EMPATHY_H__ +#define __TPL_LOG_STORE_EMPATHY_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS +#define TPL_LOG_STORE_EMPATHY_LOGFILE_REGEX "\\d{8}.log" +#define TPL_TYPE_LOG_STORE_EMPATHY \ + (tpl_log_store_empathy_get_type ()) +#define TPL_LOG_STORE_EMPATHY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPL_TYPE_LOG_STORE_EMPATHY, \ + TplLogStoreEmpathy)) +#define TPL_LOG_STORE_EMPATHY_CLASS(vtable) \ + (G_TYPE_CHECK_CLASS_CAST ((vtable), TPL_TYPE_LOG_STORE_EMPATHY, \ + TplLogStoreEmpathyClass)) +#define TPL_IS_LOG_STORE_EMPATHY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPL_TYPE_LOG_STORE_EMPATHY)) +#define TPL_IS_LOG_STORE_EMPATHY_CLASS(vtable) \ + (G_TYPE_CHECK_CLASS_TYPE ((vtable), TPL_TYPE_LOG_STORE_EMPATHY)) +#define TPL_LOG_STORE_EMPATHY_GET_CLASS(inst) \ + (G_TYPE_INSTANCE_GET_CLASS ((inst), TPL_TYPE_LOG_STORE_EMPATHY, \ + TplLogStoreEmpathyClass)) +typedef struct _TplLogStoreEmpathy TplLogStoreEmpathy; +typedef struct _TplLogStoreEmpathyClass TplLogStoreEmpathyClass; + +struct _TplLogStoreEmpathy +{ + GObject parent; + gpointer priv; +}; + +struct _TplLogStoreEmpathyClass +{ + GObjectClass parent; +}; + +GType tpl_log_store_empathy_get_type (void); + +G_END_DECLS +#endif /* __TPL_LOG_STORE_EMPATHY_H__ */ diff --git a/telepathy-logger/log-store.c b/telepathy-logger/log-store.c new file mode 100644 index 0000000..03f98a3 --- /dev/null +++ b/telepathy-logger/log-store.c @@ -0,0 +1,197 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Jonny Lamb <jonny.lamb@collabora.co.uk> + */ + +#include "log-store.h" + +GType +tpl_log_store_get_type (void) +{ + static GType type = 0; + if (type == 0) + { + static const GTypeInfo info = { + sizeof (TplLogStoreInterface), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL /* instance_init */ + }; + type = g_type_register_static (G_TYPE_INTERFACE, "TplLogStore", + &info, 0); + } + return type; +} + +const gchar * +tpl_log_store_get_name (TplLogStore * self) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->get_name) + return NULL; + + return TPL_LOG_STORE_GET_INTERFACE (self)->get_name (self); +} + +gboolean +tpl_log_store_exists (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, gboolean chatroom) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->exists) + return FALSE; + + return TPL_LOG_STORE_GET_INTERFACE (self)->exists (self, account, chat_id, + chatroom); +} + + + +gboolean +tpl_log_store_add_message (TplLogStore * self, + const gchar * chat_id, + gboolean chatroom, + TplLogEntry * message, GError ** error) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->add_message) + { + g_warning ("LogStore: add_message not implemented"); + return FALSE; + } + + return TPL_LOG_STORE_GET_INTERFACE (self)->add_message (self, chat_id, + chatroom, message, + error); +} + +GList * +tpl_log_store_get_dates (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, gboolean chatroom) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->get_dates) + return NULL; + + return TPL_LOG_STORE_GET_INTERFACE (self)->get_dates (self, account, + chat_id, chatroom); +} + +GList * +tpl_log_store_get_messages_for_date (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, const gchar * date) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->get_messages_for_date) + return NULL; + + return TPL_LOG_STORE_GET_INTERFACE (self)->get_messages_for_date (self, + account, + chat_id, + chatroom, + date); +} + +GList * +tpl_log_store_get_last_messages (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, gboolean chatroom) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->get_last_messages) + return NULL; + + return TPL_LOG_STORE_GET_INTERFACE (self)->get_last_messages (self, account, + chat_id, + chatroom); +} + +GList * +tpl_log_store_get_chats (TplLogStore * self, TpAccount * account) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->get_chats) + return NULL; + + return TPL_LOG_STORE_GET_INTERFACE (self)->get_chats (self, account); +} + + + +GList * +tpl_log_store_search_in_identifier_chats_new (TplLogStore * self, + TpAccount * account, + gchar const *identifier, + const gchar * text) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->search_new) + return NULL; + + return + TPL_LOG_STORE_GET_INTERFACE (self)->search_in_identifier_chats_new (self, + account, + identifier, + text); +} + + +GList * +tpl_log_store_search_new (TplLogStore * self, const gchar * text) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->search_new) + return NULL; + + return TPL_LOG_STORE_GET_INTERFACE (self)->search_new (self, text); +} + +void +tpl_log_store_ack_message (TplLogStore * self, + const gchar * chat_id, + gboolean chatroom, TplLogEntry * message) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->ack_message) + return; + + TPL_LOG_STORE_GET_INTERFACE (self)->ack_message (self, chat_id, chatroom, + message); +} + +GList * +tpl_log_store_get_filtered_messages (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + guint num_messages, + TplLogMessageFilter filter, + gpointer user_data) +{ + if (!TPL_LOG_STORE_GET_INTERFACE (self)->get_filtered_messages) + return NULL; + + return TPL_LOG_STORE_GET_INTERFACE (self)->get_filtered_messages (self, + account, + chat_id, + chatroom, + num_messages, + filter, + user_data); +} diff --git a/telepathy-logger/log-store.h b/telepathy-logger/log-store.h new file mode 100644 index 0000000..f24f45c --- /dev/null +++ b/telepathy-logger/log-store.h @@ -0,0 +1,115 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Jonny Lamb <jonny.lamb@collabora.co.uk> + */ + +#ifndef __TPL_LOG_STORE_H__ +#define __TPL_LOG_STORE_H__ + +#include <glib-object.h> +#include <telepathy-glib/account.h> + +#include <telepathy-logger/log-manager.h> +#include <telepathy-logger/log-entry-text.h> + +G_BEGIN_DECLS +#define TPL_TYPE_LOG_STORE (tpl_log_store_get_type ()) +#define TPL_LOG_STORE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPL_TYPE_LOG_STORE, \ + TplLogStore)) +#define TPL_IS_LOG_STORE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPL_TYPE_LOG_STORE)) +#define TPL_LOG_STORE_GET_INTERFACE(inst) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((inst), TPL_TYPE_LOG_STORE, \ + TplLogStoreInterface)) +typedef struct _TplLogStore TplLogStore; /* dummy object */ +typedef struct _TplLogStoreInterface TplLogStoreInterface; + +struct _TplLogStoreInterface +{ + GTypeInterface parent; + + const gchar *(*get_name) (TplLogStore * self); + gboolean (*exists) (TplLogStore * self, TpAccount * account, + const gchar * chat_id, gboolean chatroom); + gboolean (*add_message) (TplLogStore * self, const gchar * chat_id, + gboolean chatroom, TplLogEntry * message, + GError ** error); + GList *(*get_dates) (TplLogStore * self, TpAccount * account, + const gchar * chat_id, gboolean chatroom); + GList *(*get_messages_for_date) (TplLogStore * self, TpAccount * account, + const gchar * chat_id, gboolean chatroom, + const gchar * date); + GList *(*get_last_messages) (TplLogStore * self, TpAccount * account, + const gchar * chat_id, gboolean chatroom); + GList *(*get_chats) (TplLogStore * self, TpAccount * account); + GList *(*search_new) (TplLogStore * self, const gchar * text); + GList *(*search_in_identifier_chats_new) (TplLogStore * self, + TpAccount * account, + gchar const *identifier, + const gchar * text); + void (*ack_message) (TplLogStore * self, const gchar * chat_id, + gboolean chatroom, TplLogEntry * message); + GList *(*get_filtered_messages) (TplLogStore * self, TpAccount * account, + const gchar * chat_id, gboolean chatroom, + guint num_messages, + TplLogMessageFilter filter, + gpointer user_data); +}; + +GType tpl_log_store_get_type (void); + +const gchar *tpl_log_store_get_name (TplLogStore * self); +gboolean tpl_log_store_exists (TplLogStore * self, + TpAccount * account, const gchar * chat_id, + gboolean chatroom); +gboolean tpl_log_store_add_message (TplLogStore * self, const gchar * chat_id, + gboolean chatroom, TplLogEntry * message, + GError ** error); +GList *tpl_log_store_get_dates (TplLogStore * self, TpAccount * account, + const gchar * chat_id, gboolean chatroom); +GList *tpl_log_store_get_messages_for_date (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + const gchar * date); +GList *tpl_log_store_get_last_messages (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom); +GList *tpl_log_store_get_chats (TplLogStore * self, TpAccount * account); +GList *tpl_log_store_search_in_identifier_chats_new (TplLogStore * self, + TpAccount * account, + gchar const *identifier, + const gchar * text); +GList *tpl_log_store_search_new (TplLogStore * self, const gchar * text); +void tpl_log_store_ack_message (TplLogStore * self, + const gchar * chat_id, gboolean chatroom, + TplLogEntry * message); +GList *tpl_log_store_get_filtered_messages (TplLogStore * self, + TpAccount * account, + const gchar * chat_id, + gboolean chatroom, + guint num_messages, + TplLogMessageFilter filter, + gpointer user_data); + +G_END_DECLS +#endif /* __TPL_LOG_STORE_H__ */ diff --git a/telepathy-logger/observer.c b/telepathy-logger/observer.c new file mode 100644 index 0000000..03457d1 --- /dev/null +++ b/telepathy-logger/observer.c @@ -0,0 +1,411 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#include "observer.h" + +#include <glib.h> +#include <telepathy-glib/interfaces.h> +#include <telepathy-glib/channel.h> +#include <telepathy-glib/gtypes.h> +#include <telepathy-glib/dbus.h> +#include <telepathy-glib/svc-generic.h> +#include <telepathy-glib/svc-client.h> + +#include <telepathy-logger/channel.h> +#include <telepathy-logger/channel-text.h> +#include <telepathy-logger/log-manager.h> + +// TODO move to a member of TplObserver +static TplLogManager *logmanager = NULL; + +static void tpl_observer_finalize (GObject * obj); +static void tpl_observer_dispose (GObject * obj); + +static void observer_iface_init (gpointer, gpointer); + +G_DEFINE_TYPE_WITH_CODE (TplObserver, tpl_observer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, + tp_dbus_properties_mixin_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CLIENT, + NULL); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CLIENT_OBSERVER, + observer_iface_init); + ); + +static TplObserver *observer_singleton = NULL; +static const char *client_interfaces[] = { + TP_IFACE_CLIENT_OBSERVER, + NULL +}; + +enum +{ + PROP_0, + PROP_INTERFACES, + PROP_CHANNEL_FILTER +}; + + +static void +_observe_channel_when_ready_cb (TpChannel * channel, + const GError * error, gpointer user_data) +{ + TplChannel *tpl_chan = TPL_CHANNEL (user_data); + + if (error != NULL) + { + g_error ("%s\n", error->message); + g_error ("giving up observing channel '%s'", tpl_chan->channel_path); + g_object_unref (tpl_chan); + return; + } + + tpl_channel_set_channel_type (tpl_chan, + tp_channel_get_channel_type + (tpl_chan->channel)); + + tpl_channel_register_to_observer (tpl_chan); +} + + +static void +_get_ready_tp_channel (TpConnection * connection, + const GError * error, gpointer user_data) +{ + TplChannel *tpl_chan = TPL_CHANNEL (user_data); + + tp_channel_call_when_ready (tpl_channel_get_channel (tpl_chan), + _observe_channel_when_ready_cb, tpl_chan); +} + +static void +tpl_observer_observe_channels (TpSvcClientObserver * self, + const char *account, + const char *connection, + const GPtrArray * channels, + const char *dispatch_op, + const GPtrArray * requests_satisfied, + GHashTable * observer_info, + DBusGMethodInvocation * context) +{ + TpAccount *tp_acc; + TpConnection *tp_conn; + TpDBusDaemon *tp_bus_daemon; + GError *error = NULL; + + tp_bus_daemon = tp_dbus_daemon_dup (&error); + if (tp_bus_daemon == NULL) + { + g_error ("%s\n", error->message); + g_clear_error (&error); + g_error_free (error); + return; + } + + tp_acc = tp_account_new (tp_bus_daemon, account, &error); + if (tp_acc == NULL) + { + g_error ("%s\n", error->message); + g_clear_error (&error); + g_error_free (error); + g_object_unref (tp_bus_daemon); + return; + } + + + tp_conn = tp_connection_new (tp_bus_daemon, NULL, connection, &error); + if (tp_conn == NULL) + { + g_error ("%s\n", error->message); + g_clear_error (&error); + g_error_free (error); + g_object_unref (tp_bus_daemon); + g_object_unref (tp_acc); + return; + } + + /* channels is of type a(oa{sv}) */ + for (guint i = 0; i < channels->len; i++) + { + GValueArray *channel = g_ptr_array_index (channels, i); + TpChannel *tp_chan; + TplChannel *tpl_chan; + + gchar *path = g_value_get_boxed (g_value_array_get_nth (channel, 0)); + // propertyNameStr/value hash + GHashTable *map = + g_value_get_boxed (g_value_array_get_nth (channel, 1)); + + tp_chan = tp_channel_new (tp_conn, path, NULL, + TP_UNKNOWN_HANDLE_TYPE, 0, &error); + if (tp_conn == NULL) + { + // log and skip to next channel + g_error ("%s", error->message); + g_clear_error (&error); + g_error_free (error); + error = NULL; + continue; + } + + tpl_chan = tpl_channel_new (self); + tpl_channel_set_account (tpl_chan, tp_acc); + tpl_channel_set_account_path (tpl_chan, account); + tpl_channel_set_connection (tpl_chan, tp_conn); + tpl_channel_set_connection_path (tpl_chan, connection); + tpl_channel_set_channel (tpl_chan, tp_chan); + tpl_channel_set_channel_path (tpl_chan, path); + tpl_channel_set_channel_properties (tpl_chan, map); + + tp_connection_call_when_ready (tp_conn, + _get_ready_tp_channel, tpl_chan); + + } + + g_object_unref (tp_acc); + g_object_unref (tp_conn); + g_object_unref (tp_bus_daemon); + + tp_svc_client_observer_return_from_observe_channels (context); +} + +static void +tpl_observer_get_property (GObject * self, + guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + GPtrArray *array; + GHashTable *map; + case PROP_INTERFACES: + g_value_set_boxed (value, client_interfaces); + break; + + case PROP_CHANNEL_FILTER: + + /* create an empty filter - which means all channels */ + array = g_ptr_array_new (); + map = g_hash_table_new (NULL, NULL); + + g_ptr_array_add (array, map); + g_value_set_boxed (value, array); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec); + break; + } +} + +/* Singleton Constructor */ +static GObject * +tpl_observer_constructor (GType type, guint n_props, + GObjectConstructParam * props) +{ + GObject *retval; + + if (observer_singleton) + { + retval = g_object_ref (observer_singleton); + } + else + { + retval = G_OBJECT_CLASS (tpl_observer_parent_class)->constructor + (type, n_props, props); + + observer_singleton = TPL_OBSERVER (retval); + g_object_add_weak_pointer (retval, (gpointer *) & observer_singleton); + } + + return retval; +} + + +static void +tpl_observer_class_init (TplObserverClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = tpl_observer_constructor; + object_class->finalize = tpl_observer_finalize; + object_class->dispose = tpl_observer_dispose; + + /* D-Bus properties are exposed as GObject properties through the + * TpDBusPropertiesMixin */ + /* properties on the Client interface */ + static TpDBusPropertiesMixinPropImpl client_props[] = { + {"Interfaces", "interfaces", NULL}, + {NULL} + }; + + /* properties on the Client.Observer interface */ + static TpDBusPropertiesMixinPropImpl client_observer_props[] = { + {"ObserverChannelFilter", "channel-filter", NULL}, + {NULL} + }; + + /* complete list of interfaces with properties */ + static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { + {TP_IFACE_CLIENT, + tp_dbus_properties_mixin_getter_gobject_properties, + NULL, + client_props}, + {TP_IFACE_CLIENT_OBSERVER, + tp_dbus_properties_mixin_getter_gobject_properties, + NULL, + client_observer_props}, + {NULL} + }; + object_class->get_property = tpl_observer_get_property; + + g_object_class_install_property (object_class, PROP_INTERFACES, + g_param_spec_boxed ("interfaces", + "Interfaces", + "Available D-Bus Interfaces", + G_TYPE_STRV, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_CHANNEL_FILTER, + g_param_spec_boxed ("channel-filter", + "Channel Filter", + "Filter for channels we observe", + TP_ARRAY_TYPE_CHANNEL_CLASS_LIST, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /* call our mixin class init */ + klass->dbus_props_class.interfaces = prop_interfaces; + tp_dbus_properties_mixin_class_init (object_class, + G_STRUCT_OFFSET (TplObserverClass, + dbus_props_class)); +} + + +static gboolean +tpl_str_are_eq (gconstpointer data, gconstpointer data2) +{ + return g_strcmp0 (data, data2) ? FALSE : TRUE; +} + + +static void +tpl_observer_init (TplObserver * self) +{ + DBusGConnection *bus; + TpDBusDaemon *tp_bus; + GError *error = NULL; + + self->channel_map = g_hash_table_new_full (g_str_hash, tpl_str_are_eq, + g_free, g_object_unref); + logmanager = tpl_log_manager_dup_singleton (); + + bus = tp_get_bus (); + tp_bus = tp_dbus_daemon_new (bus); + + if (tp_dbus_daemon_request_name (tp_bus, TPL_OBSERVER_WELL_KNOWN_BUS_NAME, + TRUE, &error)) + { + g_debug ("%s DBus well known name registered\n", + TPL_OBSERVER_WELL_KNOWN_BUS_NAME); + } + else + { + g_error ("Well Known name request error: %s\n", error->message); + g_clear_error (&error); + g_error_free (error); + } + + dbus_g_connection_register_g_object (bus, + TPL_OBSERVER_OBJECT_PATH, + G_OBJECT (self)); +} + +static void +observer_iface_init (gpointer g_iface, gpointer iface_data) +{ + TpSvcClientObserverClass *klass = (TpSvcClientObserverClass *) g_iface; + + tp_svc_client_observer_implement_observe_channels (klass, + tpl_observer_observe_channels); +} + +static void +tpl_observer_dispose (GObject * obj) +{ + TplObserver *self = TPL_OBSERVER (obj); + + g_debug ("TplObserver: disposing\n"); + + if (self->channel_map != NULL) + { + g_object_unref (self->channel_map); + self->channel_map = NULL; + } + if (logmanager != NULL) + { + g_object_unref (logmanager); + logmanager = NULL; + } + + G_OBJECT_CLASS (tpl_observer_parent_class)->dispose (obj); + + g_debug ("TplObserver: disposed\n"); +} + +static void +tpl_observer_finalize (GObject * obj) +{ + //TplObserver *self = TPL_OBSERVER(obj); + + g_debug ("TplObserver: finalizing\n"); + + G_OBJECT_CLASS (tpl_observer_parent_class)->finalize (obj); + + g_debug ("TplObserver: finalized\n"); +} + +TplObserver * +tpl_observer_new (void) +{ + return g_object_new (TYPE_TPL_OBSERVER, NULL); +} + + +GHashTable * +tpl_observer_get_channel_map (TplObserver * self) +{ + g_return_val_if_fail (TPL_IS_OBSERVER (self), NULL); + + return self->channel_map; +} + +void +tpl_observer_set_channel_map (TplObserver * self, GHashTable * data) +{ + g_return_if_fail (TPL_IS_OBSERVER (self)); + //TODO check data validity + + tpl_object_unref_if_not_null (self->channel_map); + self->channel_map = data; + tpl_object_ref_if_not_null (data); +} diff --git a/telepathy-logger/observer.h b/telepathy-logger/observer.h new file mode 100644 index 0000000..798c674 --- /dev/null +++ b/telepathy-logger/observer.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#ifndef __TPL_OBSERVER_H__ +#define __TPL_OBSERVER_H__ + +#include <glib-object.h> +#include <telepathy-glib/dbus-properties-mixin.h> + +#define TP_IFACE_CHAN_TEXT "org.freedesktop.Telepathy.Channel.Type.Text" +#define TPL_OBSERVER_WELL_KNOWN_BUS_NAME \ + "org.freedesktop.Telepathy.Client.TelepathyLogger" +#define TPL_OBSERVER_OBJECT_PATH \ + "/org/freedesktop/Telepathy/Client/TelepathyLogger" + + +G_BEGIN_DECLS +#define TYPE_TPL_OBSERVER (tpl_observer_get_type ()) +#define TPL_OBSERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TPL_OBSERVER, TplObserver)) +#define TPL_OBSERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), TYPE_TPL_OBSERVER, TplObserverClass)) +#define TPL_IS_OBSERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TPL_OBSERVER)) +#define TPL_IS_OBSERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), TYPE_TPL_OBSERVER)) +#define TPL_OBSERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_TPL_OBSERVER, TplObserverClass)) +typedef struct _TplObserver TplObserver; +struct _TplObserver +{ + GObject parent; + + /* private */ + + // mapping channel_path_str->Tpl<Interface>Channel instances + GHashTable *channel_map; +}; + +typedef struct _TplObserverClass TplObserverClass; +struct _TplObserverClass +{ + GObjectClass parent_class; + TpDBusPropertiesMixinClass dbus_props_class; +}; + +GType tpl_observer_get_type (void); + +TplObserver *tpl_observer_new (void); + +void tpl_headless_logger_init (void); + +GHashTable *tpl_observer_get_channel_map (TplObserver * self); +void tpl_observer_set_channel_map (TplObserver * self, GHashTable * data); + +G_END_DECLS +#endif // __TPL_OBSERVER_H__ diff --git a/telepathy-logger/telepathy-logger.schemas b/telepathy-logger/telepathy-logger.schemas new file mode 100644 index 0000000..511dc94 --- /dev/null +++ b/telepathy-logger/telepathy-logger.schemas @@ -0,0 +1,32 @@ +<gconfschemafile> + <schemalist> + + <schema> + <key>/schemas/apps/telepathy-logger/disabling/global</key> + <applyto>/apps/telepathy-logger/disabling/global</applyto> + <owner>telepathy-logger</owner> + <type>bool</type> + <default>TRUE</default> + <locale name="C"> + <short>Globally enable or to disable logging</short> + <long>Globally enable or disable the telepathy logging system. +Setting it to "false" will completely disable any logging</long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/telepathy-logger/disabling/accounts/blocklist</key> + <applyto>/apps/telepathy-logger/disabling/accounts/blocklist</applyto> + <owner>telepathy-logger</owner> + <type>list</type> + <list_type>string</list_type> + <default>true</default> + <locale name="C"> + <short>A list of accounts for which logging is disabled</short> + <long>Disables logging for the named accounts, when logging is +globally enabled. Globally disabling logging will ignore this key.</long> + </locale> + </schema> + +</schemalist> +</gconfschemafile> diff --git a/telepathy-logger/utils.c b/telepathy-logger/utils.c new file mode 100644 index 0000000..1f51efc --- /dev/null +++ b/telepathy-logger/utils.c @@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#include "utils.h" + +void +tpl_object_unref_if_not_null (void *data) +{ + if (data && G_IS_OBJECT (data)) + { + g_object_unref (data); + } +} + +void +tpl_object_ref_if_not_null (void *data) +{ + if (data && G_IS_OBJECT (data)) + { + g_object_ref (data); + } +} diff --git a/telepathy-logger/utils.h b/telepathy-logger/utils.h new file mode 100644 index 0000000..acf4740 --- /dev/null +++ b/telepathy-logger/utils.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2009 Collabora Ltd. + * + * 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: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#ifndef __TPL_UTILS_H__ +#define __TPL_UTILS_H__ + +#include <glib-object.h> + +#define TPL_GET_PRIV(obj,type) ((type##Priv *) ((type *) obj)->priv) +#define TPL_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0') + +/* +#define tpl_object_ref_if_not_null(obj) if (obj && G_IS_OBJECT(obj)) \ + g_object_ref(obj); +#define tpl_object_unref_if_not_null(obj) if (obj && G_IS_OBJECT(obj)) \ + g_object_unref(obj); +*/ +void tpl_object_unref_if_not_null (void *data); +void tpl_object_ref_if_not_null (void *data); + +#define tpl_call_with_err_if_fail(guard, obj, PREFIX, POSTFIX, msg, func, user_data) \ + if (!(guard)) \ + { \ + if (func != NULL) \ + { \ + GError *e; \ + e = g_error_new ( PREFIX ## _ERROR, \ + PREFIX ## _ERROR_ ## POSTFIX, \ + msg); \ + func (obj, FALSE, e, user_data); \ + g_error_free (e); \ + } \ + return; \ + } + +#endif // __TPL_UTILS_H__ |